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

import { getDatabase } from "@/modules/core/server/db";
import { accounts, careTasks, elders } from "@/modules/core/server/schema";
import type { CareExecutionData, CareTask, CareTaskStatusUpdateInput } from "@/modules/care/types";

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

type AccountNameRow = {
  id: string;
  name: string;
};

type ElderNameRow = {
  id: string;
  name: string;
};

export function isCareMutationFailure(value: unknown): value is CareMutationFailure {
  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 buildNameMap(rows: AccountNameRow[] | ElderNameRow[]): Map<string, string> {
  return new Map(rows.map((row) => [row.id, row.name]));
}

function toCareTask(
  row: typeof careTasks.$inferSelect,
  elderNameById: Map<string, string>,
  accountNameById: Map<string, string>,
): CareTask {
  const elderId = row.elderId ?? undefined;
  const completedByAccountId = row.completedByAccountId ?? undefined;

  return {
    id: row.id,
    organizationId: row.organizationId,
    elderId,
    elderName: elderId ? elderNameById.get(elderId) ?? "" : "",
    title: row.title,
    careType: row.careType,
    priority: row.priority,
    status: row.status,
    scheduledAt: iso(row.scheduledAt),
    assigneeLabel: row.assigneeLabel,
    executionNotes: row.executionNotes,
    completedAt: optionalIso(row.completedAt),
    completedByAccountId,
    completedByName: completedByAccountId ? accountNameById.get(completedByAccountId) : undefined,
    createdAt: iso(row.createdAt),
    updatedAt: iso(row.updatedAt),
  };
}

export async function listCareExecutionData(organizationId: string): Promise<CareExecutionData> {
  const database = getDatabase();
  const todayStart = new Date();
  todayStart.setHours(0, 0, 0, 0);

  const taskRows = await database
    .select()
    .from(careTasks)
    .where(eq(careTasks.organizationId, organizationId))
    .orderBy(desc(careTasks.scheduledAt));

  const elderIds = Array.from(new Set(taskRows.map((task) => task.elderId).filter((elderId): elderId is string => typeof elderId === "string")));
  const accountIds = Array.from(
    new Set(taskRows.map((task) => task.completedByAccountId).filter((accountId): accountId is string => typeof accountId === "string")),
  );

  const [elderRows, accountRows] = await Promise.all([
    elderIds.length > 0
      ? database.select({ id: elders.id, name: elders.name }).from(elders).where(and(eq(elders.organizationId, organizationId), inArray(elders.id, elderIds)))
      : [],
    accountIds.length > 0 ? database.select({ id: accounts.id, name: accounts.name }).from(accounts).where(inArray(accounts.id, accountIds)) : [],
  ]);

  const tasks = taskRows.map((task) => toCareTask(task, buildNameMap(elderRows), buildNameMap(accountRows)));

  return {
    metrics: {
      pending: taskRows.filter((task) => task.status === "pending").length,
      inProgress: taskRows.filter((task) => task.status === "in_progress").length,
      completedToday: taskRows.filter((task) => task.status === "completed" && task.completedAt !== null && task.completedAt >= todayStart).length,
      highPriority: taskRows.filter((task) => task.priority === "high" || task.priority === "urgent").length,
    },
    tasks,
  };
}

export async function updateCareTaskStatus(
  input: CareTaskStatusUpdateInput & { accountId: string; id: string; organizationId: string },
): Promise<CareTask | CareMutationFailure> {
  const database = getDatabase();
  const completedAt = input.status === "completed" ? new Date() : null;
  const completedByAccountId = input.status === "completed" ? input.accountId : null;

  const rows = await database
    .update(careTasks)
    .set({
      status: input.status,
      executionNotes: input.executionNotes,
      completedAt,
      completedByAccountId,
      updatedAt: new Date(),
    })
    .where(and(eq(careTasks.id, input.id), eq(careTasks.organizationId, input.organizationId)))
    .returning();
  const task = rows[0];
  if (!task) {
    return { success: false, reason: "护理任务不存在", status: 404 };
  }

  const [elderRows, accountRows] = await Promise.all([
    task.elderId
      ? database.select({ id: elders.id, name: elders.name }).from(elders).where(and(eq(elders.id, task.elderId), eq(elders.organizationId, input.organizationId)))
      : [],
    task.completedByAccountId
      ? database.select({ id: accounts.id, name: accounts.name }).from(accounts).where(eq(accounts.id, task.completedByAccountId))
      : [],
  ]);

  return toCareTask(task, buildNameMap(elderRows), buildNameMap(accountRows));
}
