import {
  IUseCase,
  IUseCaseError,
  IUseCaseRequest,
  UseCaseError,
} from "@/contexts/common/application";
import {
  DomainError,
  IDomainError,
  ISendIncidence_Request_DTO,
  Result,
  TextValueObject,
  UTCDateValue,
  UniqueID,
} from "@shared/contexts";

import { IInfrastructureError } from "@/contexts/common/infrastructure";
import { Dealer } from "@/contexts/sales/domain";
import { Incidence } from "../domain";
import { ISupportContext } from "../infrastructure";

export interface ISendIncidenceUseCaseRequest extends IUseCaseRequest {
  incidenceDTO: ISendIncidence_Request_DTO;
}

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

export class SendIncidenceUseCase
  implements IUseCase<ISendIncidenceUseCaseRequest, Promise<SendIncidenceResponseOrError>>
{
  private _context: ISupportContext;

  constructor(context: ISupportContext) {
    this._context = context;
  }

  async execute(request: ISendIncidenceUseCaseRequest): Promise<SendIncidenceResponseOrError> {
    const { incidenceDTO } = request;
    //const QuoteRepository = this._getQuoteRepository();

    // Validaciones de datos
    if (!this._context.user) {
      const message = "Error. Missing User";
      return Result.fail(UseCaseError.create(UseCaseError.INVALID_INPUT_DATA, message));
    }

    // Validaciones de datos
    if (!this._context.dealer) {
      const message = "Error. Missing Dealer";
      return Result.fail(UseCaseError.create(UseCaseError.INVALID_INPUT_DATA, message));
    }

    const dealer = this._context.dealer;
    const user = this._context.user;

    // Send incidence
    const incidenceOrError = this._tryIncidenceInstance(incidenceDTO, dealer);

    if (incidenceOrError.isFailure) {
      const { error: domainError } = incidenceOrError;
      let errorCode = "";
      let message = "";

      switch (domainError.code) {
        // Errores manuales
        case DomainError.INVALID_INPUT_DATA:
          errorCode = UseCaseError.INVALID_INPUT_DATA;
          message = "The issue has some erroneous data.";
          break;

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

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

    const incidence = incidenceOrError.object;

    try {
      const sendInfo = await this._context.services?.emailService.sendMail({
        from: this._context.defaults.support.from,
        to: this._context.defaults.support.to,
        subject: this._context.defaults.support.subject,
        html: `<p>Distribuidor: ${dealer.name.toString()}</p>
        <p>Email: ${user.email.toString()}</p>
        <br>
        <p>${incidence.description.toString()}</p>`,
      });

      return Result.ok<void>();
    } catch (error: unknown) {
      const _error = error as IInfrastructureError;
      return Result.fail(UseCaseError.create(UseCaseError.UNEXCEPTED_ERROR, _error.message));
    }
  }

  private _tryIncidenceInstance(
    incidenceDTO: ISendIncidence_Request_DTO,
    dealer: Dealer
  ): Result<Incidence, IDomainError> {
    const dateOrError = UTCDateValue.createCurrentDate();
    if (dateOrError.isFailure) {
      return Result.fail(dateOrError.error);
    }

    const descriptionOrError = TextValueObject.create(incidenceDTO.incidence);
    if (descriptionOrError.isFailure) {
      return Result.fail(descriptionOrError.error);
    }

    return Incidence.create(
      {
        date: dateOrError.object,
        description: descriptionOrError.object,
        dealerId: dealer.id,
      },
      UniqueID.generateNewID().object
    );
  }
}
