import { EmailAddress, UniqueID } from "@/core/common/domain";
import { SequelizeRepository } from "@/core/common/infrastructure";
import { Collection, Result } from "@repo/rdx-utils";
import { Sequelize, Transaction } from "sequelize";
import { IUserRepository, User } from "../../domain";
import { IUserMapper } from "../mappers";
import { UserModel } from "./user.model";

export class UserRepository extends SequelizeRepository<User> implements IUserRepository {
  private readonly _mapper!: IUserMapper;

  /**
   * 🔹 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: IUserMapper) {
    super(database);
    this._mapper = mapper;
  }

  async findAll(transaction?: Transaction): Promise<Result<Collection<User>, Error>> {
    try {
      const rawUsers = await this._findAll(UserModel, {}, transaction);

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

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

  async findById(id: UniqueID, transaction?: Transaction): Promise<Result<User, Error>> {
    try {
      const rawUser = await this._getById(UserModel, id, {}, transaction);

      if (!rawUser === true) {
        return Result.fail(new Error(`User with id ${id.toString()} not exists`));
      }

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

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

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

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