import { createKyselyMock } from "@tailor-platform/sdk/vitest";
import { describe, expect, it } from "vitest";
import type { Namespace } from "../generated/kysely-tailordb";
import { DuplicateCategoryNameError } from "../lib/errors.generated";
import { baseUoMCategory, baseUnitKg } from "../testing/fixtures";
import { run } from "./createCategory";

describe("createCategory", () => {
  // Error cases
  it("throws when category name already exists", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([baseUoMCategory]);

    const result = await mock.withTx((trx) =>
      run(trx, {
        name: baseUoMCategory.name,
        referenceUnit: {
          name: "Kilogram",
          symbol: "kg",
          roundingPrecision: 2,
        },
      }),
    );

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

  // Success cases
  it("creates category with reference unit", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const createdCategory = {
      ...baseUoMCategory,
      id: "new-category-id",
      referenceUnitId: "new-unit-id",
    };
    const createdUnit = {
      ...baseUnitKg,
      id: "new-unit-id",
      categoryId: "new-category-id",
    };

    mock.enqueueResults(
      [], // No existing category
      [createdCategory],
      [createdUnit],
      [{ ...createdCategory, referenceUnitId: "new-unit-id" }],
    );

    const result = await mock.withTx((trx) =>
      run(trx, {
        name: "Weight",
        description: "Weight units",
        referenceUnit: {
          name: "Kilogram",
          symbol: "kg",
          roundingPrecision: 2,
        },
      }),
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.uoMCategory.name).toBe("Weight");
      expect(result.value.uoMCategory.status).toBe("ACTIVE");
      expect(result.value.referenceUnit.conversionFactor).toBe(1.0);
    }
    expect(mock.inserts.length).toBeGreaterThan(0);
  });

  it("passes custom fields through to insert", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const createdCategory = {
      ...baseUoMCategory,
      id: "new-category-id",
      referenceUnitId: "new-unit-id",
    };
    const createdUnit = {
      ...baseUnitKg,
      id: "new-unit-id",
      categoryId: "new-category-id",
    };

    mock.enqueueResults(
      [],
      [createdCategory],
      [createdUnit],
      [{ ...createdCategory, referenceUnitId: "new-unit-id" }],
    );

    await mock.withTx((trx) =>
      run(trx, {
        name: "Weight",
        priority: 1,
        referenceUnit: {
          name: "Kilogram",
          symbol: "kg",
          roundingPrecision: 2,
          tolerance: 0.01,
        },
      }),
    );

    // Category insert includes category custom field
    expect(mock.inserts[0].insertValues()).toEqual(expect.objectContaining({ priority: 1 }));
    // Unit insert includes unit custom field
    expect(mock.inserts[1].insertValues()).toEqual(expect.objectContaining({ tolerance: 0.01 }));
  });
});
