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

export type UpdateUserInput = { id: string } & { name?: string; email?: string };

const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

/**
 * Function: updateUser
 *
 * Updates an existing user's name or email.
 * At least one field must be provided. Email must be unique.
 */
export async function run<CF extends Record<string, unknown>>(
  db: Transaction,
  input: UpdateUserInput & Omit<Partial<CF>, "status">,
) {
  const { id, name, email, ...customFields } = input;

  if (name === undefined && email === undefined && Object.keys(customFields).length === 0) {
    return err(new MissingRequiredFieldError("name or email"));
  }

  if (name?.trim() === "") {
    return err(new InvalidNameError(name));
  }

  if (email !== undefined && !EMAIL_PATTERN.test(email)) {
    return err(new InvalidEmailError(email));
  }

  const user = await db
    .selectFrom("User")
    .selectAll()
    .where("id", "=", id)
    .forUpdate()
    .executeTakeFirst();

  if (!user) {
    return err(new UserNotFoundError(id));
  }

  if (email !== undefined && email !== user.email) {
    const existingUser = await db
      .selectFrom("User")
      .selectAll()
      .where("email", "=", email)
      .forUpdate()
      .executeTakeFirst();

    if (existingUser) {
      return err(new UserAlreadyExistsError(email));
    }
  }

  const updates: Updateable<"User"> = {
    ...(customFields as Updateable<"User">),
  };
  if (name !== undefined) updates.name = name;
  if (email !== undefined) updates.email = email;

  const updatedUser = await db
    .updateTable("User")
    .set(updates)
    .where("id", "=", id)
    .returningAll()
    .executeTakeFirstOrThrow();

  return ok({ user: updatedUser });
}
