import { createKyselyMock } from "@tailor-platform/sdk/vitest";
import { describe, it } from "vitest";
import type { Namespace } from "../generated/kysely-tailordb";
import {
  BillingPolicyViolationError,
  DueScheduleNotComputableError,
} from "../lib/errors.generated";
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
import {
  basePaymentTerm,
  baseSalesInvoice,
  baseSalesInvoiceLine,
  baseSalesOrderLine,
  baseShipmentLine,
} from "../testing/fixtures";
import { run } from "./issueSalesInvoice";

describe("issueSalesInvoice", () => {
  it("issues an order-based invoice when quantities and prices are valid", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const invoice = { ...baseSalesInvoice, billingPolicy: "ORDER" as const };
    mock.enqueueResults(
      [invoice],
      [baseSalesInvoiceLine],
      [baseSalesOrderLine],
      [basePaymentTerm],
      [baseSalesOrderLine],
      [{ ...baseSalesOrderLine, invoicedQuantity: 5 }],
      [{ ...invoice, status: "ISSUED" as const }],
    );

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

    expectOk(result);
  });

  it("issues a shipment-gated invoice when posted shipment evidence exists", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const shipmentInvoiceLine = {
      ...baseSalesInvoiceLine,
      shipmentLineId: baseShipmentLine.id,
      billingPolicy: "SHIPMENT" as const,
      quantity: baseShipmentLine.quantity,
    };
    const invoice = { ...baseSalesInvoice, billingPolicy: "SHIPMENT" as const };
    mock.enqueueResults(
      [invoice],
      [shipmentInvoiceLine],
      [baseSalesOrderLine],
      [baseShipmentLine],
      [basePaymentTerm],
      [baseSalesOrderLine],
      [{ ...baseSalesOrderLine, invoicedQuantity: 4 }],
      [{ ...invoice, status: "ISSUED" as const }],
    );

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

    expectOk(result);
  });

  it("throws when draft invoice exceeds allowed billed quantity", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults(
      [baseSalesInvoice],
      [{ ...baseSalesInvoiceLine, quantity: 11 }],
      [baseSalesOrderLine],
    );

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

    expectErr(result, BillingPolicyViolationError);
  });

  it("throws when due schedule cannot be computed", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([{ ...baseSalesInvoice, paymentTermId: null }]);

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

    expectErr(result, DueScheduleNotComputableError);
  });
});
