import { prisma } from '@documenso/prisma';
import { hash } from '@node-rs/bcrypt';
import type { User } from '@prisma/client';

import { SALT_ROUNDS } from '../../constants/auth';
import { AppError, AppErrorCode } from '../../errors/app-error';
import { createPersonalOrganisation } from '../organisation/create-organisation';

export interface CreateUserOptions {
  name: string;
  email: string;
  password: string;
  signature?: string | null;
}

export const createUser = async ({ name, email, password, signature }: CreateUserOptions) => {
  const hashedPassword = await hash(password, SALT_ROUNDS);

  const userExists = await prisma.user.findFirst({
    where: {
      email: email.toLowerCase(),
    },
  });

  if (userExists) {
    throw new AppError(AppErrorCode.ALREADY_EXISTS);
  }

  const user = await prisma.user.create({
    data: {
      name,
      email: email.toLowerCase(),
      password: hashedPassword, // Todo: (RR7) Drop password.
      signature,
    },
  });

  // Todo: (RR7) Migrate to use this after RR7.
  // Note: If we actually ever proceed with this, there are multiple
  // locations where we will need to update this.
  // const user = await prisma.$transaction(async (tx) => {
  //   const user = await tx.user.create({
  //     data: {
  //       name,
  //       email: email.toLowerCase(),
  //       password: hashedPassword, // Todo: (RR7) Drop password.
  //       signature,
  //     },
  //   });

  //   await tx.account.create({
  //     data: {
  //       userId: user.id,
  //       type: 'emailPassword', // Todo: (RR7)
  //       provider: 'DOCUMENSO', // Todo: (RR7) Enums
  //       providerAccountId: user.id.toString(),
  //       password: hashedPassword,
  //     },
  //   });

  //   return user;
  // });

  // Not used at the moment, uncomment if required.
  await onCreateUserHook(user).catch((err) => {
    // Todo: (RR7) Add logging.
    console.error(err);
  });

  return user;
};

export type OnCreateUserHookOptions = {
  /**
   * When true, do not create a "Personal Organisation" for the new user.
   * Used by the Organisation SSO signup path, where the user is intended
   * to operate inside the SSO organisation rather than a personal space.
   *
   * Defaults to false — preserves the historical behaviour of creating a
   * personal organisation for every new user.
   */
  skipPersonalOrganisation?: boolean;
};

/**
 * Should be run after a user is created, example during email password signup or google sign in.
 *
 * @returns User
 */
export const onCreateUserHook = async (user: User, options: OnCreateUserHookOptions = {}) => {
  if (!options.skipPersonalOrganisation) {
    await createPersonalOrganisation({ userId: user.id });
  }

  return user;
};
