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

import { getDatabase } from "@/modules/core/server/db";
import { deviceAssets, maintenanceTickets } from "@/modules/core/server/schema";
import type { DeviceAsset, DeviceAssetInput, DeviceOperationsData, MaintenanceTicket, MaintenanceTicketInput } from "@/modules/devices/types";

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

export function isDeviceMutationFailure(value: unknown): value is DeviceMutationFailure {
  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 toDeviceAsset(row: typeof deviceAssets.$inferSelect): DeviceAsset {
  return {
    id: row.id,
    organizationId: row.organizationId,
    name: row.name,
    code: row.code,
    category: row.category,
    location: row.location,
    status: row.status,
    lastInspectedAt: optionalIso(row.lastInspectedAt),
    notes: row.notes,
    createdAt: iso(row.createdAt),
    updatedAt: iso(row.updatedAt),
  };
}

function toMaintenanceTicket(row: typeof maintenanceTickets.$inferSelect, deviceNameById: Map<string, string>): MaintenanceTicket {
  const deviceId = row.deviceId ?? undefined;
  return {
    id: row.id,
    organizationId: row.organizationId,
    deviceId,
    deviceName: deviceId ? deviceNameById.get(deviceId) ?? "" : "",
    title: row.title,
    description: row.description,
    priority: row.priority,
    status: row.status,
    assigneeLabel: row.assigneeLabel,
    resolutionNotes: row.resolutionNotes,
    resolvedAt: optionalIso(row.resolvedAt),
    createdAt: iso(row.createdAt),
    updatedAt: iso(row.updatedAt),
  };
}

export async function listDeviceOperationsData(organizationId: string): Promise<DeviceOperationsData> {
  const database = getDatabase();
  const [deviceRows, ticketRows] = await Promise.all([
    database.select().from(deviceAssets).where(eq(deviceAssets.organizationId, organizationId)).orderBy(desc(deviceAssets.updatedAt)),
    database.select().from(maintenanceTickets).where(eq(maintenanceTickets.organizationId, organizationId)).orderBy(desc(maintenanceTickets.updatedAt)),
  ]);
  const deviceNameById = new Map(deviceRows.map((device) => [device.id, device.name]));

  return {
    metrics: {
      activeDevices: deviceRows.filter((device) => device.status === "active").length,
      maintenanceDevices: deviceRows.filter((device) => device.status === "maintenance").length,
      openTickets: ticketRows.filter((ticket) => ticket.status === "open" || ticket.status === "assigned").length,
      urgentTickets: ticketRows.filter((ticket) => ticket.priority === "urgent").length,
    },
    devices: deviceRows.map(toDeviceAsset),
    tickets: ticketRows.map((ticket) => toMaintenanceTicket(ticket, deviceNameById)),
  };
}

export async function createDeviceAsset(input: DeviceAssetInput & { organizationId: string }): Promise<DeviceAsset | DeviceMutationFailure> {
  const database = getDatabase();
  const rows = await database
    .insert(deviceAssets)
    .values({
      organizationId: input.organizationId,
      name: input.name,
      code: input.code,
      category: input.category,
      location: input.location,
      status: input.status,
      lastInspectedAt: input.lastInspectedAt ? new Date(input.lastInspectedAt) : undefined,
      notes: input.notes,
    })
    .returning();
  const device = rows[0];
  return device ? toDeviceAsset(device) : { success: false, reason: "设备创建失败", status: 500 };
}

export async function updateDeviceAsset(input: DeviceAssetInput & { id: string; organizationId: string }): Promise<DeviceAsset | DeviceMutationFailure> {
  const database = getDatabase();
  const rows = await database
    .update(deviceAssets)
    .set({
      name: input.name,
      code: input.code,
      category: input.category,
      location: input.location,
      status: input.status,
      lastInspectedAt: input.lastInspectedAt ? new Date(input.lastInspectedAt) : null,
      notes: input.notes,
      updatedAt: new Date(),
    })
    .where(and(eq(deviceAssets.id, input.id), eq(deviceAssets.organizationId, input.organizationId)))
    .returning();
  const device = rows[0];
  return device ? toDeviceAsset(device) : { success: false, reason: "设备不存在", status: 404 };
}

export async function deleteDeviceAsset(input: { id: string; organizationId: string }): Promise<DeviceAsset | DeviceMutationFailure> {
  const database = getDatabase();
  const rows = await database
    .delete(deviceAssets)
    .where(and(eq(deviceAssets.id, input.id), eq(deviceAssets.organizationId, input.organizationId)))
    .returning();
  const device = rows[0];
  return device ? toDeviceAsset(device) : { success: false, reason: "设备不存在", status: 404 };
}

async function getDeviceNameById(organizationId: string, deviceId: string | undefined): Promise<Map<string, string> | DeviceMutationFailure> {
  if (!deviceId) {
    return new Map();
  }

  const database = getDatabase();
  const rows = await database
    .select({ id: deviceAssets.id, name: deviceAssets.name })
    .from(deviceAssets)
    .where(and(eq(deviceAssets.id, deviceId), eq(deviceAssets.organizationId, organizationId)));
  const device = rows[0];
  return device ? new Map([[device.id, device.name]]) : { success: false, reason: "设备不存在", status: 404 };
}

export async function createMaintenanceTicket(input: MaintenanceTicketInput & { organizationId: string }): Promise<MaintenanceTicket | DeviceMutationFailure> {
  const deviceNameById = await getDeviceNameById(input.organizationId, input.deviceId);
  if (isDeviceMutationFailure(deviceNameById)) {
    return deviceNameById;
  }

  const database = getDatabase();
  const resolvedAt = input.status === "resolved" || input.status === "closed" ? new Date() : undefined;
  const rows = await database
    .insert(maintenanceTickets)
    .values({
      organizationId: input.organizationId,
      deviceId: input.deviceId,
      title: input.title,
      description: input.description,
      priority: input.priority,
      status: input.status,
      assigneeLabel: input.assigneeLabel,
      resolutionNotes: input.resolutionNotes,
      resolvedAt,
    })
    .returning();
  const ticket = rows[0];
  return ticket ? toMaintenanceTicket(ticket, deviceNameById) : { success: false, reason: "维修工单创建失败", status: 500 };
}

export async function updateMaintenanceTicket(input: MaintenanceTicketInput & { id: string; organizationId: string }): Promise<MaintenanceTicket | DeviceMutationFailure> {
  const deviceNameById = await getDeviceNameById(input.organizationId, input.deviceId);
  if (isDeviceMutationFailure(deviceNameById)) {
    return deviceNameById;
  }

  const database = getDatabase();
  const resolvedAt = input.status === "resolved" || input.status === "closed" ? new Date() : null;
  const rows = await database
    .update(maintenanceTickets)
    .set({
      deviceId: input.deviceId ?? null,
      title: input.title,
      description: input.description,
      priority: input.priority,
      status: input.status,
      assigneeLabel: input.assigneeLabel,
      resolutionNotes: input.resolutionNotes,
      resolvedAt,
      updatedAt: new Date(),
    })
    .where(and(eq(maintenanceTickets.id, input.id), eq(maintenanceTickets.organizationId, input.organizationId)))
    .returning();
  const ticket = rows[0];
  return ticket ? toMaintenanceTicket(ticket, deviceNameById) : { success: false, reason: "维修工单不存在", status: 404 };
}

export async function deleteMaintenanceTicket(input: { id: string; organizationId: string }): Promise<MaintenanceTicket | DeviceMutationFailure> {
  const database = getDatabase();
  const rows = await database
    .delete(maintenanceTickets)
    .where(and(eq(maintenanceTickets.id, input.id), eq(maintenanceTickets.organizationId, input.organizationId)))
    .returning();
  const ticket = rows[0];
  if (!ticket) {
    return { success: false, reason: "维修工单不存在", status: 404 };
  }

  const deviceIds = ticket.deviceId ? [ticket.deviceId] : [];
  const deviceRows =
    deviceIds.length > 0
      ? await database.select({ id: deviceAssets.id, name: deviceAssets.name }).from(deviceAssets).where(inArray(deviceAssets.id, deviceIds))
      : [];
  return toMaintenanceTicket(ticket, new Map(deviceRows.map((device) => [device.id, device.name])));
}
