import { createKyselyMock } from "@tailor-platform/sdk/vitest";
import { describe, expect, it } from "vitest";
import type { Namespace } from "../generated/kysely-tailordb";
import { CostSummaryNotFoundError } from "../lib/errors.generated";
import { expectOk, expectErr } from "../testing/commandTestUtils";
import {
  baseCollectingCostSummary,
  baseReviewedCostSummary,
  baseSettledCostSummary,
  baseCostLine,
  baseVarianceLine,
  baseSettlementRecord,
} from "../testing/fixtures";
import { run } from "./getManufacturingCostSummary";

describe("getManufacturingCostSummary", () => {
  it("returns a collecting cost summary with planned and actual totals", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const costLine = { ...baseCostLine, costSummaryId: "cost-summary-1" };
    mock.enqueueResults([baseCollectingCostSummary], [costLine], [], []);

    const result = await run(mock.db, { id: "cost-summary-1" });
    const { costSummary, costLines } = expectOk<{
      costSummary: {
        status: string;
        plannedMaterialCost: number;
        actualMaterialCost: number;
      };
      costLines: unknown[];
      varianceLines: unknown[];
      settlementRecords: unknown[];
    }>(result as never);

    expect(costSummary.status).toBe("COLLECTING");
    expect(costSummary.plannedMaterialCost).toBe(2000);
    expect(costSummary.actualMaterialCost).toBe(0);
    expect(costLines).toHaveLength(1);
    expect(mock.selects).toHaveLength(4);
    expect(mock.selects[0].parameters).toContain("cost-summary-1");
    expect(mock.selects[0].sql).toMatch(/"id"/);
    expect(mock.selects[1].parameters).toContain("cost-summary-1");
    expect(mock.selects[1].sql).toMatch(/"costSummaryId"/);
    expect(mock.selects[2].sql).toMatch(/"costSummaryId"/);
    expect(mock.selects[3].sql).toMatch(/"costSummaryId"/);
  });

  it("returns a reviewed cost summary with variance lines", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const varianceLine = {
      ...baseVarianceLine,
      costSummaryId: "cost-summary-3",
    };
    mock.enqueueResults([baseReviewedCostSummary], [], [varianceLine], []);

    const result = await run(mock.db, { id: "cost-summary-3" });
    const { costSummary, varianceLines } = expectOk<{
      costSummary: { status: string; reviewedDate: Date | null };
      costLines: unknown[];
      varianceLines: { varianceType: string; amount: number }[];
      settlementRecords: unknown[];
    }>(result as never);

    expect(costSummary.status).toBe("VARIANCE_REVIEWED");
    expect(costSummary.reviewedDate).toEqual(new Date("2024-04-01T00:00:00.000Z"));
    expect(varianceLines).toHaveLength(1);
    expect(varianceLines[0].varianceType).toBe("MATERIAL_PRICE");
    expect(varianceLines[0].amount).toBe(100);
  });

  it("returns a settled cost summary with acknowledgment traceability", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const settlement = {
      ...baseSettlementRecord,
      costSummaryId: "cost-summary-4",
    };
    mock.enqueueResults([baseSettledCostSummary], [], [], [settlement]);

    const result = await run(mock.db, { id: "cost-summary-4" });
    const { costSummary, settlementRecords } = expectOk<{
      costSummary: { status: string };
      costLines: unknown[];
      varianceLines: unknown[];
      settlementRecords: {
        settlementReference: string;
        acknowledgedDate: Date;
      }[];
    }>(result as never);

    expect(costSummary.status).toBe("SETTLED");
    expect(settlementRecords).toHaveLength(1);
    expect(settlementRecords[0].settlementReference).toBe("SETTLE-2024-001");
    expect(settlementRecords[0].acknowledgedDate).toEqual(new Date("2024-04-15T00:00:00.000Z"));
  });

  it("returns variance lines with downstream account references", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const varianceLine = {
      ...baseVarianceLine,
      costSummaryId: "cost-summary-3",
    };
    mock.enqueueResults([baseReviewedCostSummary], [], [varianceLine], []);

    const result = await run(mock.db, { id: "cost-summary-3" });
    const { varianceLines } = expectOk<{
      costSummary: unknown;
      costLines: unknown[];
      varianceLines: {
        accountReference: string;
        variancePercentage: number;
      }[];
      settlementRecords: unknown[];
    }>(result as never);

    expect(varianceLines[0].accountReference).toBe("acct-5001");
    expect(varianceLines[0].variancePercentage).toBe(5.0);
  });

  it("returns error when the cost summary does not exist", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([]);

    const result = await run(mock.db, { id: "nonexistent-id" });
    expectErr(result as never, CostSummaryNotFoundError);
  });
});
