import { createKyselyMock } from "@tailor-platform/sdk/vitest";
import { describe, expect, it } from "vitest";
import type { Namespace } from "../generated/kysely-tailordb";
import {
  DescriptionRequiredError,
  EmptyPurchaseRequisitionLinesError,
  InvalidEstimatedUnitPriceError,
  InvalidQuantityError,
  PurchaseRequisitionNotDraftError,
  PurchaseRequisitionNotFoundError,
} from "../lib/errors.generated";
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
import { basePurchaseRequisition, basePurchaseRequisitionLine } from "../testing/fixtures";
import { run } from "./submitPurchaseRequisition";

describe("submitPurchaseRequisition", () => {
  it("submits a valid draft purchase requisition", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const line = { ...basePurchaseRequisitionLine };
    mock.enqueueResults(
      [basePurchaseRequisition], // selectFrom PurchaseRequisition
      [line], // selectFrom PurchaseRequisitionLine (execute → array)
      [{ ...basePurchaseRequisition, status: "SUBMITTED" }], // update PurchaseRequisition
    );

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

    expect(expectOk(result).purchaseRequisition.status).toBe("SUBMITTED");
  });

  it("returns error when purchase requisition does not exist", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([]);

    const result = await mock.withTx((trx) => run(trx, { id: "missing-pr" }, commandCtx));

    expectErr(result, PurchaseRequisitionNotFoundError);
  });

  it("returns error when purchase requisition is not in draft", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([{ ...basePurchaseRequisition, status: "SUBMITTED" }]);

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

    expectErr(result, PurchaseRequisitionNotDraftError);
  });

  it("returns error when purchase requisition has no lines", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults(
      [basePurchaseRequisition], // selectFrom PurchaseRequisition
      [], // selectFrom PurchaseRequisitionLine (execute → empty array)
    );

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

    expectErr(result, EmptyPurchaseRequisitionLinesError);
  });

  it("returns error when a line is missing a description", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const lineWithoutDescription = {
      ...basePurchaseRequisitionLine,
      description: "",
    };
    mock.enqueueResults(
      [basePurchaseRequisition], // selectFrom PurchaseRequisition
      [lineWithoutDescription], // selectFrom PurchaseRequisitionLine
    );

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

    expectErr(result, DescriptionRequiredError);
  });

  it("returns error when quantity is zero or negative", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const lineWithBadQuantity = {
      ...basePurchaseRequisitionLine,
      quantity: 0,
    };
    mock.enqueueResults(
      [basePurchaseRequisition], // selectFrom PurchaseRequisition
      [lineWithBadQuantity], // selectFrom PurchaseRequisitionLine
    );

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

    expectErr(result, InvalidQuantityError);
  });

  it("returns error when estimated unit price is negative", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const lineWithBadPrice = {
      ...basePurchaseRequisitionLine,
      estimatedUnitPrice: -1,
    };
    mock.enqueueResults(
      [basePurchaseRequisition], // selectFrom PurchaseRequisition
      [lineWithBadPrice], // selectFrom PurchaseRequisitionLine
    );

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

    expectErr(result, InvalidEstimatedUnitPriceError);
  });
});
