import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import { Transaction } 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 { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValue(baseUoMCategory);

    const result = await run(db, {
      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 { db, spies } = createMockDb<Transaction>();
    const createdCategory = {
      ...baseUoMCategory,
      id: "new-category-id",
      referenceUnitId: "new-unit-id",
    };
    const createdUnit = {
      ...baseUnitKg,
      id: "new-unit-id",
      categoryId: "new-category-id",
    };

    spies.select.mockReturnValue(undefined); // No existing category
    spies.insert.mockReturnValueOnce(createdCategory).mockReturnValueOnce(createdUnit);
    spies.update.mockReturnValue({ ...createdCategory, referenceUnitId: "new-unit-id" });

    const result = await run(db, {
      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.isActive).toBe(true);
      expect(result.value.referenceUnit.conversionFactor).toBe(1.0);
    }
    expect(spies.insert).toHaveBeenCalled();
  });

  it("passes custom fields through to insert", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const createdCategory = {
      ...baseUoMCategory,
      id: "new-category-id",
      referenceUnitId: "new-unit-id",
    };
    const createdUnit = {
      ...baseUnitKg,
      id: "new-unit-id",
      categoryId: "new-category-id",
    };

    spies.select.mockReturnValue(undefined);
    spies.insert.mockReturnValueOnce(createdCategory).mockReturnValueOnce(createdUnit);
    spies.update.mockReturnValue({ ...createdCategory, referenceUnitId: "new-unit-id" });

    await run(db, {
      name: "Weight",
      priority: 1,
      referenceUnit: {
        name: "Kilogram",
        symbol: "kg",
        roundingPrecision: 2,
        tolerance: 0.01,
      },
    });

    // Category insert includes category custom field
    expect(spies.values).toHaveBeenNthCalledWith(1, expect.objectContaining({ priority: 1 }));
    // Unit insert includes unit custom field
    expect(spies.values).toHaveBeenNthCalledWith(2, expect.objectContaining({ tolerance: 0.01 }));
  });
});
