import { UniqueID } from "@/core/common/domain";
import { ITransactionManager } from "@/core/common/infrastructure/database";
import { Result } from "@repo/rdx-utils";
import { IInvoiceService, Invoice } from "../domain";
import { IUpdateInvoiceRequestDTO } from "../presentation/dto";

export class CreateInvoiceUseCase {
  constructor(
    private readonly invoiceService: IInvoiceService,
    private readonly transactionManager: ITransactionManager
  ) {}

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

        const data = validOrErrors.data;

        // Update invoice with dto
        return await this.invoiceService.updateInvoiceById(invoiceID, data, transaction);
      } catch (error: unknown) {
        logger.error(error as Error);
        return Result.fail(error as Error);
      }
 */
    });
  }

  /*   private validateInvoiceData(
    dto: Partial<IUpdateInvoiceRequestDTO>
  ): Result<Partial<IInvoiceProps>, Error> {
    const errors: Error[] = [];
    const validatedData: Partial<IInvoiceProps> = {};

    // Create invoice
    let invoice_status = InvoiceStatus.create(invoiceDTO.status).object;
    if (invoice_status.isEmpty()) {
      invoice_status = InvoiceStatus.createDraft();
    }

    let invoice_series = InvoiceSeries.create(invoiceDTO.invoice_series).object;
    if (invoice_series.isEmpty()) {
      invoice_series = InvoiceSeries.create(invoiceDTO.invoice_series).object;
    }

    let issue_date = InvoiceDate.create(invoiceDTO.issue_date).object;
    if (issue_date.isEmpty()) {
      issue_date = InvoiceDate.createCurrentDate().object;
    }

    let operation_date = InvoiceDate.create(invoiceDTO.operation_date).object;
    if (operation_date.isEmpty()) {
      operation_date = InvoiceDate.createCurrentDate().object;
    }

    let invoiceCurrency = Currency.createFromCode(invoiceDTO.currency).object;

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

    let invoiceLanguage = Language.createFromCode(invoiceDTO.language_code).object;

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

    const items = new Collection<InvoiceItem>(
      invoiceDTO.items?.map(
        (item) =>
          InvoiceSimpleItem.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 (!invoice_status.isDraft()) {
      throw Error("Error al crear una factura que no es borrador");
    }

    return DraftInvoice.create(
      {
        invoiceSeries: invoice_series,
        issueDate: issue_date,
        operationDate: operation_date,
        invoiceCurrency,
        language: invoiceLanguage,
        invoiceNumber: InvoiceNumber.create(undefined).object,
        //notes: Note.create(invoiceDTO.notes).object,

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

        items,
      },
      invoiceId
    );
  } */
}

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

export class UpdateInvoiceUseCase2
  implements
    IUseCase<{ id: UniqueID; data: IUpdateInvoice_DTO }, Promise<UpdateInvoiceResponseOrError>>
{
  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: IUpdateInvoice_DTO;
  }): Promise<UpdateInvoiceResponseOrError> {
    const { id, data: invoiceDTO } = request;

    // Validaciones
    const invoiceDTOOrError = ensureUpdateInvoice_DTOIsValid(invoiceDTO);
    if (invoiceDTOOrError.isFailure) {
      return this.handleValidationFailure(invoiceDTOOrError.error);
    }

    const transaction = this._adapter.startTransaction();

    const invoiceRepoBuilder = this.getRepository<IInvoiceRepository>("Invoice");

    let invoice: Invoice | null = null;

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

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

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

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

    const recipientBillingIdOrError = ensureParticipantAddressIdIsValid(
      invoiceDTO?.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(
      invoiceDTO?.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 invoice
    const invoiceOrError = await this.tryUpdateInvoiceInstance(
      invoiceDTO,
      invoiceIdOrError.object,
      //senderId,
      //senderBillingId,
      //senderShippingId,
      recipientContact,
    );

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

      switch (domainError.code) {
        case Invoice.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.saveInvoice(invoiceOrError.object);
    
  }

  private async tryUpdateInvoiceInstance(invoiceDTO, invoiceId, recipient) {
    // Create invoice
    let invoice_status = InvoiceStatus.create(invoiceDTO.status).object;
    if (invoice_status.isEmpty()) {
      invoice_status = InvoiceStatus.createDraft();
    }

    let invoice_series = InvoiceSeries.create(invoiceDTO.invoice_series).object;
    if (invoice_series.isEmpty()) {
      invoice_series = InvoiceSeries.create(invoiceDTO.invoice_series).object;
    }

    let issue_date = InvoiceDate.create(invoiceDTO.issue_date).object;
    if (issue_date.isEmpty()) {
      issue_date = InvoiceDate.createCurrentDate().object;
    }

    let operation_date = InvoiceDate.create(invoiceDTO.operation_date).object;
    if (operation_date.isEmpty()) {
      operation_date = InvoiceDate.createCurrentDate().object;
    }

    let invoiceCurrency = Currency.createFromCode(invoiceDTO.currency).object;

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

    let invoiceLanguage = Language.createFromCode(invoiceDTO.language_code).object;

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

    const items = new Collection<InvoiceItem>(
      invoiceDTO.items?.map(
        (item) =>
          InvoiceSimpleItem.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 (!invoice_status.isDraft()) {
      throw Error("Error al crear una factura que no es borrador");
    }

    return DraftInvoice.create(
      {
        invoiceSeries: invoice_series,
        issueDate: issue_date,
        operationDate: operation_date,
        invoiceCurrency,
        language: invoiceLanguage,
        invoiceNumber: InvoiceNumber.create(undefined).object,
        //notes: Note.create(invoiceDTO.notes).object,

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

        items,
      },
      invoiceId
    );
  }

  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 saveInvoice(invoice: DraftInvoice) {
    const transaction = this._adapter.startTransaction();
    const invoiceRepoBuilder = this.getRepository<IInvoiceRepository>("Invoice");

    try {
      await transaction.complete(async (t) => {
        const invoiceRepo = invoiceRepoBuilder({ transaction: t });
        await invoiceRepo.save(invoice);
      });

      return Result.ok<DraftInvoice>(invoice);
    } catch (error: unknown) {
      const _error = error as Error;
      if (invoiceRepoBuilder().isRepositoryError(_error)) {
        return this.handleRepositoryError(error as BaseError, invoiceRepoBuilder());
      } 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: IInvoiceRepository
  ): Result<never, IUseCaseError> {
    const { message, details } = repository.handleRepositoryError(error);
    return Result.fail<IUseCaseError>(
      UseCaseError.create(UseCaseError.REPOSITORY_ERROR, message, details)
    );
  }
}
 */
