import { SystemRole } from "@capakraken/shared";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { insightsRouter } from "../router/insights.js";
import { createCallerFactory } from "../trpc.js";

const createCaller = createCallerFactory(insightsRouter);

function createControllerCaller(db: Record<string, unknown>) {
  return createCaller({
    session: {
      user: { email: "controller@example.com", name: "Controller", image: null },
      expires: "2099-01-01T00:00:00.000Z",
    },
    db: db as never,
    dbUser: {
      id: "user_controller",
      systemRole: SystemRole.CONTROLLER,
      permissionOverrides: null,
    },
    roleDefaults: null,
  });
}

describe("insights router", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("derives the summary from the same canonical anomaly snapshot", async () => {
    vi.useFakeTimers();
    vi.setSystemTime(new Date("2026-03-29T12:00:00.000Z"));

    try {
      const db = {
        project: {
          findMany: vi.fn().mockResolvedValue([
            {
              id: "project_1",
              name: "Apollo",
              budgetCents: 100_000,
              startDate: new Date("2026-03-01T00:00:00.000Z"),
              endDate: new Date("2026-03-31T00:00:00.000Z"),
              demandRequirements: [
                {
                  headcount: 3,
                  startDate: new Date("2026-03-20T00:00:00.000Z"),
                  endDate: new Date("2026-04-05T00:00:00.000Z"),
                  _count: { assignments: 1 },
                },
              ],
              assignments: [
                {
                  resourceId: "res_1",
                  startDate: new Date("2026-03-01T00:00:00.000Z"),
                  endDate: new Date("2026-04-05T00:00:00.000Z"),
                  hoursPerDay: 12,
                  dailyCostCents: 10_000,
                  status: "ACTIVE",
                },
              ],
            },
          ]),
        },
        resource: {
          findMany: vi.fn().mockResolvedValue([
            {
              id: "res_1",
              displayName: "Peter Parker",
              availability: {
                monday: 8,
                tuesday: 8,
                wednesday: 8,
                thursday: 8,
                friday: 8,
              },
            },
          ]),
        },
        assignment: {
          findMany: vi.fn().mockResolvedValue([
            {
              resourceId: "res_1",
              hoursPerDay: 12,
            },
          ]),
        },
      };

      const caller = createControllerCaller(db);
      const anomalies = await caller.detectAnomalies();
      const summary = await caller.getInsightsSummary();

      expect(anomalies).toEqual([
        expect.objectContaining({ type: "budget", severity: "critical", entityName: "Apollo" }),
        expect.objectContaining({ type: "staffing", severity: "critical", entityName: "Apollo" }),
        expect.objectContaining({ type: "utilization", severity: "critical", entityName: "Peter Parker" }),
        expect.objectContaining({ type: "timeline", severity: "warning", entityName: "Apollo" }),
      ]);
      expect(summary).toEqual({
        total: anomalies.length,
        criticalCount: anomalies.filter((anomaly) => anomaly.severity === "critical").length,
        budget: anomalies.filter((anomaly) => anomaly.type === "budget").length,
        staffing: anomalies.filter((anomaly) => anomaly.type === "staffing").length,
        timeline: anomalies.filter((anomaly) => anomaly.type === "timeline").length,
        utilization: anomalies.filter((anomaly) => anomaly.type === "utilization").length,
      });
      expect(db.project.findMany).toHaveBeenCalledTimes(2);
      expect(db.resource.findMany).toHaveBeenCalledWith({
        where: { isActive: true },
        select: {
          id: true,
          displayName: true,
          availability: true,
        },
      });
    } finally {
      vi.useRealTimers();
    }
  });

  it("returns assistant-friendly anomaly detail from the canonical snapshot", async () => {
    vi.useFakeTimers();
    vi.setSystemTime(new Date("2026-03-15T00:00:00.000Z"));

    try {
      const db = {
        project: {
          findMany: vi.fn().mockResolvedValue([
            {
              id: "project_1",
              name: "Apollo",
              budgetCents: 100_000,
              startDate: new Date("2026-03-01T00:00:00.000Z"),
              endDate: new Date("2026-03-31T00:00:00.000Z"),
              demandRequirements: [
                {
                  headcount: 3,
                  startDate: new Date("2026-03-10T00:00:00.000Z"),
                  endDate: new Date("2026-03-20T00:00:00.000Z"),
                  _count: { assignments: 1 },
                },
              ],
              assignments: [
                {
                  resourceId: "res_1",
                  startDate: new Date("2026-03-01T00:00:00.000Z"),
                  endDate: new Date("2026-04-05T00:00:00.000Z"),
                  hoursPerDay: 12,
                  dailyCostCents: 10_000,
                  status: "ACTIVE",
                },
              ],
            },
          ]),
        },
        resource: {
          findMany: vi.fn().mockResolvedValue([
            {
              id: "res_1",
              displayName: "Peter Parker",
              availability: {
                monday: 8,
                tuesday: 8,
                wednesday: 8,
                thursday: 8,
                friday: 8,
              },
            },
          ]),
        },
        assignment: {
          findMany: vi.fn().mockResolvedValue([
            {
              resourceId: "res_1",
              hoursPerDay: 12,
            },
          ]),
        },
      };

      const caller = createControllerCaller(db);
      const result = await caller.getAnomalyDetail();

      expect(result).toEqual({
        count: 4,
        anomalies: [
          expect.objectContaining({ type: "budget", severity: "critical", entityName: "Apollo" }),
          expect.objectContaining({ type: "staffing", severity: "critical", entityName: "Apollo" }),
          expect.objectContaining({ type: "utilization", severity: "critical", entityName: "Peter Parker" }),
          expect.objectContaining({ type: "timeline", severity: "warning", entityName: "Apollo" }),
        ],
      });
      expect(db.project.findMany).toHaveBeenCalledTimes(1);
    } finally {
      vi.useRealTimers();
    }
  });

  it("returns the cached narrative through the controller router", async () => {
    const projectFindUnique = vi.fn().mockResolvedValue({
      dynamicFields: {
        aiNarrative: "Cached summary",
        aiNarrativeGeneratedAt: "2026-03-30T08:00:00.000Z",
      },
    });

    const caller = createControllerCaller({
      project: {
        findUnique: projectFindUnique,
      },
    });
    const result = await caller.getCachedNarrative({ projectId: "project_1" });

    expect(projectFindUnique).toHaveBeenCalledWith({
      where: { id: "project_1" },
      select: { dynamicFields: true },
    });
    expect(result).toEqual({
      narrative: "Cached summary",
      generatedAt: "2026-03-30T08:00:00.000Z",
    });
  });
});
