import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  EmptyOrderNotAllowedError,
  OrderNotEditableError,
  SalesOrderNotFoundError,
} from "../lib/errors.generated";
import {
  baseItem,
  baseOrderLineInput,
  baseSalesOrder,
  baseSalesOrderLine,
} from "../testing/fixtures";
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
import { run } from "./updateSalesOrder";

describe("updateSalesOrder", () => {
  it("updates mutable draft order header fields", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const updated = {
      ...baseSalesOrder,
      requestedShipDate: new Date("2024-01-05T00:00:00.000Z"),
      updatedAt: new Date("2024-01-02T00:00:00.000Z"),
    };
    spies.select.mockReturnValueOnce(baseSalesOrder);
    spies.update.mockReturnValueOnce(updated);

    const result = await run(
      db,
      { salesOrderId: baseSalesOrder.id, requestedShipDate: updated.requestedShipDate },
      commandCtx,
    );

    expectOk(result);
  });

  it("updates draft order lines and preserves line integrity", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(baseSalesOrder).mockReturnValueOnce(baseItem);
    spies.update.mockReturnValueOnce(baseSalesOrder);
    spies.insert.mockReturnValueOnce({
      ...baseSalesOrderLine,
      quantity: 5,
      salesOrderId: baseSalesOrder.id,
    });

    const result = await run(
      db,
      {
        salesOrderId: baseSalesOrder.id,
        lines: [{ ...baseOrderLineInput, quantity: 5 }],
      },
      commandCtx,
    );

    expectOk(result);
    expect(spies.delete).toHaveBeenCalled();
    expect(spies.values).toHaveBeenLastCalledWith(
      expect.objectContaining({
        salesOrderId: baseSalesOrder.id,
        quantity: 5,
      }),
    );
  });

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

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

    expectErr(result, SalesOrderNotFoundError);
  });

  it("returns error when updating a non-draft order", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce({ ...baseSalesOrder, status: "SUBMITTED" });

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

    expectErr(result, OrderNotEditableError);
  });

  it("returns error when the update would remove all lines", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(baseSalesOrder);

    const result = await run(db, { salesOrderId: baseSalesOrder.id, lines: [] }, commandCtx);

    expectErr(result, EmptyOrderNotAllowedError);
  });
});
