import {
  IUseCase,
  IUseCaseError,
  IUseCaseRequest,
  UseCaseError,
} from "@/contexts/common/application/useCases";
import { IRepositoryManager } from "@/contexts/common/domain";
import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
import { Result, UniqueID } from "@shared/contexts";
import { IUserRepository } from "../domain";

import { IInfrastructureError } from "@/contexts/common/infrastructure";
import { User } from "../domain/entities/User";

export interface IGetUserUseCaseRequest extends IUseCaseRequest {
  id: UniqueID;
}

export type GetUserResponseOrError =
  | Result<never, IUseCaseError> // Misc errors (value objects)
  | Result<User, never>; // Success!

export class GetUserUseCase
  implements IUseCase<IGetUserUseCaseRequest, Promise<GetUserResponseOrError>>
{
  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: IGetUserUseCaseRequest,
  ): Promise<GetUserResponseOrError> {
    const { id } = request;

    // Validación de datos
    // No hay en este caso

    return await this.findUser(id);
  }

  private async findUser(id: UniqueID) {
    const transaction = this._adapter.startTransaction();
    const userRepoBuilder = this.getRepositoryByName<IUserRepository>("User");

    let user: User | null = null;

    try {
      await transaction.complete(async (t) => {
        const userRepo = userRepoBuilder({ transaction: t });
        user = await userRepo.getById(id);
      });

      if (!user) {
        return Result.fail(
          UseCaseError.create(UseCaseError.NOT_FOUND_ERROR, "User not found"),
        );
      }

      return Result.ok<User>(user!);
    } catch (error: unknown) {
      const _error = error as IInfrastructureError;
      return Result.fail(
        UseCaseError.create(
          UseCaseError.REPOSITORY_ERROR,
          "Error al consultar el usuario",
          _error,
        ),
      );
    }
  }
}
