import { createKyselyMock } from "@tailor-platform/sdk/vitest";
import { describe, expect, it } from "vitest";
import type { Namespace } from "../generated/kysely-tailordb";
import { CreditLimitExceededError, InvalidCreditNoteStatusError } from "../lib/errors.generated";
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
import {
  baseCreditNote,
  baseCreditNoteLine,
  baseSalesInvoiceLine,
  issuedCreditNote,
} from "../testing/fixtures";
import { run } from "./issueSalesCreditNote";

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

    const result = await mock.withTx((trx) =>
      run(trx, { 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 mock = createKyselyMock<Namespace["main-db"]>();
    const invoiceLine = { ...baseSalesInvoiceLine, salesInvoiceId: baseCreditNote.salesInvoiceId };
    mock.enqueueResults(
      [baseCreditNote],
      [{ ...baseCreditNoteLine, quantity: 4 }],
      [invoiceLine],
      [issuedCreditNote],
      [{ ...baseCreditNoteLine, salesCreditNoteId: issuedCreditNote.id, quantity: 2 }],
    );

    const result = await mock.withTx((trx) =>
      run(trx, { salesCreditNoteId: baseCreditNote.id }, commandCtx),
    );

    expectErr(result, CreditLimitExceededError);
  });

  it("throws when credit note is not in DRAFT status", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([issuedCreditNote]);

    const result = await mock.withTx((trx) =>
      run(trx, { salesCreditNoteId: issuedCreditNote.id }, commandCtx),
    );

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