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

export interface ISendQuoteUseCaseRequest extends IUseCaseRequest {
  id: UniqueID;
  sentQuoteDTO: ISendQuote_Request_DTO;
}

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

export class SendQuoteUseCase
  implements IUseCase<ISendQuoteUseCaseRequest, Promise<SendQuoteResponseOrError>>
{
  private _adapter: ISequelizeAdapter;
  private _repositoryManager: IRepositoryManager;

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

  async execute(request: ISendQuoteUseCaseRequest): Promise<SendQuoteResponseOrError> {
    const {
      id,
      sentQuoteDTO: { sent_date },
    } = request;

    const quoteRepository = this._getQuoteRepository();

    // Comprobar que existe el Quote
    const idExists = await quoteRepository().exists(id);
    if (!idExists) {
      const message = `Quote ID not found`;
      return Result.fail(
        UseCaseError.create(UseCaseError.NOT_FOUND_ERROR, message, {
          path: "id",
        })
      );
    }

    // //
    // regla de negocio -> no poder enviar un quote enviado
    // regla de negocio -> no poder enviar un quote que no esté aceptado

    // Comprobar el status
    const sentDateOrError = UTCDateValue.create(sent_date);
    if (sentDateOrError.isFailure) {
      const { error: domainError } = sentDateOrError;
      let errorCode = "";
      let message = "";

      switch (domainError.code) {
        // Errores manuales
        case DomainError.INVALID_INPUT_DATA:
          errorCode = UseCaseError.INVALID_INPUT_DATA;
          message = "La fecha de envío no es correcta";
          break;

        default:
          errorCode = UseCaseError.UNEXCEPTED_ERROR;
          message = domainError.message;
          break;
      }

      return Result.fail(UseCaseError.create(errorCode, message, domainError));
    }

    const transaction = this._adapter.startTransaction();
    try {
      await transaction.complete(async (t) => {
        const quoteRepo = quoteRepository({ transaction: t });
        await quoteRepo.updateSentDateById(id, sentDateOrError.object);
      });

      return Result.ok<void>();
    } catch (error: unknown) {
      //const _error = error as IInfrastructureError;
      return Result.fail(
        UseCaseError.create(
          UseCaseError.REPOSITORY_ERROR,
          "Error al establecer el nuevo estado en la cotización"
        )
      );
    }
  }

  private _getQuoteRepository() {
    return this._repositoryManager.getRepository<IQuoteRepository>("Quote");
  }
}
