import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { DB } from "../generated/kysely-tailordb";
import { baseVarianceReport, baseVarianceReportLine } from "../testing/fixtures";
import { run } from "./getVarianceReport";

describe("getVarianceReport", () => {
  it("returns variance report with all header fields and detail lines when found by id", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce(baseVarianceReport)
      .mockReturnValueOnce([baseVarianceReportLine]);

    const result = await run(db, { id: baseVarianceReport.id });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.varianceReport).toEqual(baseVarianceReport);
      expect(result.value.lines).toEqual([baseVarianceReportLine]);
    }
  });

  it("returns variance report with summary totals matching the sum of detail lines", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce(baseVarianceReport)
      .mockReturnValueOnce([baseVarianceReportLine]);

    const result = await run(db, { id: baseVarianceReport.id });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.varianceReport?.totalPlanned).toEqual(
        baseVarianceReportLine.plannedAmount,
      );
      expect(result.value.varianceReport?.totalActual).toEqual(baseVarianceReportLine.actualAmount);
      expect(result.value.varianceReport?.totalVariance).toEqual(
        baseVarianceReportLine.varianceAmount,
      );
    }
  });

  it("returns detail lines with planned amount, actual amount, variance amount, and variance percentage", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce(baseVarianceReport)
      .mockReturnValueOnce([baseVarianceReportLine]);

    const result = await run(db, { id: baseVarianceReport.id });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.lines[0]).toMatchObject({
        plannedAmount: baseVarianceReportLine.plannedAmount,
        actualAmount: baseVarianceReportLine.actualAmount,
        varianceAmount: baseVarianceReportLine.varianceAmount,
        variancePercentage: baseVarianceReportLine.variancePercentage,
      });
    }
  });

  it("returns detail lines with variance direction FAVORABLE for cost elements where actual is below plan", async () => {
    const favorableLine = {
      ...baseVarianceReportLine,
      id: "variance-report-line-favorable",
      plannedAmount: 10000,
      actualAmount: 8000,
      varianceAmount: -2000,
      varianceDirection: "FAVORABLE" as const,
    };
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(baseVarianceReport).mockReturnValueOnce([favorableLine]);

    const result = await run(db, { id: baseVarianceReport.id });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.lines[0].varianceDirection).toEqual("FAVORABLE");
    }
  });

  it("returns detail lines with variance direction UNFAVORABLE for cost elements where actual is above plan", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce(baseVarianceReport)
      .mockReturnValueOnce([baseVarianceReportLine]);

    const result = await run(db, { id: baseVarianceReport.id });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.lines[0].varianceDirection).toEqual("UNFAVORABLE");
    }
  });

  it("returns null when no variance report matches the given id", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(undefined);

    const result = await run(db, { id: "non-existent" });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.varianceReport).toBeNull();
      expect(result.value.lines).toEqual([]);
    }
  });
});
