import { Account } from "@/contexts/accounts/domain";
import { IAccountRepository } from "@/contexts/accounts/domain/repositories/account-repository.interface";
import { EmailAddress, UniqueID } from "@/core/common/domain";
import { SequelizeRepository } from "@/core/common/infrastructure";
import { Collection, Result } from "@repo/rdx-utils";
import { Transaction } from "sequelize";
import { IAccountMapper, accountMapper } from "../mappers/account.mapper";
import { AccountModel } from "./account.model";

class AccountRepository extends SequelizeRepository<Account> implements IAccountRepository {
  private readonly _mapper!: IAccountMapper;

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

    return null;
  }

  constructor(mapper: IAccountMapper) {
    super();
    this._mapper = mapper;
  }

  async accountExists(id: UniqueID, transaction?: Transaction): Promise<Result<boolean, Error>> {
    try {
      const _account = await this._getById(AccountModel, id, {}, transaction);

      return Result.ok(Boolean(id.equals(_account.id)));
    } catch (error: any) {
      return this._handleDatabaseError(error, this._customErrorMapper);
    }
  }

  async findAll(transaction?: Transaction): Promise<Result<Collection<Account>, Error>> {
    try {
      const rawAccounts: any = await this._findAll(AccountModel, {}, transaction);

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

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

  async findById(id: UniqueID, transaction?: Transaction): Promise<Result<Account, Error>> {
    try {
      const rawAccount: any = await this._getById(AccountModel, id, {}, transaction);

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

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

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

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

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

  async create(account: Account, transaction?: Transaction): Promise<void> {
    const accountData = this._mapper.mapToPersistence(account);
    await this._save(AccountModel, account.id, accountData, {}, transaction);
  }

  async update(account: Account, transaction?: Transaction): Promise<void> {
    const accountData = this._mapper.mapToPersistence(account);
    await this._save(AccountModel, account.id, accountData, {}, transaction);
  }
}

const accountRepository = new AccountRepository(accountMapper);
export { accountRepository };
