import {
  IUseCase,
  IUseCaseError,
  IUseCaseRequest,
  UseCaseError,
} from "@/contexts/common/application";
import { IRepositoryManager } from "@/contexts/common/domain";
import { IInfrastructureError } from "@/contexts/common/infrastructure";
import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
import { Result, ensureEmailIsValid } from "@shared/contexts";
import { AuthUser } from "../domain";
import { findUserByEmail } from "./authServices";

export interface FindUserByEmailRequest extends IUseCaseRequest {
  email: string;
}

export type FindUserByEmailResponseOrError =
  | Result<never, IUseCaseError>
  | Result<AuthUser, never>;

export class FindUserByEmailUseCase
  implements
    IUseCase<FindUserByEmailRequest, Promise<FindUserByEmailResponseOrError>>
{
  private _adapter: ISequelizeAdapter;
  private _repositoryManager: IRepositoryManager;

  constructor(props: {
    adapter: ISequelizeAdapter;
    repositoryManager: IRepositoryManager;
  }) {
    this._adapter = props.adapter;
    this._repositoryManager = props.repositoryManager;
  }

  private getRepositoryByName<T>(name: string) {
    return this._repositoryManager.getRepository<T>(name);
  }

  async execute(
    request: FindUserByEmailRequest,
  ): Promise<FindUserByEmailResponseOrError> {
    const { email } = request;

    // Validaciones de datos

    const emailOrError = ensureEmailIsValid(email);
    if (emailOrError.isFailure) {
      return Result.fail(
        UseCaseError.create(
          UseCaseError.INVALID_INPUT_DATA,
          "Email or password is not valid",
          emailOrError.error,
        ),
      );
    }

    try {
      const user = await findUserByEmail(
        emailOrError.object,
        this._adapter,
        this.getRepositoryByName("Auth"),
      );

      if (user === null) {
        return Result.fail(
          UseCaseError.create(
            UseCaseError.NOT_FOUND_ERROR,
            `User with email ${email} not found`,
          ),
        );
      }
      return Result.ok<AuthUser>(user);
    } catch (error: unknown) {
      const _error = error as IInfrastructureError;
      return Result.fail(
        UseCaseError.create(
          UseCaseError.REPOSITORY_ERROR,
          "Error al buscar el usuario",
          _error,
        ),
      );
    }
  }
}
