import { IUseCaseError, UseCaseError } from "@/contexts/common/application";
import { IServerError } from "@/contexts/common/domain/errors";
import {
  IInfrastructureError,
  InfrastructureError,
} from "@/contexts/common/infrastructure";
import { ExpressController } from "@/contexts/common/infrastructure/express";
import { CreateUserUseCase } from "@/contexts/users/application";
import { User } from "@/contexts/users/domain";
import {
  ICreateUser_Request_DTO,
  ICreateUser_Response_DTO,
  ensureCreateUser_Request_DTOIsValid,
} from "@shared/contexts";
import { IUserContext } from "../../../User.context";
import { ICreateUserPresenter } from "./presenter/CreateUser.presenter";

export class CreateUserController extends ExpressController {
  private useCase: CreateUserUseCase;
  private presenter: ICreateUserPresenter;
  private context: IUserContext;

  constructor(
    props: {
      useCase: CreateUserUseCase;
      presenter: ICreateUserPresenter;
    },
    context: IUserContext,
  ) {
    super();

    const { useCase, presenter } = props;
    this.useCase = useCase;
    this.presenter = presenter;
    this.context = context;
  }

  async executeImpl() {
    try {
      const userDTO: ICreateUser_Request_DTO = this.req.body;

      // Validaciones de DTO
      const userDTOOrError = ensureCreateUser_Request_DTOIsValid(userDTO);

      if (userDTOOrError.isFailure) {
        const errorMessage = "User data not valid";
        const infraError = InfrastructureError.create(
          InfrastructureError.INVALID_INPUT_DATA,
          errorMessage,
          userDTOOrError.error,
        );
        return this.invalidInputError(errorMessage, infraError);
      }

      // Llamar al caso de uso
      const result = await this.useCase.execute(userDTO);

      if (result.isFailure) {
        return this._handleExecuteError(result.error);
      }

      const user = <User>result.object;

      return this.created<ICreateUser_Response_DTO>(
        this.presenter.map(user, this.context),
      );
    } catch (e: unknown) {
      return this.fail(e as IServerError);
    }
  }

  private _handleExecuteError(error: IUseCaseError) {
    let errorMessage: string;
    let infraError: IInfrastructureError;

    switch (error.code) {
      case UseCaseError.INVALID_INPUT_DATA:
        errorMessage = "User data not valid";
        infraError = InfrastructureError.create(
          InfrastructureError.INVALID_INPUT_DATA,
          errorMessage,
          error,
        );
        return this.invalidInputError(errorMessage, infraError);
        break;

      case UseCaseError.RESOURCE_ALREADY_EXITS:
        errorMessage = "User already exists";

        infraError = InfrastructureError.create(
          InfrastructureError.RESOURCE_ALREADY_REGISTERED,
          errorMessage,
          error,
        );
        return this.conflictError(errorMessage, infraError);
        break;

      case UseCaseError.REPOSITORY_ERROR:
        errorMessage = "Error saving user";
        infraError = InfrastructureError.create(
          InfrastructureError.UNEXCEPTED_ERROR,
          errorMessage,
          error,
        );
        return this.conflictError(errorMessage, infraError);
        break;

      case UseCaseError.UNEXCEPTED_ERROR:
        errorMessage = error.message;

        infraError = InfrastructureError.create(
          InfrastructureError.UNEXCEPTED_ERROR,
          errorMessage,
          error,
        );
        return this.internalServerError(errorMessage, infraError);
        break;

      default:
        errorMessage = error.message;
        return this.clientError(errorMessage);
    }
  }
}
