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

import { getDatabase } from "@/modules/core/server/db";
import { accounts, systemIncidents } from "@/modules/core/server/schema";
import type {
  EmergencyIncident,
  EmergencyIncidentCreateInput,
  EmergencyIncidentData,
  EmergencyIncidentStatusUpdateInput,
} from "@/modules/emergency/types";

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

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

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

function toEmergencyIncident(row: typeof systemIncidents.$inferSelect, accountNameById: Map<string, string>): EmergencyIncident {
  const acknowledgedByAccountId = row.acknowledgedByAccountId ?? undefined;
  const resolvedByAccountId = row.resolvedByAccountId ?? undefined;

  return {
    id: row.id,
    organizationId: row.organizationId ?? undefined,
    severity: row.severity,
    status: row.status,
    title: row.title,
    description: row.description,
    source: row.source,
    acknowledgedByAccountId,
    acknowledgedByName: acknowledgedByAccountId ? accountNameById.get(acknowledgedByAccountId) : undefined,
    acknowledgedAt: optionalIso(row.acknowledgedAt),
    resolvedByAccountId,
    resolvedByName: resolvedByAccountId ? accountNameById.get(resolvedByAccountId) : undefined,
    resolvedAt: optionalIso(row.resolvedAt),
    createdAt: iso(row.createdAt),
    updatedAt: iso(row.updatedAt),
  };
}

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

  const incidentRows = await database
    .select()
    .from(systemIncidents)
    .where(eq(systemIncidents.organizationId, organizationId))
    .orderBy(desc(systemIncidents.createdAt));

  const accountIds = Array.from(
    new Set(
      incidentRows
        .flatMap((incident) => [incident.acknowledgedByAccountId, incident.resolvedByAccountId])
        .filter((accountId): accountId is string => typeof accountId === "string"),
    ),
  );
  const accountRows =
    accountIds.length > 0 ? await database.select({ id: accounts.id, name: accounts.name }).from(accounts).where(inArray(accounts.id, accountIds)) : [];
  const accountNameById = buildNameMap(accountRows);

  return {
    incidents: incidentRows.map((incident) => toEmergencyIncident(incident, accountNameById)),
    metrics: {
      open: incidentRows.filter((incident) => incident.status === "open").length,
      acknowledged: incidentRows.filter((incident) => incident.status === "acknowledged").length,
      critical: incidentRows.filter((incident) => incident.severity === "critical").length,
      resolvedToday: incidentRows.filter(
        (incident) =>
          (incident.status === "resolved" || incident.status === "closed") &&
          incident.resolvedAt !== null &&
          incident.resolvedAt >= todayStart,
      ).length,
    },
  };
}

export async function createEmergencyIncident(
  input: EmergencyIncidentCreateInput & { organizationId: string },
): Promise<EmergencyIncident | EmergencyMutationFailure> {
  const database = getDatabase();
  const rows = await database
    .insert(systemIncidents)
    .values({
      organizationId: input.organizationId,
      severity: input.severity,
      status: "open",
      title: input.title,
      description: input.description,
      source: input.source,
    })
    .returning();
  const incident = rows[0];
  if (!incident) {
    return { success: false, reason: "应急事件创建失败", status: 500 };
  }

  return toEmergencyIncident(incident, new Map());
}

export async function updateEmergencyIncidentStatus(
  input: EmergencyIncidentStatusUpdateInput & { accountId: string; id: string; organizationId: string },
): Promise<EmergencyIncident | EmergencyMutationFailure> {
  const database = getDatabase();
  const now = new Date();
  const rows = await database
    .update(systemIncidents)
    .set({
      status: input.status,
      updatedAt: now,
      acknowledgedByAccountId: input.status === "acknowledged" ? input.accountId : undefined,
      acknowledgedAt: input.status === "acknowledged" ? now : undefined,
      resolvedByAccountId: input.status === "resolved" || input.status === "closed" ? input.accountId : undefined,
      resolvedAt: input.status === "resolved" || input.status === "closed" ? now : undefined,
    })
    .where(and(eq(systemIncidents.id, input.id), eq(systemIncidents.organizationId, input.organizationId)))
    .returning();
  const incident = rows[0];
  if (!incident) {
    return { success: false, reason: "应急事件不存在", status: 404 };
  }

  const accountRows = await database.select({ id: accounts.id, name: accounts.name }).from(accounts).where(eq(accounts.id, input.accountId));
  return toEmergencyIncident(incident, buildNameMap(accountRows));
}
