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

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

const {
  cancelImportBatchMutation,
  commitImportBatchMutation,
  resolveStagedRecordMutation,
} = vi.hoisted(() => ({
  cancelImportBatchMutation: vi.fn(),
  commitImportBatchMutation: vi.fn(),
  resolveStagedRecordMutation: vi.fn(),
}));

const {
  getImportBatchQuery,
  listImportBatchesQuery,
  listStagedAssignmentsQuery,
  listStagedProjectsQuery,
  listStagedResourcesQuery,
  listStagedUnresolvedRecordsQuery,
  listStagedVacationsQuery,
} = vi.hoisted(() => ({
  getImportBatchQuery: vi.fn(),
  listImportBatchesQuery: vi.fn(),
  listStagedAssignmentsQuery: vi.fn(),
  listStagedProjectsQuery: vi.fn(),
  listStagedResourcesQuery: vi.fn(),
  listStagedUnresolvedRecordsQuery: vi.fn(),
  listStagedVacationsQuery: vi.fn(),
}));

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

vi.mock("../router/dispo-management.js", () => ({
  cancelImportBatch: cancelImportBatchMutation,
  commitImportBatch: commitImportBatchMutation,
  resolveStagedRecord: resolveStagedRecordMutation,
}));

vi.mock("../router/dispo-read.js", () => ({
  getImportBatch: getImportBatchQuery,
  listImportBatches: listImportBatchesQuery,
  listStagedAssignments: listStagedAssignmentsQuery,
  listStagedProjects: listStagedProjectsQuery,
  listStagedResources: listStagedResourcesQuery,
  listStagedUnresolvedRecords: listStagedUnresolvedRecordsQuery,
  listStagedVacations: listStagedVacationsQuery,
}));

import {
  cancelImportBatch,
  commitImportBatch,
  getImportBatch,
  listImportBatches,
  listStagedAssignments,
  listStagedProjects,
  listStagedResources,
  listStagedUnresolvedRecords,
  listStagedVacations,
  resolveStagedRecord,
  stageImportBatch as stageImportBatchProcedure,
  validateImportBatch,
} from "../router/dispo-procedure-support.js";

function createContext(db: Record<string, unknown>) {
  return {
    db: db as never,
    dbUser: { id: "user_admin" } as never,
  };
}

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

  it("forwards stage-import inputs to the application layer", async () => {
    stageDispoImportBatch.mockResolvedValue({ id: "batch_1" });
    const db = { marker: "db" };

    const result = await stageImportBatchProcedure(createContext(db), {
      chargeabilityWorkbookPath: "/tmp/chargeability.xlsx",
      costWorkbookPath: "/tmp/cost.xlsx",
      notes: null,
      planningWorkbookPath: "/tmp/planning.xlsx",
      referenceWorkbookPath: "/tmp/reference.xlsx",
      rosterWorkbookPath: "/tmp/roster.xlsx",
    });

    expect(stageDispoImportBatch).toHaveBeenCalledWith(db, {
      chargeabilityWorkbookPath: "/tmp/chargeability.xlsx",
      costWorkbookPath: "/tmp/cost.xlsx",
      notes: null,
      planningWorkbookPath: "/tmp/planning.xlsx",
      referenceWorkbookPath: "/tmp/reference.xlsx",
      rosterWorkbookPath: "/tmp/roster.xlsx",
    });
    expect(result).toEqual({ id: "batch_1" });
  });

  it("forwards validate-import inputs without requiring router context", async () => {
    assessDispoImportReadiness.mockResolvedValue({ ready: true });

    const result = await validateImportBatch({
      chargeabilityWorkbookPath: "/tmp/chargeability.xlsx",
      importBatchId: "batch_1",
      notes: "recheck",
      planningWorkbookPath: "/tmp/planning.xlsx",
      referenceWorkbookPath: "/tmp/reference.xlsx",
    });

    expect(assessDispoImportReadiness).toHaveBeenCalledWith({
      chargeabilityWorkbookPath: "/tmp/chargeability.xlsx",
      importBatchId: "batch_1",
      notes: "recheck",
      planningWorkbookPath: "/tmp/planning.xlsx",
      referenceWorkbookPath: "/tmp/reference.xlsx",
    });
    expect(result).toEqual({ ready: true });
  });

  it("delegates staged-record reads to the read helpers", async () => {
    const db = { marker: "db" };
    listImportBatchesQuery.mockResolvedValue({ items: [], nextCursor: undefined });
    getImportBatchQuery.mockResolvedValue({ id: "batch_1" });
    listStagedResourcesQuery.mockResolvedValue({ items: [{ id: "res_1" }] });
    listStagedProjectsQuery.mockResolvedValue({ items: [{ id: "proj_1" }] });
    listStagedAssignmentsQuery.mockResolvedValue({ items: [{ id: "assign_1" }] });
    listStagedVacationsQuery.mockResolvedValue({ items: [{ id: "vac_1" }] });
    listStagedUnresolvedRecordsQuery.mockResolvedValue({ items: [{ id: "unres_1" }] });

    await expect(
      listImportBatches(createContext(db), { limit: 50, status: ImportBatchStatus.STAGED }),
    ).resolves.toEqual({ items: [], nextCursor: undefined });
    await expect(
      getImportBatch(createContext(db), { id: "batch_1" }),
    ).resolves.toEqual({ id: "batch_1" });
    await expect(
      listStagedResources(createContext(db), {
        importBatchId: "batch_1",
        limit: 25,
        status: StagedRecordStatus.STAGED,
      }),
    ).resolves.toEqual({ items: [{ id: "res_1" }] });
    await expect(
      listStagedProjects(createContext(db), {
        importBatchId: "batch_1",
        isTbd: true,
        limit: 25,
      }),
    ).resolves.toEqual({ items: [{ id: "proj_1" }] });
    await expect(
      listStagedAssignments(createContext(db), {
        importBatchId: "batch_1",
        limit: 25,
        resourceExternalId: "R-1",
      }),
    ).resolves.toEqual({ items: [{ id: "assign_1" }] });
    await expect(
      listStagedVacations(createContext(db), {
        importBatchId: "batch_1",
        limit: 25,
      }),
    ).resolves.toEqual({ items: [{ id: "vac_1" }] });
    await expect(
      listStagedUnresolvedRecords(createContext(db), {
        importBatchId: "batch_1",
        limit: 25,
        recordType: DispoStagedRecordType.PROJECT,
      }),
    ).resolves.toEqual({ items: [{ id: "unres_1" }] });

    expect(listImportBatchesQuery).toHaveBeenCalledWith(db, {
      limit: 50,
      status: ImportBatchStatus.STAGED,
    });
    expect(getImportBatchQuery).toHaveBeenCalledWith(db, "batch_1");
    expect(listStagedResourcesQuery).toHaveBeenCalledWith(db, {
      importBatchId: "batch_1",
      limit: 25,
      status: StagedRecordStatus.STAGED,
    });
    expect(listStagedProjectsQuery).toHaveBeenCalledWith(db, {
      importBatchId: "batch_1",
      isTbd: true,
      limit: 25,
    });
    expect(listStagedAssignmentsQuery).toHaveBeenCalledWith(db, {
      importBatchId: "batch_1",
      limit: 25,
      resourceExternalId: "R-1",
    });
    expect(listStagedVacationsQuery).toHaveBeenCalledWith(db, {
      importBatchId: "batch_1",
      limit: 25,
    });
    expect(listStagedUnresolvedRecordsQuery).toHaveBeenCalledWith(db, {
      importBatchId: "batch_1",
      limit: 25,
      recordType: DispoStagedRecordType.PROJECT,
    });
  });

  it("passes user-scoped mutations through the management helpers", async () => {
    const db = { marker: "db" };
    cancelImportBatchMutation.mockResolvedValue({ id: "batch_1", status: ImportBatchStatus.CANCELLED });
    resolveStagedRecordMutation.mockResolvedValue({ id: "record_1", status: StagedRecordStatus.APPROVED });
    commitImportBatchMutation.mockResolvedValue({ importedAssignments: 4 });

    await expect(
      cancelImportBatch(createContext(db), { id: "batch_1" }),
    ).resolves.toEqual({ id: "batch_1", status: ImportBatchStatus.CANCELLED });
    await expect(
      resolveStagedRecord(createContext(db), {
        action: "APPROVE",
        id: "record_1",
        recordType: DispoStagedRecordType.ASSIGNMENT,
      }),
    ).resolves.toEqual({ id: "record_1", status: StagedRecordStatus.APPROVED });
    await expect(
      commitImportBatch(createContext(db), {
        allowTbdUnresolved: true,
        importBatchId: "batch_1",
        importTbdProjects: false,
      }),
    ).resolves.toEqual({ importedAssignments: 4 });

    expect(cancelImportBatchMutation).toHaveBeenCalledWith(db, {
      id: "batch_1",
      userId: "user_admin",
    });
    expect(resolveStagedRecordMutation).toHaveBeenCalledWith(db, {
      action: "APPROVE",
      id: "record_1",
      recordType: DispoStagedRecordType.ASSIGNMENT,
    });
    expect(commitImportBatchMutation).toHaveBeenCalledWith(db, {
      allowTbdUnresolved: true,
      importBatchId: "batch_1",
      importTbdProjects: false,
      userId: "user_admin",
    });
  });
});
