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

export type UpdateProductCategoryInput = (
  | {
      id: string;
    }
  | {
      code: string;
    }
) & {
  name?: string;
};

/**
 * Function: updateProductCategory
 *
 * Updates the display name of an existing product category.
 * The category can be looked up by id or code.
 */
export async function run<CF extends Record<string, unknown>>(
  db: Transaction,
  input: UpdateProductCategoryInput & Omit<Partial<CF>, "status">,
) {
  const { name } = input;

  const KNOWN_KEYS = new Set(["id", "code", "name"]);
  const customFields: Record<string, unknown> = {};
  for (const [key, value] of Object.entries(input as Record<string, unknown>)) {
    if (!KNOWN_KEYS.has(key)) {
      customFields[key] = value;
    }
  }

  // 1. Check category exists (lookup by id or code)
  const category =
    "id" in input
      ? await db
          .selectFrom("ProductCategory")
          .selectAll()
          .where("id", "=", (input as { id: string }).id)
          .forUpdate()
          .executeTakeFirst()
      : await db
          .selectFrom("ProductCategory")
          .selectAll()
          .where("code", "=", (input as { code: string }).code)
          .forUpdate()
          .executeTakeFirst();

  if (!category) {
    const key = "id" in input ? (input as { id: string }).id : (input as { code: string }).code;
    return err(new CategoryNotFoundError(key));
  }

  const hasNameUpdate = name !== undefined;
  const hasCustomFields = Object.keys(customFields).length > 0;

  if (!hasNameUpdate && !hasCustomFields) {
    return err(new MissingRequiredFieldError(category.id));
  }

  if (hasNameUpdate && !name?.trim()) {
    return err(new MissingRequiredFieldError("name"));
  }

  const updates: Updateable<"ProductCategory"> = {
    ...(customFields as Updateable<"ProductCategory">),
  };
  if (hasNameUpdate) updates.name = name;

  const updated = await db
    .updateTable("ProductCategory")
    .set(updates)
    .where("id", "=", category.id)
    .returningAll()
    .executeTakeFirstOrThrow();

  return ok({ category: updated });
}
