import { UniqueID } from "@/core/common/domain";
import { ITransactionManager } from "@/core/common/infrastructure/database";
import { Result } from "@repo/rdx-utils";
import { IUpdateCustomerInvoiceRequestDTO } from "../../common/dto";
import { CustomerInvoice, ICustomerInvoiceService } from "../domain";

export class CreateCustomerInvoiceUseCase {
  constructor(
    private readonly customerInvoiceService: ICustomerInvoiceService,
    private readonly transactionManager: ITransactionManager
  ) {}

  public execute(
    customerInvoiceID: UniqueID,
    dto: Partial<IUpdateCustomerInvoiceRequestDTO>
  ): Promise<Result<CustomerInvoice, Error>> {
    return this.transactionManager.complete(async (transaction) => {
      return Result.fail(new Error("No implementado"));
      /* 
      try {
        const validOrErrors = this.validateCustomerInvoiceData(dto);
        if (validOrErrors.isFailure) {
          return Result.fail(validOrErrors.error);
        }

        const data = validOrErrors.data;

        // Update customerInvoice with dto
        return await this.customerInvoiceService.updateCustomerInvoiceById(customerInvoiceID, data, transaction);
      } catch (error: unknown) {
        logger.error(error as Error);
        return Result.fail(error as Error);
      }
 */
    });
  }

  /*   private validateCustomerInvoiceData(
    dto: Partial<IUpdateCustomerInvoiceRequestDTO>
  ): Result<Partial<ICustomerInvoiceProps>, Error> {
    const errors: Error[] = [];
    const validatedData: Partial<ICustomerInvoiceProps> = {};

    // Create customerInvoice
    let customerInvoice_status = CustomerInvoiceStatus.create(customerInvoiceDTO.status).object;
    if (customerInvoice_status.isEmpty()) {
      customerInvoice_status = CustomerInvoiceStatus.createDraft();
    }

    let customerInvoice_series = CustomerInvoiceSeries.create(customerInvoiceDTO.customerInvoice_series).object;
    if (customerInvoice_series.isEmpty()) {
      customerInvoice_series = CustomerInvoiceSeries.create(customerInvoiceDTO.customerInvoice_series).object;
    }

    let invoice_date = CustomerInvoiceDate.create(customerInvoiceDTO.invoice_date).object;
    if (invoice_date.isEmpty()) {
      invoice_date = CustomerInvoiceDate.createCurrentDate().object;
    }

    let operation_date = CustomerInvoiceDate.create(customerInvoiceDTO.operation_date).object;
    if (operation_date.isEmpty()) {
      operation_date = CustomerInvoiceDate.createCurrentDate().object;
    }

    let customerInvoiceCurrency = Currency.createFromCode(customerInvoiceDTO.currency).object;

    if (customerInvoiceCurrency.isEmpty()) {
      customerInvoiceCurrency = Currency.createDefaultCode().object;
    }

    let customerInvoiceLanguage = Language.createFromCode(customerInvoiceDTO.language_code).object;

    if (customerInvoiceLanguage.isEmpty()) {
      customerInvoiceLanguage = Language.createDefaultCode().object;
    }

    const items = new Collection<CustomerInvoiceItem>(
      customerInvoiceDTO.items?.map(
        (item) =>
          CustomerInvoiceSimpleItem.create({
            description: Description.create(item.description).object,
            quantity: Quantity.create(item.quantity).object,
            unitPrice: UnitPrice.create({
              amount: item.unit_price.amount,
              currencyCode: item.unit_price.currency,
              precision: item.unit_price.precision,
            }).object,
          }).object
      )
    );

    if (!customerInvoice_status.isDraft()) {
      throw Error("Error al crear una factura que no es borrador");
    }

    return DraftCustomerInvoice.create(
      {
        customerInvoiceSeries: customerInvoice_series,
        invoiceDate: invoice_date,
        operationDate: operation_date,
        customerInvoiceCurrency,
        language: customerInvoiceLanguage,
        customerInvoiceNumber: CustomerInvoiceNumber.create(undefined).object,
        //notes: Note.create(customerInvoiceDTO.notes).object,

        //senderId: UniqueID.create(null).object,
        recipient,

        items,
      },
      customerInvoiceId
    );
  } */
}

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

export class UpdateCustomerInvoiceUseCase2
  implements
    IUseCase<{ id: UniqueID; data: IUpdateCustomerInvoice_DTO }, Promise<UpdateCustomerInvoiceResponseOrError>>
{
  private _context: IInvoicingContext;
  private _adapter: ISequelizeAdapter;
  private _repositoryManager: IRepositoryManager;

  constructor(context: IInvoicingContext) {
    this._context = context;
    this._adapter = context.adapter;
    this._repositoryManager = context.repositoryManager;
  }

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

  private handleValidationFailure(
    validationError: Error,
    message?: string
  ): Result<never, IUseCaseError> {
    return Result.fail<IUseCaseError>(
      UseCaseError.create(
        UseCaseError.INVALID_INPUT_DATA,
        message ? message : validationError.message,
        validationError
      )
    );
  }

  async execute(request: {
    id: UniqueID;
    data: IUpdateCustomerInvoice_DTO;
  }): Promise<UpdateCustomerInvoiceResponseOrError> {
    const { id, data: customerInvoiceDTO } = request;

    // Validaciones
    const customerInvoiceDTOOrError = ensureUpdateCustomerInvoice_DTOIsValid(customerInvoiceDTO);
    if (customerInvoiceDTOOrError.isFailure) {
      return this.handleValidationFailure(customerInvoiceDTOOrError.error);
    }

    const transaction = this._adapter.startTransaction();

    const customerInvoiceRepoBuilder = this.getRepository<ICustomerInvoiceRepository>("CustomerInvoice");

    let customerInvoice: CustomerInvoice | null = null;

    try {
      await transaction.complete(async (t) => {
        customerInvoice = await customerInvoiceRepoBuilder({ transaction: t }).getById(id);
      });

      if (customerInvoice === null) {
        return Result.fail<IUseCaseError>(
          UseCaseError.create(UseCaseError.NOT_FOUND_ERROR, `CustomerInvoice not found`, {
            id: request.id.toString(),
            entity: "customerInvoice",
          })
        );
      }

      return Result.ok<CustomerInvoice>(customerInvoice);
    } catch (error: unknown) {
      const _error = error as Error;
      if (customerInvoiceRepoBuilder().isRepositoryError(_error)) {
        return this.handleRepositoryError(error as BaseError, customerInvoiceRepoBuilder());
      } else {
        return this.handleUnexceptedError(error);
      }
    }

    // Recipient validations
    const recipientIdOrError = ensureParticipantIdIsValid(
      customerInvoiceDTO?.recipient?.id,
    );
    if (recipientIdOrError.isFailure) {
      return this.handleValidationFailure(
        recipientIdOrError.error,
        "Recipient ID not valid",
      );
    }
    const recipientId = recipientIdOrError.object;

    const recipientBillingIdOrError = ensureParticipantAddressIdIsValid(
      customerInvoiceDTO?.recipient?.billing_address_id,
    );
    if (recipientBillingIdOrError.isFailure) {
      return this.handleValidationFailure(
        recipientBillingIdOrError.error,
        "Recipient billing address ID not valid",
      );
    }
    const recipientBillingId = recipientBillingIdOrError.object;

    const recipientShippingIdOrError = ensureParticipantAddressIdIsValid(
      customerInvoiceDTO?.recipient?.shipping_address_id,
    );
    if (recipientShippingIdOrError.isFailure) {
      return this.handleValidationFailure(
        recipientShippingIdOrError.error,
        "Recipient shipping address ID not valid",
      );
    }
    const recipientShippingId = recipientShippingIdOrError.object;

    const recipientContact = await this.findContact(
      recipientId,
      recipientBillingId,
      recipientShippingId,
    );

    if (!recipientContact) {
      return this.handleValidationFailure(
        new Error(`Recipient with ID ${recipientId.toString()} does not exist`),
      );
    }

    // Crear customerInvoice
    const customerInvoiceOrError = await this.tryUpdateCustomerInvoiceInstance(
      customerInvoiceDTO,
      customerInvoiceIdOrError.object,
      //senderId,
      //senderBillingId,
      //senderShippingId,
      recipientContact,
    );

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

      switch (domainError.code) {
        case CustomerInvoice.ERROR_CUSTOMER_WITHOUT_NAME:
          errorCode = UseCaseError.INVALID_INPUT_DATA;
          message =
            "El cliente debe ser una compañía o tener nombre y apellidos.";
          break;

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

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

    return this.saveCustomerInvoice(customerInvoiceOrError.object);
    
  }

  private async tryUpdateCustomerInvoiceInstance(customerInvoiceDTO, customerInvoiceId, recipient) {
    // Create customerInvoice
    let customerInvoice_status = CustomerInvoiceStatus.create(customerInvoiceDTO.status).object;
    if (customerInvoice_status.isEmpty()) {
      customerInvoice_status = CustomerInvoiceStatus.createDraft();
    }

    let customerInvoice_series = CustomerInvoiceSeries.create(customerInvoiceDTO.customerInvoice_series).object;
    if (customerInvoice_series.isEmpty()) {
      customerInvoice_series = CustomerInvoiceSeries.create(customerInvoiceDTO.customerInvoice_series).object;
    }

    let invoice_date = CustomerInvoiceDate.create(customerInvoiceDTO.invoice_date).object;
    if (invoice_date.isEmpty()) {
      invoice_date = CustomerInvoiceDate.createCurrentDate().object;
    }

    let operation_date = CustomerInvoiceDate.create(customerInvoiceDTO.operation_date).object;
    if (operation_date.isEmpty()) {
      operation_date = CustomerInvoiceDate.createCurrentDate().object;
    }

    let customerInvoiceCurrency = Currency.createFromCode(customerInvoiceDTO.currency).object;

    if (customerInvoiceCurrency.isEmpty()) {
      customerInvoiceCurrency = Currency.createDefaultCode().object;
    }

    let customerInvoiceLanguage = Language.createFromCode(customerInvoiceDTO.language_code).object;

    if (customerInvoiceLanguage.isEmpty()) {
      customerInvoiceLanguage = Language.createDefaultCode().object;
    }

    const items = new Collection<CustomerInvoiceItem>(
      customerInvoiceDTO.items?.map(
        (item) =>
          CustomerInvoiceSimpleItem.create({
            description: Description.create(item.description).object,
            quantity: Quantity.create(item.quantity).object,
            unitPrice: UnitPrice.create({
              amount: item.unit_price.amount,
              currencyCode: item.unit_price.currency,
              precision: item.unit_price.precision,
            }).object,
          }).object
      )
    );

    if (!customerInvoice_status.isDraft()) {
      throw Error("Error al crear una factura que no es borrador");
    }

    return DraftCustomerInvoice.create(
      {
        customerInvoiceSeries: customerInvoice_series,
        invoiceDate: invoice_date,
        operationDate: operation_date,
        customerInvoiceCurrency,
        language: customerInvoiceLanguage,
        customerInvoiceNumber: CustomerInvoiceNumber.create(undefined).object,
        //notes: Note.create(customerInvoiceDTO.notes).object,

        //senderId: UniqueID.create(null).object,
        recipient,

        items,
      },
      customerInvoiceId
    );
  }

  private async findContact(
    contactId: UniqueID,
    billingAddressId: UniqueID,
    shippingAddressId: UniqueID
  ) {
    const contactRepoBuilder = this.getRepository<IContactRepository>("Contact");

    const contact = await contactRepoBuilder().getById2(
      contactId,
      billingAddressId,
      shippingAddressId
    );

    return contact;
  }

  private async saveCustomerInvoice(customerInvoice: DraftCustomerInvoice) {
    const transaction = this._adapter.startTransaction();
    const customerInvoiceRepoBuilder = this.getRepository<ICustomerInvoiceRepository>("CustomerInvoice");

    try {
      await transaction.complete(async (t) => {
        const customerInvoiceRepo = customerInvoiceRepoBuilder({ transaction: t });
        await customerInvoiceRepo.save(customerInvoice);
      });

      return Result.ok<DraftCustomerInvoice>(customerInvoice);
    } catch (error: unknown) {
      const _error = error as Error;
      if (customerInvoiceRepoBuilder().isRepositoryError(_error)) {
        return this.handleRepositoryError(error as BaseError, customerInvoiceRepoBuilder());
      } else {
        return this.handleUnexceptedError(error);
      }
    }
  }

  private handleUnexceptedError(error): Result<never, IUseCaseError> {
    return Result.fail<IUseCaseError>(
      UseCaseError.create(UseCaseError.UNEXCEPTED_ERROR, error.message, error)
    );
  }

  private handleRepositoryError(
    error: BaseError,
    repository: ICustomerInvoiceRepository
  ): Result<never, IUseCaseError> {
    const { message, details } = repository.handleRepositoryError(error);
    return Result.fail<IUseCaseError>(
      UseCaseError.create(UseCaseError.REPOSITORY_ERROR, message, details)
    );
  }
}
 */
