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,
  InvalidReservationQuantitiesError,
  ItemNotFoundError,
  SiteNotFoundError,
  StorageLocationNotFoundError,
} from "../lib/errors.generated";
import {
  mockItemManagementQueries,
  mockOrganizationQueries,
  type ItemManagementQueryItem,
  type OrganizationQuerySite,
} from "../testing/commandTestUtils";
import {
  baseActiveItem,
  baseActiveSite,
  baseStockLevel,
  baseStockReservation,
} from "../testing/fixtures";
import { run, type CreateStockReservationInput } from "./createStockReservation";

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

  const mockItemQueries = (item: ItemManagementQueryItem) => mockItemManagementQueries(item);
  const mockOrgQueries = (site: OrganizationQuerySite) => mockOrganizationQueries(site);
  const input: CreateStockReservationInput = {
    sourceType: "SALES_ORDER",
    sourceId: "sales-order-1",
    sourceLineId: "sales-order-line-1",
    itemId: "item-1",
    siteId: "site-1",
    reservedQuantity: "10",
    requiredDate: new Date("2024-01-15T00:00:00.000Z"),
  };

  it("creates reservation successfully", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([{ id: "location-1" }], [baseStockLevel], [], [baseStockReservation]);

    const result = await mock.withTx((trx) =>
      run(trx, input, ctx, mockItemQueries(baseActiveItem), mockOrgQueries(baseActiveSite)),
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.stockReservation.reservedQuantity).toBe("10");
      expect(result.value.stockReservation.consumedQuantity).toBe("0");
    }
    expect(mock.inserts.length).toBeGreaterThan(0);
  });

  it("appends another reservation row for the same source line", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults(
      [{ id: "location-1" }],
      [baseStockLevel],
      [{ ...baseStockReservation, reservedQuantity: "5" }],
      [{ ...baseStockReservation, id: "stock-reservation-2" }],
    );

    const result = await mock.withTx((trx) =>
      run(
        trx,
        { ...input, reservedQuantity: "5" },
        ctx,
        mockItemQueries(baseActiveItem),
        mockOrgQueries(baseActiveSite),
      ),
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.stockReservation.id).toBe("stock-reservation-2");
    }
    expect(mock.inserts[0].insertValues().sourceLineId).toBe("sales-order-line-1");
  });

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

    const result = await mock.withTx((trx) =>
      run(
        trx,
        { ...input, reservedQuantity: "0" },
        ctx,
        mockItemQueries(baseActiveItem),
        mockOrgQueries(baseActiveSite),
      ),
    );

    expect(result.ok).toBe(false);
    if (!result.ok) {
      expect(result.error).toBeInstanceOf(InvalidReservationQuantitiesError);
    }
  });

  it("returns error when item not found", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();

    const result = await mock.withTx((trx) =>
      run(
        trx,
        { ...input, itemId: "nonexistent" },
        ctx,
        mockItemQueries(null),
        mockOrgQueries(baseActiveSite),
      ),
    );

    expect(result.ok).toBe(false);
    if (!result.ok) {
      expect(result.error).toBeInstanceOf(ItemNotFoundError);
    }
  });

  it("returns error when site not found", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();

    const result = await mock.withTx((trx) =>
      run(trx, input, ctx, mockItemQueries(baseActiveItem), mockOrgQueries(null)),
    );

    expect(result.ok).toBe(false);
    if (!result.ok) {
      expect(result.error).toBeInstanceOf(SiteNotFoundError);
    }
  });

  it("returns error when optional location not found", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([]);

    const result = await mock.withTx((trx) =>
      run(
        trx,
        { ...input, storageLocationId: "nonexistent" },
        ctx,
        mockItemQueries(baseActiveItem),
        mockOrgQueries(baseActiveSite),
      ),
    );

    expect(result.ok).toBe(false);
    if (!result.ok) {
      expect(result.error).toBeInstanceOf(StorageLocationNotFoundError);
    }
  });

  it("returns error when insufficient available stock", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults(
      [{ id: "location-1" }],
      [baseStockLevel],
      [{ ...baseStockReservation, reservedQuantity: "80" }],
    );

    const result = await mock.withTx((trx) =>
      run(
        trx,
        { ...input, reservedQuantity: "20" },
        ctx,
        mockItemQueries(baseActiveItem),
        mockOrgQueries(baseActiveSite),
      ),
    );

    expect(result.ok).toBe(false);
    if (!result.ok) {
      expect(result.error).toBeInstanceOf(InsufficientAvailableStockError);
    }
  });
});
