import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { Transaction } 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 { db, spies } = createMockDb<Transaction>();
    const line = { ...basePurchaseRequisitionLine };
    spies.select
      .mockReturnValueOnce(basePurchaseRequisition) // selectFrom PurchaseRequisition
      .mockReturnValueOnce([line]); // selectFrom PurchaseRequisitionLine (execute → array)
    spies.update.mockReturnValueOnce({
      ...basePurchaseRequisition,
      status: "SUBMITTED",
    });

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

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

  it("returns error when purchase requisition does not exist", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(undefined);

    const result = await run(db, { id: "missing-pr" }, commandCtx);

    expectErr(result, PurchaseRequisitionNotFoundError);
  });

  it("returns error when purchase requisition is not in draft", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce({
      ...basePurchaseRequisition,
      status: "SUBMITTED",
    });

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

    expectErr(result, PurchaseRequisitionNotDraftError);
  });

  it("returns error when purchase requisition has no lines", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(basePurchaseRequisition) // selectFrom PurchaseRequisition
      .mockReturnValueOnce([]); // selectFrom PurchaseRequisitionLine (execute → empty array)

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

    expectErr(result, EmptyPurchaseRequisitionLinesError);
  });

  it("returns error when a line is missing a description", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const lineWithoutDescription = {
      ...basePurchaseRequisitionLine,
      description: "",
    };
    spies.select
      .mockReturnValueOnce(basePurchaseRequisition) // selectFrom PurchaseRequisition
      .mockReturnValueOnce([lineWithoutDescription]); // selectFrom PurchaseRequisitionLine

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

    expectErr(result, DescriptionRequiredError);
  });

  it("returns error when quantity is zero or negative", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const lineWithBadQuantity = {
      ...basePurchaseRequisitionLine,
      quantity: 0,
    };
    spies.select
      .mockReturnValueOnce(basePurchaseRequisition) // selectFrom PurchaseRequisition
      .mockReturnValueOnce([lineWithBadQuantity]); // selectFrom PurchaseRequisitionLine

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

    expectErr(result, InvalidQuantityError);
  });

  it("returns error when estimated unit price is negative", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const lineWithBadPrice = {
      ...basePurchaseRequisitionLine,
      estimatedUnitPrice: -1,
    };
    spies.select
      .mockReturnValueOnce(basePurchaseRequisition) // selectFrom PurchaseRequisition
      .mockReturnValueOnce([lineWithBadPrice]); // selectFrom PurchaseRequisitionLine

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

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