import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { Transaction } from "../generated/kysely-tailordb";
import { CreditLimitExceededError, InvalidCreditNoteStatusError } from "../lib/errors.generated";
import {
  baseCreditNote,
  baseCreditNoteLine,
  baseSalesInvoiceLine,
  issuedCreditNote,
} from "../testing/fixtures";
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
import { run } from "./issueSalesCreditNote";

describe("issueSalesCreditNote", () => {
  it("issues a credit note within the original invoice baseline", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const invoiceLine = { ...baseSalesInvoiceLine, salesInvoiceId: baseCreditNote.salesInvoiceId };
    spies.select
      .mockReturnValueOnce(baseCreditNote)
      .mockReturnValueOnce([baseCreditNoteLine])
      .mockReturnValueOnce(invoiceLine)
      .mockReturnValueOnce([issuedCreditNote])
      .mockReturnValueOnce([]);
    spies.update.mockReturnValueOnce({
      ...baseCreditNote,
      status: "ISSUED",
      creditNoteNumber: `CN-${baseCreditNote.id}`,
      correctionSnapshot: JSON.stringify({ issuedAt: new Date().toISOString() }),
    });

    const result = await run(db, { salesCreditNoteId: baseCreditNote.id }, commandCtx);

    const value = expectOk(result);
    expect(value.salesCreditNote.status).toBe("ISSUED");
  });

  it("throws when credit quantity exceeds original billed quantity", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const invoiceLine = { ...baseSalesInvoiceLine, salesInvoiceId: baseCreditNote.salesInvoiceId };
    spies.select
      .mockReturnValueOnce(baseCreditNote)
      .mockReturnValueOnce([{ ...baseCreditNoteLine, quantity: 4 }])
      .mockReturnValueOnce(invoiceLine)
      .mockReturnValueOnce([issuedCreditNote])
      .mockReturnValueOnce([
        { ...baseCreditNoteLine, salesCreditNoteId: issuedCreditNote.id, quantity: 2 },
      ]);

    const result = await run(db, { salesCreditNoteId: baseCreditNote.id }, commandCtx);

    expectErr(result, CreditLimitExceededError);
  });

  it("throws when credit note is not in DRAFT status", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(issuedCreditNote);

    const result = await run(db, { salesCreditNoteId: issuedCreditNote.id }, commandCtx);

    expectErr(result, InvalidCreditNoteStatusError);
  });
});
