import { ok, err } from "@tailor-platform/erp-kit/core";
import { Transaction } from "../generated/kysely-tailordb";
import { DuplicateCategoryNameError } from "../lib/errors.generated";

interface CreateCategoryInput<UnitCF extends Record<string, unknown>> {
  name: string;
  description?: string;
  referenceUnit: {
    name: string;
    symbol: string;
    roundingPrecision: number;
  } & UnitCF;
}

/**
 * Function: createCategory
 *
 * Establishes a new unit of measure category that groups related units.
 * Creates the category with a reference unit that has conversion factor 1.0.
 */
export async function run<
  CatCF extends Record<string, unknown>,
  UnitCF extends Record<string, unknown>,
>(db: Transaction, input: CreateCategoryInput<UnitCF> & CatCF) {
  const { name, description, referenceUnit, ...categoryCustomFields } = input;
  const { name: unitName, symbol, roundingPrecision, ...unitCustomFields } = referenceUnit;

  // 1. Check if category name already exists
  const existingCategory = await db
    .selectFrom("UoMCategory")
    .selectAll()
    .where("name", "=", name)
    .forUpdate()
    .executeTakeFirst();

  if (existingCategory) {
    return err(new DuplicateCategoryNameError(name));
  }

  // 2. Create category without reference unit first
  const category = await db
    .insertInto("UoMCategory")
    .values({
      ...(categoryCustomFields as Record<string, unknown>),
      name,
      description: description ?? null,
      referenceUnitId: null,
      status: "ACTIVE" as const,
    })
    .returningAll()
    .executeTakeFirstOrThrow();

  // 3. Create reference unit with conversion factor 1.0
  const createdReferenceUnit = await db
    .insertInto("Unit")
    .values({
      ...(unitCustomFields as Record<string, unknown>),
      name: unitName,
      symbol,
      categoryId: category.id,
      conversionFactor: 1.0,
      roundingPrecision,
      status: "ACTIVE" as const,
    })
    .returningAll()
    .executeTakeFirstOrThrow();

  // 4. Update category with reference unit ID
  const updatedCategory = await db
    .updateTable("UoMCategory")
    .set({
      referenceUnitId: createdReferenceUnit.id,
    })
    .where("id", "=", category.id)
    .returningAll()
    .executeTakeFirstOrThrow();

  return ok({ uoMCategory: updatedCategory, referenceUnit: createdReferenceUnit });
}
