import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  WarehouseNotFoundError,
  WarehouseInactiveError,
  InvalidLocationTypeError,
  ParentZoneNotFoundError,
  ParentNotAZoneError,
  ParentInDifferentWarehouseError,
  DuplicateLocationNameError,
} from "../lib/errors.generated";
import { baseActiveWarehouse, baseInactiveWarehouse, baseZone, baseBin } from "../testing/fixtures";
import { run } from "./createStorageLocation";
import type { CommandContext } from "@tailor-platform/erp-kit/module";

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

  it("returns error when warehouse not found", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(undefined); // warehouse not found

    const result = await run(
      db,
      { name: "Zone A", code: "ZA", warehouseId: "nonexistent", locationType: "zone" },
      ctx,
    );

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

  it("returns error when warehouse inactive", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(baseInactiveWarehouse); // warehouse inactive

    const result = await run(
      db,
      { name: "Zone A", code: "ZA", warehouseId: baseInactiveWarehouse.id, locationType: "zone" },
      ctx,
    );

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

  it("returns error when invalid location type", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(baseActiveWarehouse); // warehouse found

    const result = await run(
      db,
      { name: "Area 1", code: "A1", warehouseId: baseActiveWarehouse.id, locationType: "shelf" },
      ctx,
    );

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

  it("returns error when bin has no parent", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(baseActiveWarehouse); // warehouse found

    const result = await run(
      db,
      { name: "Bin 1", code: "B1", warehouseId: baseActiveWarehouse.id, locationType: "bin" },
      ctx,
    );

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

  it("returns error when bin parent is another bin", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(baseActiveWarehouse) // warehouse found
      .mockReturnValueOnce(baseBin); // parent is a bin, not a zone

    const result = await run(
      db,
      {
        name: "Bin 2",
        code: "B2",
        warehouseId: baseActiveWarehouse.id,
        locationType: "bin",
        parentId: baseBin.id,
      },
      ctx,
    );

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

  it("returns error when parent zone is in different warehouse", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const zoneInOtherWarehouse = { ...baseZone, warehouseId: "warehouse-other" };
    spies.select
      .mockReturnValueOnce(baseActiveWarehouse) // warehouse found
      .mockReturnValueOnce(zoneInOtherWarehouse); // parent in different warehouse

    const result = await run(
      db,
      {
        name: "Bin 3",
        code: "B3",
        warehouseId: baseActiveWarehouse.id,
        locationType: "bin",
        parentId: zoneInOtherWarehouse.id,
      },
      ctx,
    );

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

  it("returns error when location name already exists", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(baseActiveWarehouse) // warehouse found
      .mockReturnValueOnce(baseZone); // duplicate name found

    const result = await run(
      db,
      { name: "Zone A", code: "ZA-NEW", warehouseId: baseActiveWarehouse.id, locationType: "zone" },
      ctx,
    );

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

  it("creates zone with null parentId", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const createdZone = { ...baseZone, id: "new-zone-id", name: "Zone B", code: "ZB" };

    spies.select
      .mockReturnValueOnce(baseActiveWarehouse) // warehouse found
      .mockReturnValueOnce(undefined); // no duplicate
    spies.insert.mockReturnValue(createdZone);

    const result = await run(
      db,
      { name: "Zone B", code: "ZB", warehouseId: baseActiveWarehouse.id, locationType: "zone" },
      ctx,
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.storageLocation.locationType).toBe("zone");
      expect(result.value.storageLocation.parentId).toBeNull();
      expect(result.value.storageLocation.name).toBe("Zone B");
    }
    expect(spies.insert).toHaveBeenCalled();
  });

  it("creates bin with parent zone", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const createdBin = {
      ...baseBin,
      id: "new-bin-id",
      name: "Bin A-2",
      code: "BA2",
      parentId: baseZone.id,
    };

    spies.select
      .mockReturnValueOnce(baseActiveWarehouse) // warehouse found
      .mockReturnValueOnce(baseZone) // parent zone found
      .mockReturnValueOnce(undefined); // no duplicate
    spies.insert.mockReturnValue(createdBin);

    const result = await run(
      db,
      {
        name: "Bin A-2",
        code: "BA2",
        warehouseId: baseActiveWarehouse.id,
        locationType: "bin",
        parentId: baseZone.id,
      },
      ctx,
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.storageLocation.locationType).toBe("bin");
      expect(result.value.storageLocation.parentId).toBe(baseZone.id);
    }
    expect(spies.insert).toHaveBeenCalled();
  });

  it("creates location with storage condition", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const createdZone = {
      ...baseZone,
      id: "new-zone-id",
      name: "Cold Zone",
      code: "CZ",
      storageCondition: "cold",
    };

    spies.select
      .mockReturnValueOnce(baseActiveWarehouse) // warehouse found
      .mockReturnValueOnce(undefined); // no duplicate
    spies.insert.mockReturnValue(createdZone);

    const result = await run(
      db,
      {
        name: "Cold Zone",
        code: "CZ",
        warehouseId: baseActiveWarehouse.id,
        locationType: "zone",
        storageCondition: "cold",
      },
      ctx,
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.storageLocation.storageCondition).toBe("cold");
    }
  });
});
