import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { DB } from "../generated/kysely-tailordb";
import { expectOk, expectErr } from "../testing/commandTestUtils";
import {
  baseCollectingCostSummary,
  baseReviewedCostSummary,
  baseSettledCostSummary,
  baseCostLine,
  baseVarianceLine,
  baseSettlementRecord,
} from "../testing/fixtures";
import { CostSummaryNotFoundError } from "../lib/errors.generated";
import { run } from "./getManufacturingCostSummary";

describe("getManufacturingCostSummary", () => {
  it("returns a collecting cost summary with planned and actual totals", async () => {
    const { db, spies } = createMockDb<DB>();
    const costLine = { ...baseCostLine, costSummaryId: "cost-summary-1" };
    // 1st select: costSummary
    spies.select.mockReturnValueOnce(baseCollectingCostSummary);
    // 2nd select: costLines
    spies.select.mockReturnValueOnce([costLine]);
    // 3rd select: varianceLines
    spies.select.mockReturnValueOnce([]);
    // 4th select: settlementRecords
    spies.select.mockReturnValueOnce([]);

    const result = await run(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);
  });

  it("returns a reviewed cost summary with variance lines", async () => {
    const { db, spies } = createMockDb<DB>();
    const varianceLine = {
      ...baseVarianceLine,
      costSummaryId: "cost-summary-3",
    };
    // 1st select: costSummary
    spies.select.mockReturnValueOnce(baseReviewedCostSummary);
    // 2nd select: costLines
    spies.select.mockReturnValueOnce([]);
    // 3rd select: varianceLines
    spies.select.mockReturnValueOnce([varianceLine]);
    // 4th select: settlementRecords
    spies.select.mockReturnValueOnce([]);

    const result = await run(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 { db, spies } = createMockDb<DB>();
    const settlement = {
      ...baseSettlementRecord,
      costSummaryId: "cost-summary-4",
    };
    // 1st select: costSummary
    spies.select.mockReturnValueOnce(baseSettledCostSummary);
    // 2nd select: costLines
    spies.select.mockReturnValueOnce([]);
    // 3rd select: varianceLines
    spies.select.mockReturnValueOnce([]);
    // 4th select: settlementRecords
    spies.select.mockReturnValueOnce([settlement]);

    const result = await run(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 { db, spies } = createMockDb<DB>();
    const varianceLine = {
      ...baseVarianceLine,
      costSummaryId: "cost-summary-3",
    };
    // 1st select: costSummary
    spies.select.mockReturnValueOnce(baseReviewedCostSummary);
    // 2nd select: costLines
    spies.select.mockReturnValueOnce([]);
    // 3rd select: varianceLines
    spies.select.mockReturnValueOnce([varianceLine]);
    // 4th select: settlementRecords
    spies.select.mockReturnValueOnce([]);

    const result = await run(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 { db, spies } = createMockDb<DB>();
    // 1st select: costSummary returns undefined
    spies.select.mockReturnValueOnce(undefined);

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