import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  PaymentScheduleCalculationFailedError,
  PurchaseBillNotBlockedError,
  PurchaseBillNotFoundError,
  ReleaseReasonRequiredError,
} from "../lib/errors.generated";
import { blockedPurchaseBill, orderedPurchaseOrder } from "../testing/fixtures";
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
import { run } from "./releasePurchaseBill";

describe("releasePurchaseBill", () => {
  it("releases a blocked purchase bill with an override reason", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(blockedPurchaseBill)
      .mockReturnValueOnce([orderedPurchaseOrder]);
    spies.update
      .mockReturnValueOnce({ ...orderedPurchaseOrder, status: "PARTIALLY_BILLED" })
      .mockReturnValueOnce({
        ...blockedPurchaseBill,
        status: "MATCHED",
        releaseReason: "Approved",
      });

    const result = await run(
      db,
      { id: blockedPurchaseBill.id, releaseReason: "Approved" },
      commandCtx,
    );

    expect(expectOk(result).purchaseBill.status).toBe("MATCHED");
  });

  it("preserves original variance evidence when releasing", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(blockedPurchaseBill)
      .mockReturnValueOnce([orderedPurchaseOrder]);
    spies.update
      .mockReturnValueOnce({ ...orderedPurchaseOrder, status: "PARTIALLY_BILLED" })
      .mockReturnValueOnce({
        ...blockedPurchaseBill,
        status: "MATCHED",
        releaseReason: "Approved",
      });

    await run(db, { id: blockedPurchaseBill.id, releaseReason: "Approved" }, commandCtx);

    const setCall = spies.set.mock.calls[1]?.[0] as Record<string, unknown>;
    expect(setCall).not.toHaveProperty("comparisonEvidence");
  });

  it("returns error when purchase bill does not exist", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(undefined);
    expectErr(
      await run(db, { id: "missing-bill", releaseReason: "Approved" }, commandCtx),
      PurchaseBillNotFoundError,
    );
  });

  it("returns error when purchase bill is not blocked", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce({ ...blockedPurchaseBill, status: "DRAFT" });
    expectErr(
      await run(db, { id: blockedPurchaseBill.id, releaseReason: "Approved" }, commandCtx),
      PurchaseBillNotBlockedError,
    );
  });

  it("returns error when override reason is missing", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(blockedPurchaseBill);
    expectErr(
      await run(db, { id: blockedPurchaseBill.id, releaseReason: "   " }, commandCtx),
      ReleaseReasonRequiredError,
    );
  });

  it("returns error when payment schedule calculation fails", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce({
      ...blockedPurchaseBill,
      paymentTermSnapshot: JSON.stringify({ lines: [] }),
    });
    expectErr(
      await run(db, { id: blockedPurchaseBill.id, releaseReason: "Approved" }, commandCtx),
      PaymentScheduleCalculationFailedError,
    );
  });
});
