import type { CommandContext } from "@tailor-platform/erp-kit/core";
import { createKyselyMock } from "@tailor-platform/sdk/vitest";
import { describe, expect, it } from "vitest";
import type { Namespace } from "../generated/kysely-tailordb";
import {
  InsufficientAvailableStockError,
  InvalidStateTransitionError,
} from "../lib/errors.generated";
import { expectErr, expectOk } from "../testing/commandTestUtils";
import {
  baseClosedTransferOrder,
  baseDraftTransferOrder,
  baseStockLevel,
  baseTransferOrderLine,
} from "../testing/fixtures";
import { run } from "./openTransferOrder";

describe("openTransferOrder", () => {
  const ctx: CommandContext = {
    actorId: "test-actor",
    permissions: ["inventory:transferOrder:openTransferOrder"],
  };

  it("opens a DRAFT transfer order", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults(
      [baseDraftTransferOrder], // TransferOrder select
      [baseTransferOrderLine], // TransferOrderLine select
      [{ id: "location-1" }], // StorageLocation select
      [baseStockLevel], // StockLevel select
      [], // StockReservation (open) select
      [{ ...baseDraftTransferOrder, status: "OPEN" }], // TransferOrder update
      [], // StockReservation insert
      [], // InventorySupplyPlan insert
    );

    const result = await mock.withTx((trx) => run(trx, { id: "transfer-order-1" }, ctx));
    const value = expectOk<{
      transferOrder: { status: string };
      stockReservations: unknown[];
      supplyPlans: unknown[];
    }>(result as never);

    expect(value.transferOrder.status).toBe("OPEN");
    expect(mock.inserts[0].insertRows()).toEqual([
      expect.objectContaining({
        sourceType: "TRANSFER_ORDER",
        sourceId: "transfer-order-1",
        sourceLineId: "transfer-order-line-1",
        itemId: "item-1",
        siteId: "site-1",
        reservedQuantity: "10",
        consumedQuantity: "0",
        status: "OPEN",
        requiredDate: new Date("2024-01-15T00:00:00.000Z"),
      }),
    ]);
    expect(mock.inserts[1].insertRows()).toEqual([
      expect.objectContaining({
        sourceType: "TRANSFER_ORDER",
        sourceId: "transfer-order-1",
        sourceLineId: "transfer-order-line-1",
        itemId: "item-1",
        siteId: "site-2",
        expectedQuantity: "10",
        receivedQuantity: "0",
        status: "OPEN",
        expectedDate: new Date("2024-01-17T00:00:00.000Z"),
      }),
    ]);
  });

  it("returns error when transfer order is not DRAFT", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([baseClosedTransferOrder]);

    const result = await mock.withTx((trx) => run(trx, { id: "transfer-order-4" }, ctx));

    expectErr(result as never, InvalidStateTransitionError);
  });

  it("returns error when source site available stock cannot cover transfer demand", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults(
      [baseDraftTransferOrder],
      [baseTransferOrderLine],
      [{ id: "location-1" }],
      [{ ...baseStockLevel, quantity: "4" }],
      [],
    );

    const result = await mock.withTx((trx) => run(trx, { id: "transfer-order-1" }, ctx));

    expectErr(result as never, InsufficientAvailableStockError);
    expect(mock.updates).toHaveLength(0);
    expect(mock.inserts).toHaveLength(0);
  });

  it("aggregates repeated item demand before validating source site availability", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults(
      [baseDraftTransferOrder],
      [baseTransferOrderLine, { ...baseTransferOrderLine, id: "transfer-order-line-2" }],
      [{ id: "location-1" }],
      [{ ...baseStockLevel, quantity: "15" }],
      [],
    );

    const result = await mock.withTx((trx) => run(trx, { id: "transfer-order-1" }, ctx));

    expectErr(result as never, InsufficientAvailableStockError);
    expect(mock.updates).toHaveLength(0);
    expect(mock.inserts).toHaveLength(0);
  });
});
