import { ImportBatchStatus } from "@capakraken/db";
import { SystemRole } from "@capakraken/shared";
import { beforeEach, describe, expect, it, vi } from "vitest";

const {
  assessDispoImportReadiness,
  stageDispoImportBatch,
} = vi.hoisted(() => ({
  assessDispoImportReadiness: vi.fn(),
  stageDispoImportBatch: vi.fn(),
}));

const { createAuditEntry } = vi.hoisted(() => ({
  createAuditEntry: vi.fn(),
}));

vi.mock("@capakraken/application", () => ({
  assessDispoImportReadiness,
  stageDispoImportBatch,
}));

vi.mock("../lib/audit.js", () => ({
  createAuditEntry,
}));

import { dispoRouter } from "../router/dispo.js";
import { createCallerFactory } from "../trpc.js";

const createCaller = createCallerFactory(dispoRouter);

function createDispoCaller(
  db: Record<string, unknown>,
  options: { role?: SystemRole } = {},
) {
  const { role = SystemRole.ADMIN } = options;

  return createCaller({
    session: {
      user: { email: "user@example.com", name: "User", image: null },
      expires: "2099-01-01T00:00:00.000Z",
    },
    db: db as never,
    dbUser: {
      id: role === SystemRole.ADMIN ? "user_admin" : "user_1",
      systemRole: role,
      permissionOverrides: null,
    },
  });
}

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

  it("requires admin access for dispo import procedures", async () => {
    const findMany = vi.fn();
    const caller = createDispoCaller(
      { importBatch: { findMany } },
      { role: SystemRole.USER },
    );

    await expect(caller.listImportBatches({ limit: 10 })).rejects.toMatchObject({
      code: "FORBIDDEN",
      message: "Admin role required",
    });
    await expect(
      caller.stageImportBatch({
        chargeabilityWorkbookPath: "/tmp/chargeability.xlsx",
        planningWorkbookPath: "/tmp/planning.xlsx",
        referenceWorkbookPath: "/tmp/reference.xlsx",
      }),
    ).rejects.toMatchObject({
      code: "FORBIDDEN",
      message: "Admin role required",
    });

    expect(findMany).not.toHaveBeenCalled();
    expect(stageDispoImportBatch).not.toHaveBeenCalled();
  });

  it("lists import batches through the thin router wiring", async () => {
    const findMany = vi.fn().mockResolvedValue([
      { id: "batch_2", createdAt: new Date("2026-03-29T00:00:00.000Z") },
      { id: "batch_1", createdAt: new Date("2026-03-28T00:00:00.000Z") },
    ]);

    const caller = createDispoCaller({
      importBatch: { findMany },
    });
    const result = await caller.listImportBatches({
      limit: 1,
      status: ImportBatchStatus.STAGED,
    });

    expect(findMany).toHaveBeenCalledWith({
      where: { status: ImportBatchStatus.STAGED },
      orderBy: { createdAt: "desc" },
      take: 2,
    });
    expect(result).toEqual({
      items: [{ id: "batch_2", createdAt: new Date("2026-03-29T00:00:00.000Z") }],
      nextCursor: "batch_1",
    });
  });

  it("stages, validates, and cancels import batches through the admin router", async () => {
    stageDispoImportBatch.mockResolvedValue({ id: "batch_1", status: ImportBatchStatus.STAGED });
    assessDispoImportReadiness.mockResolvedValue({ ready: true, issues: [] });
    const findUnique = vi.fn().mockResolvedValue({
      id: "batch_1",
      status: ImportBatchStatus.STAGED,
    });
    const update = vi.fn().mockResolvedValue({
      id: "batch_1",
      status: ImportBatchStatus.CANCELLED,
    });

    const caller = createDispoCaller({
      importBatch: { findUnique, update },
    });

    const staged = await caller.stageImportBatch({
      chargeabilityWorkbookPath: "/tmp/chargeability.xlsx",
      notes: "overnight patch",
      planningWorkbookPath: "/tmp/planning.xlsx",
      referenceWorkbookPath: "/tmp/reference.xlsx",
    });
    const validated = await caller.validateImportBatch({
      chargeabilityWorkbookPath: "/tmp/chargeability.xlsx",
      planningWorkbookPath: "/tmp/planning.xlsx",
      referenceWorkbookPath: "/tmp/reference.xlsx",
    });
    const cancelled = await caller.cancelImportBatch({ id: "batch_1" });

    expect(stageDispoImportBatch).toHaveBeenCalledWith(
      expect.objectContaining({ importBatch: { findUnique, update } }),
      {
        chargeabilityWorkbookPath: "/tmp/chargeability.xlsx",
        notes: "overnight patch",
        planningWorkbookPath: "/tmp/planning.xlsx",
        referenceWorkbookPath: "/tmp/reference.xlsx",
      },
    );
    expect(assessDispoImportReadiness).toHaveBeenCalledWith({
      chargeabilityWorkbookPath: "/tmp/chargeability.xlsx",
      planningWorkbookPath: "/tmp/planning.xlsx",
      referenceWorkbookPath: "/tmp/reference.xlsx",
    });
    expect(findUnique).toHaveBeenCalledWith({
      where: { id: "batch_1" },
      select: { id: true, status: true },
    });
    expect(update).toHaveBeenCalledWith({
      where: { id: "batch_1" },
      data: {
        status: ImportBatchStatus.CANCELLED,
      },
    });
    expect(staged).toEqual({ id: "batch_1", status: ImportBatchStatus.STAGED });
    expect(validated).toEqual({ ready: true, issues: [] });
    expect(cancelled).toEqual({ id: "batch_1", status: ImportBatchStatus.CANCELLED });
    expect(createAuditEntry).toHaveBeenCalledWith(
      expect.objectContaining({
        entityType: "ImportBatch",
        entityId: "batch_1",
        summary: "Cancelled import batch",
        userId: "user_admin",
      }),
    );
  });
});
