import type { CommandContext } from "@tailor-platform/erp-kit/core";
import { createKyselyMock } from "@tailor-platform/sdk/vitest";
import { describe, expect, it, vi } from "vitest";
import type { Namespace } from "../generated/kysely-tailordb";
import {
  SiteNotFoundError,
  SiteInactiveError,
  DuplicateLocationNameError,
} from "../lib/errors.generated";
import type { OrganizationQueries } from "../module";
import { baseActiveSite, baseInactiveSite, baseStorageLocation } from "../testing/fixtures";
import { run } from "./createStorageLocation";

const ok = <T>(value: T) => ({ ok: true as const, value });

function mockOrgQueries(site: unknown): Pick<OrganizationQueries, "getSite"> {
  return {
    getSite: vi.fn().mockResolvedValue(ok({ site })),
  } as unknown as Pick<OrganizationQueries, "getSite">;
}

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

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

    const result = await mock.withTx((trx) =>
      run(
        trx,
        { name: "Receiving Dock", code: "RCV-01", siteId: "nonexistent" },
        ctx,
        mockOrgQueries(null),
      ),
    );

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

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

    const result = await mock.withTx((trx) =>
      run(
        trx,
        { name: "Receiving Dock", code: "RCV-01", siteId: baseInactiveSite.id },
        ctx,
        mockOrgQueries(baseInactiveSite),
      ),
    );

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

  it("returns error when location name already exists in site", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([baseStorageLocation]);

    const result = await mock.withTx((trx) =>
      run(
        trx,
        { name: baseStorageLocation.name, code: "NEW", siteId: baseActiveSite.id },
        ctx,
        mockOrgQueries(baseActiveSite),
      ),
    );

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

  it("creates storage location", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const created = {
      ...baseStorageLocation,
      id: "new-loc-id",
      name: "Main Shelf",
      code: "MAIN",
    };

    mock.enqueueResults([], [created]);

    const result = await mock.withTx((trx) =>
      run(
        trx,
        { name: "Main Shelf", code: "MAIN", siteId: baseActiveSite.id },
        ctx,
        mockOrgQueries(baseActiveSite),
      ),
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.storageLocation.name).toBe("Main Shelf");
      expect(result.value.storageLocation.siteId).toBe(baseActiveSite.id);
    }
    expect(mock.inserts).toHaveLength(1);
  });

  it("creates storage location without code", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const created = {
      ...baseStorageLocation,
      id: "new-loc-id",
      name: "Quarantine",
      code: null,
    };

    mock.enqueueResults([], [created]);

    const result = await mock.withTx((trx) =>
      run(
        trx,
        { name: "Quarantine", siteId: baseActiveSite.id },
        ctx,
        mockOrgQueries(baseActiveSite),
      ),
    );

    expect(result.ok).toBe(true);
    expect(mock.inserts).toHaveLength(1);
  });

  it("creates storage location with storage condition", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const created = {
      ...baseStorageLocation,
      id: "new-loc-id",
      name: "Cold Area",
      code: "COLD",
      storageCondition: "cold",
    };

    mock.enqueueResults([], [created]);

    const result = await mock.withTx((trx) =>
      run(
        trx,
        {
          name: "Cold Area",
          code: "COLD",
          siteId: baseActiveSite.id,
          storageCondition: "cold",
        },
        ctx,
        mockOrgQueries(baseActiveSite),
      ),
    );

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