import { IPresenterRegistry, ITransactionManager } from "@erp/core/api";
import { UniqueID } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import { Transaction } from "sequelize";
import { UpdateCustomerInvoiceByIdRequestDTO } from "../../../../common";
import { CustomerInvoicePatchProps } from "../../../domain";
import { CustomerInvoiceApplicationService } from "../../customer-invoice-application.service";
import { CustomerInvoiceFullPresenter } from "../../presenters";
import { mapDTOToUpdateCustomerInvoicePatchProps } from "./map-dto-to-update-customer-invoice-props";

type UpdateCustomerInvoiceUseCaseInput = {
  companyId: UniqueID;
  invoice_id: string;
  dto: UpdateCustomerInvoiceByIdRequestDTO;
};

export class UpdateCustomerInvoiceUseCase {
  constructor(
    private readonly service: CustomerInvoiceApplicationService,
    private readonly transactionManager: ITransactionManager,
    private readonly presenterRegistry: IPresenterRegistry
  ) {}

  public execute(params: UpdateCustomerInvoiceUseCaseInput) {
    const { companyId, invoice_id, dto } = params;

    const idOrError = UniqueID.create(invoice_id);
    if (idOrError.isFailure) {
      return Result.fail(idOrError.error);
    }

    const invoiceId = idOrError.data;
    const presenter = this.presenterRegistry.getPresenter({
      resource: "customer-invoice",
      projection: "FULL",
    }) as CustomerInvoiceFullPresenter;

    // Mapear DTO → props de dominio
    const patchPropsResult = mapDTOToUpdateCustomerInvoicePatchProps(dto);
    if (patchPropsResult.isFailure) {
      return Result.fail(patchPropsResult.error);
    }

    const patchProps: CustomerInvoicePatchProps = patchPropsResult.data;

    return this.transactionManager.complete(async (transaction: Transaction) => {
      try {
        const updatedInvoice = await this.service.patchInvoiceByIdInCompany(
          companyId,
          invoiceId,
          patchProps,
          transaction
        );

        if (updatedInvoice.isFailure) {
          return Result.fail(updatedInvoice.error);
        }

        const invoiceOrError = await this.service.updateInvoiceInCompany(
          companyId,
          updatedInvoice.data,
          transaction
        );
        const invoice = invoiceOrError.data;
        const dto = presenter.toOutput(invoice);
        return Result.ok(dto);
      } catch (error: unknown) {
        return Result.fail(error as Error);
      }
    });
  }
}
