import { EmailAddress } from "@/core/common/domain";
import { SequelizeRepository } from "@/core/common/infrastructure";
import { Result } from "@repo/rdx-utils";
import { Sequelize, Transaction } from "sequelize";
import { AuthenticatedUser, IAuthenticatedUserRepository, Username } from "../../domain";
import { IAuthenticatedUserMapper } from "../mappers";
import { AuthUserModel } from "./auth-user.model";

export class AuthenticatedUserRepository
  extends SequelizeRepository<AuthenticatedUser>
  implements IAuthenticatedUserRepository
{
  private readonly _mapper!: IAuthenticatedUserMapper;

  /**
   * 🔹 Función personalizada para mapear errores de unicidad en autenticación
   */
  private _customErrorMapper(error: Error): string | null {
    if (error.name === "SequelizeUniqueConstraintError") {
      return "User with this email or username already exists";
    }

    return null;
  }

  constructor(database: Sequelize, mapper: IAuthenticatedUserMapper) {
    super(database);
    this._mapper = mapper;
  }

  async userExists(
    username: Username,
    email: EmailAddress,
    transaction?: Transaction
  ): Promise<Result<boolean, Error>> {
    try {
      const userWithEmail = await this._findById(
        AuthUserModel,
        "email",
        email.toString(),
        transaction
      );

      const userWithUsername = await this._findById(
        AuthUserModel,
        "username",
        username.toString(),
        transaction
      );

      return Result.ok(Boolean(userWithEmail || userWithUsername));
    } catch (error: unknown) {
      return this._handleDatabaseError(error, this._customErrorMapper);
    }
  }

  async getUserByEmail(
    email: EmailAddress,
    transaction?: Transaction
  ): Promise<Result<AuthenticatedUser, Error>> {
    try {
      const rawUser = await this._getBy(AuthUserModel, "email", email.toString(), {}, transaction);

      if (!rawUser === true) {
        return Result.fail(new Error("User with email not exists"));
      }

      return this._mapper.mapToDomain(rawUser);
    } catch (error: unknown) {
      return this._handleDatabaseError(error, this._customErrorMapper);
    }
  }

  async createUser(
    user: AuthenticatedUser,
    transaction?: Transaction
  ): Promise<Result<void, Error>> {
    try {
      const persistenceData = this._mapper.mapToPersistence(user);
      await AuthUserModel.create(persistenceData, { transaction });
      return Result.ok();
    } catch (error: unknown) {
      return this._handleDatabaseError(error, this._customErrorMapper);
    }
  }
}
