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 {
  EmptyTransferLinesError,
  InvalidTransferLocationsError,
  InvalidQuantityError,
} from "../lib/errors.generated";
import {
  expectErr,
  expectOk,
  mockItemManagementQueries,
  mockOrganizationQueries,
} from "../testing/commandTestUtils";
import {
  baseActiveItem,
  baseActiveSite,
  baseDraftTransferOrder,
  baseTransferOrderLine,
} from "../testing/fixtures";
import { run, type CreateTransferOrderInput } from "./createTransferOrder";

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

  const input: CreateTransferOrderInput = {
    header: {
      sourceSiteId: "site-1",
      destinationSiteId: "site-2",
      plannedShipmentDate: new Date("2024-01-15T00:00:00.000Z"),
      expectedReceiptDate: new Date("2024-01-17T00:00:00.000Z"),
    },
    lines: [
      {
        itemId: "item-1",
        orderedQuantity: "10",
      },
    ],
  };

  it("creates a DRAFT transfer order with lines", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([baseDraftTransferOrder], [baseTransferOrderLine]);

    const result = await mock.withTx((trx) =>
      run(
        trx,
        input,
        ctx,
        mockItemManagementQueries(baseActiveItem),
        mockOrganizationQueries(baseActiveSite),
      ),
    );
    const value = expectOk<{ transferOrder: { status: string } }>(result as never);

    expect(value.transferOrder.status).toBe("DRAFT");
    expect(mock.inserts[1].insertRows()).toEqual(
      expect.arrayContaining([
        expect.objectContaining({
          unitId: "unit-ea",
          orderedQuantity: "10",
          shippedQuantity: "0",
          receivedQuantity: "0",
        }),
      ]),
    );
  });

  it("persists custom header and line fields", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([baseDraftTransferOrder], [baseTransferOrderLine]);

    const result = await mock.withTx((trx) =>
      run<{ externalReference: string }, { memo: string }>(
        trx,
        {
          header: { ...input.header, externalReference: "EXT-1" },
          lines: [{ ...input.lines[0], memo: "Urgent move" }],
        },
        ctx,
        mockItemManagementQueries(baseActiveItem),
        mockOrganizationQueries(baseActiveSite),
      ),
    );

    expectOk(result as never);
    expect(mock.inserts[0].insertValues()).toEqual(
      expect.objectContaining({
        externalReference: "EXT-1",
        sourceSiteId: "site-1",
        destinationSiteId: "site-2",
        plannedShipmentDate: new Date("2024-01-15T00:00:00.000Z"),
        expectedReceiptDate: new Date("2024-01-17T00:00:00.000Z"),
      }),
    );
    expect(mock.inserts[1].insertRows()).toEqual(
      expect.arrayContaining([
        expect.objectContaining({
          memo: "Urgent move",
          unitId: "unit-ea",
          orderedQuantity: "10",
        }),
      ]),
    );
  });

  it("returns error when no lines are provided", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const result = await mock.withTx((trx) =>
      run(
        trx,
        { ...input, lines: [] },
        ctx,
        mockItemManagementQueries(baseActiveItem),
        mockOrganizationQueries(baseActiveSite),
      ),
    );

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

  it("returns error when source and destination sites are the same", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const result = await mock.withTx((trx) =>
      run(
        trx,
        { ...input, header: { ...input.header, destinationSiteId: "site-1" } },
        ctx,
        mockItemManagementQueries(baseActiveItem),
        mockOrganizationQueries(baseActiveSite),
      ),
    );

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

  it("returns error when ordered quantity is zero or negative", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();

    const result = await mock.withTx((trx) =>
      run(
        trx,
        {
          ...input,
          lines: [{ ...input.lines[0], orderedQuantity: "0" }],
        },
        ctx,
        mockItemManagementQueries(baseActiveItem),
        mockOrganizationQueries(baseActiveSite),
      ),
    );

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