import { and, desc, eq, inArray } from "drizzle-orm";

import { getDatabase } from "@/modules/core/server/db";
import { accounts, elders, familyContacts, familyFeedback, familyVisitAppointments } from "@/modules/core/server/schema";
import type {
  FamilyContact,
  FamilyContactInput,
  FamilyFeedback,
  FamilyFeedbackInput,
  FamilyServiceData,
  FamilyVisitAppointment,
  FamilyVisitInput,
} from "@/modules/family/types";

export type FamilyMutationFailure = {
  success: false;
  reason: string;
  status: number;
};

export function isFamilyMutationFailure(value: unknown): value is FamilyMutationFailure {
  return (
    typeof value === "object" &&
    value !== null &&
    "success" in value &&
    (value as { success?: unknown }).success === false
  );
}

function iso(value: Date): string {
  return value.toISOString();
}

function optionalIso(value: Date | null): string | undefined {
  return value ? iso(value) : undefined;
}

function toContact(row: typeof familyContacts.$inferSelect, elderNameById: Map<string, string>): FamilyContact {
  return {
    id: row.id,
    organizationId: row.organizationId,
    elderId: row.elderId,
    elderName: elderNameById.get(row.elderId) ?? "",
    name: row.name,
    relationship: row.relationship,
    phone: row.phone,
    status: row.status,
    notes: row.notes,
    createdAt: iso(row.createdAt),
    updatedAt: iso(row.updatedAt),
  };
}

function toVisit(
  row: typeof familyVisitAppointments.$inferSelect,
  elderNameById: Map<string, string>,
  contactNameById: Map<string, string>,
  accountNameById: Map<string, string>,
): FamilyVisitAppointment {
  const contactId = row.contactId ?? undefined;
  const handledByAccountId = row.handledByAccountId ?? undefined;
  return {
    id: row.id,
    organizationId: row.organizationId,
    elderId: row.elderId,
    elderName: elderNameById.get(row.elderId) ?? "",
    contactId,
    contactName: contactId ? contactNameById.get(contactId) ?? "" : "",
    scheduledAt: iso(row.scheduledAt),
    status: row.status,
    notes: row.notes,
    handledByAccountId,
    handledByName: handledByAccountId ? accountNameById.get(handledByAccountId) : undefined,
    handledAt: optionalIso(row.handledAt),
    createdAt: iso(row.createdAt),
    updatedAt: iso(row.updatedAt),
  };
}

function toFeedback(
  row: typeof familyFeedback.$inferSelect,
  elderNameById: Map<string, string>,
  contactNameById: Map<string, string>,
  accountNameById: Map<string, string>,
): FamilyFeedback {
  const contactId = row.contactId ?? undefined;
  const handledByAccountId = row.handledByAccountId ?? undefined;
  return {
    id: row.id,
    organizationId: row.organizationId,
    elderId: row.elderId,
    elderName: elderNameById.get(row.elderId) ?? "",
    contactId,
    contactName: contactId ? contactNameById.get(contactId) ?? "" : "",
    feedbackType: row.feedbackType,
    content: row.content,
    status: row.status,
    responseNotes: row.responseNotes,
    handledByAccountId,
    handledByName: handledByAccountId ? accountNameById.get(handledByAccountId) : undefined,
    handledAt: optionalIso(row.handledAt),
    createdAt: iso(row.createdAt),
    updatedAt: iso(row.updatedAt),
  };
}

async function validateElderAndContact(input: {
  contactId?: string;
  elderId: string;
  organizationId: string;
}): Promise<{
  contactNameById: Map<string, string>;
  elderNameById: Map<string, string>;
} | FamilyMutationFailure> {
  const database = getDatabase();
  const [elderRows, contactRows] = await Promise.all([
    database.select({ id: elders.id, name: elders.name }).from(elders).where(and(eq(elders.id, input.elderId), eq(elders.organizationId, input.organizationId))),
    input.contactId
      ? database.select({ id: familyContacts.id, name: familyContacts.name }).from(familyContacts).where(and(eq(familyContacts.id, input.contactId), eq(familyContacts.organizationId, input.organizationId)))
      : [],
  ]);
  const elder = elderRows[0];
  if (!elder) {
    return { success: false, reason: "老人档案不存在", status: 404 };
  }
  if (input.contactId && !contactRows[0]) {
    return { success: false, reason: "家属联系人不存在", status: 404 };
  }

  return {
    elderNameById: new Map([[elder.id, elder.name]]),
    contactNameById: new Map(contactRows.map((contact) => [contact.id, contact.name])),
  };
}

export async function listFamilyServiceData(organizationId: string): Promise<FamilyServiceData> {
  const database = getDatabase();
  const [elderRows, contactRows, visitRows, feedbackRows] = await Promise.all([
    database.select({ id: elders.id, name: elders.name }).from(elders).where(eq(elders.organizationId, organizationId)).orderBy(desc(elders.createdAt)),
    database.select().from(familyContacts).where(eq(familyContacts.organizationId, organizationId)).orderBy(desc(familyContacts.updatedAt)),
    database.select().from(familyVisitAppointments).where(eq(familyVisitAppointments.organizationId, organizationId)).orderBy(desc(familyVisitAppointments.scheduledAt)),
    database.select().from(familyFeedback).where(eq(familyFeedback.organizationId, organizationId)).orderBy(desc(familyFeedback.updatedAt)),
  ]);
  const contactIds = Array.from(
    new Set(
      [...visitRows.map((visit) => visit.contactId), ...feedbackRows.map((feedback) => feedback.contactId)].filter((id): id is string => typeof id === "string"),
    ),
  );
  const accountIds = Array.from(
    new Set(
      [...visitRows.map((visit) => visit.handledByAccountId), ...feedbackRows.map((feedback) => feedback.handledByAccountId)].filter(
        (id): id is string => typeof id === "string",
      ),
    ),
  );
  const [linkedContactRows, accountRows] = await Promise.all([
    contactIds.length > 0 ? database.select({ id: familyContacts.id, name: familyContacts.name }).from(familyContacts).where(inArray(familyContacts.id, contactIds)) : [],
    accountIds.length > 0 ? database.select({ id: accounts.id, name: accounts.name }).from(accounts).where(inArray(accounts.id, accountIds)) : [],
  ]);
  const elderNameById = new Map(elderRows.map((elder) => [elder.id, elder.name]));
  const contactNameById = new Map([...contactRows.map((contact) => [contact.id, contact.name] as const), ...linkedContactRows.map((contact) => [contact.id, contact.name] as const)]);
  const accountNameById = new Map(accountRows.map((account) => [account.id, account.name]));

  return {
    metrics: {
      activeContacts: contactRows.filter((contact) => contact.status === "active").length,
      pendingVisits: visitRows.filter((visit) => visit.status === "requested").length,
      openFeedback: feedbackRows.filter((feedback) => feedback.status === "open" || feedback.status === "in_progress").length,
      resolvedFeedback: feedbackRows.filter((feedback) => feedback.status === "resolved" || feedback.status === "closed").length,
    },
    elderOptions: elderRows,
    contacts: contactRows.map((contact) => toContact(contact, elderNameById)),
    visits: visitRows.map((visit) => toVisit(visit, elderNameById, contactNameById, accountNameById)),
    feedback: feedbackRows.map((feedback) => toFeedback(feedback, elderNameById, contactNameById, accountNameById)),
  };
}

export async function createFamilyContact(input: FamilyContactInput & { organizationId: string }): Promise<FamilyContact | FamilyMutationFailure> {
  const maps = await validateElderAndContact({ elderId: input.elderId, organizationId: input.organizationId });
  if (isFamilyMutationFailure(maps)) {
    return maps;
  }

  const database = getDatabase();
  const rows = await database.insert(familyContacts).values({ ...input }).returning();
  const contact = rows[0];
  return contact ? toContact(contact, maps.elderNameById) : { success: false, reason: "家属联系人创建失败", status: 500 };
}

export async function updateFamilyContact(input: FamilyContactInput & { id: string; organizationId: string }): Promise<FamilyContact | FamilyMutationFailure> {
  const maps = await validateElderAndContact({ elderId: input.elderId, organizationId: input.organizationId });
  if (isFamilyMutationFailure(maps)) {
    return maps;
  }

  const database = getDatabase();
  const rows = await database
    .update(familyContacts)
    .set({
      elderId: input.elderId,
      name: input.name,
      relationship: input.relationship,
      phone: input.phone,
      status: input.status,
      notes: input.notes,
      updatedAt: new Date(),
    })
    .where(and(eq(familyContacts.id, input.id), eq(familyContacts.organizationId, input.organizationId)))
    .returning();
  const contact = rows[0];
  return contact ? toContact(contact, maps.elderNameById) : { success: false, reason: "家属联系人不存在", status: 404 };
}

export async function deleteFamilyContact(input: { id: string; organizationId: string }): Promise<FamilyContact | FamilyMutationFailure> {
  const database = getDatabase();
  const rows = await database.delete(familyContacts).where(and(eq(familyContacts.id, input.id), eq(familyContacts.organizationId, input.organizationId))).returning();
  const contact = rows[0];
  return contact ? toContact(contact, new Map()) : { success: false, reason: "家属联系人不存在", status: 404 };
}

export async function createFamilyVisit(input: FamilyVisitInput & { accountId: string; organizationId: string }): Promise<FamilyVisitAppointment | FamilyMutationFailure> {
  const maps = await validateElderAndContact(input);
  if (isFamilyMutationFailure(maps)) {
    return maps;
  }

  const database = getDatabase();
  const handledAt = input.status === "approved" || input.status === "completed" || input.status === "cancelled" ? new Date() : undefined;
  const rows = await database
    .insert(familyVisitAppointments)
    .values({
      organizationId: input.organizationId,
      elderId: input.elderId,
      contactId: input.contactId,
      scheduledAt: new Date(input.scheduledAt),
      status: input.status,
      notes: input.notes,
      handledByAccountId: handledAt ? input.accountId : undefined,
      handledAt,
    })
    .returning();
  const visit = rows[0];
  return visit ? toVisit(visit, maps.elderNameById, maps.contactNameById, new Map()) : { success: false, reason: "探访预约创建失败", status: 500 };
}

export async function updateFamilyVisit(input: FamilyVisitInput & { accountId: string; id: string; organizationId: string }): Promise<FamilyVisitAppointment | FamilyMutationFailure> {
  const maps = await validateElderAndContact(input);
  if (isFamilyMutationFailure(maps)) {
    return maps;
  }

  const database = getDatabase();
  const handledAt = input.status === "approved" || input.status === "completed" || input.status === "cancelled" ? new Date() : null;
  const rows = await database
    .update(familyVisitAppointments)
    .set({
      elderId: input.elderId,
      contactId: input.contactId ?? null,
      scheduledAt: new Date(input.scheduledAt),
      status: input.status,
      notes: input.notes,
      handledByAccountId: handledAt ? input.accountId : null,
      handledAt,
      updatedAt: new Date(),
    })
    .where(and(eq(familyVisitAppointments.id, input.id), eq(familyVisitAppointments.organizationId, input.organizationId)))
    .returning();
  const visit = rows[0];
  return visit ? toVisit(visit, maps.elderNameById, maps.contactNameById, new Map()) : { success: false, reason: "探访预约不存在", status: 404 };
}

export async function deleteFamilyVisit(input: { id: string; organizationId: string }): Promise<FamilyVisitAppointment | FamilyMutationFailure> {
  const database = getDatabase();
  const rows = await database
    .delete(familyVisitAppointments)
    .where(and(eq(familyVisitAppointments.id, input.id), eq(familyVisitAppointments.organizationId, input.organizationId)))
    .returning();
  const visit = rows[0];
  return visit ? toVisit(visit, new Map(), new Map(), new Map()) : { success: false, reason: "探访预约不存在", status: 404 };
}

export async function createFamilyFeedback(input: FamilyFeedbackInput & { accountId: string; organizationId: string }): Promise<FamilyFeedback | FamilyMutationFailure> {
  const maps = await validateElderAndContact(input);
  if (isFamilyMutationFailure(maps)) {
    return maps;
  }

  const database = getDatabase();
  const handledAt = input.status === "resolved" || input.status === "closed" || input.status === "in_progress" ? new Date() : undefined;
  const rows = await database
    .insert(familyFeedback)
    .values({
      organizationId: input.organizationId,
      elderId: input.elderId,
      contactId: input.contactId,
      feedbackType: input.feedbackType,
      content: input.content,
      status: input.status,
      responseNotes: input.responseNotes,
      handledByAccountId: handledAt ? input.accountId : undefined,
      handledAt,
    })
    .returning();
  const feedback = rows[0];
  return feedback ? toFeedback(feedback, maps.elderNameById, maps.contactNameById, new Map()) : { success: false, reason: "家属反馈创建失败", status: 500 };
}

export async function updateFamilyFeedback(input: FamilyFeedbackInput & { accountId: string; id: string; organizationId: string }): Promise<FamilyFeedback | FamilyMutationFailure> {
  const maps = await validateElderAndContact(input);
  if (isFamilyMutationFailure(maps)) {
    return maps;
  }

  const database = getDatabase();
  const handledAt = input.status === "resolved" || input.status === "closed" || input.status === "in_progress" ? new Date() : null;
  const rows = await database
    .update(familyFeedback)
    .set({
      elderId: input.elderId,
      contactId: input.contactId ?? null,
      feedbackType: input.feedbackType,
      content: input.content,
      status: input.status,
      responseNotes: input.responseNotes,
      handledByAccountId: handledAt ? input.accountId : null,
      handledAt,
      updatedAt: new Date(),
    })
    .where(and(eq(familyFeedback.id, input.id), eq(familyFeedback.organizationId, input.organizationId)))
    .returning();
  const feedback = rows[0];
  return feedback ? toFeedback(feedback, maps.elderNameById, maps.contactNameById, new Map()) : { success: false, reason: "家属反馈不存在", status: 404 };
}

export async function deleteFamilyFeedback(input: { id: string; organizationId: string }): Promise<FamilyFeedback | FamilyMutationFailure> {
  const database = getDatabase();
  const rows = await database.delete(familyFeedback).where(and(eq(familyFeedback.id, input.id), eq(familyFeedback.organizationId, input.organizationId))).returning();
  const feedback = rows[0];
  return feedback ? toFeedback(feedback, new Map(), new Map(), new Map()) : { success: false, reason: "家属反馈不存在", status: 404 };
}
