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

export type UpdateOwnProfileInput = {
  name?: string;
  email?: string;
};

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

export async function run<CF extends Record<string, unknown>>(
  db: Transaction,
  input: UpdateOwnProfileInput & Omit<Partial<CF>, "status">,
  ctx: CommandContext,
) {
  const { name, email, ...customFields } = input;

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

  if (!user) {
    return err(new UserNotFoundError(ctx.actorId));
  }

  if (user.status !== "ACTIVE") {
    return err(new InvalidStatusTransitionError(`${user.status} to UPDATE_OWN_PROFILE`));
  }

  if (name === undefined && email === undefined) {
    return err(new MissingRequiredFieldError("name or email"));
  }

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

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

    // Skip uniqueness check if email is unchanged
    if (email !== user.email) {
      const existingUser = await db
        .selectFrom("User")
        .selectAll()
        .where("email", "=", email)
        .forUpdate()
        .executeTakeFirst();

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

  const updatedUser = await db
    .updateTable("User")
    .set({
      ...(customFields as Record<string, unknown>),
      ...(name !== undefined ? { name } : {}),
      ...(email !== undefined ? { email } : {}),
      updatedAt: new Date(),
    })
    .where("id", "=", ctx.actorId)
    .returningAll()
    .executeTakeFirst();

  return ok({ user: updatedUser! });
}
