import { UtcDate } from "@repo/rdx-ddd";
import { Maybe, Result } from "@repo/rdx-utils";
import { CustomerInvoice } from "../aggregates";
import { EntityIsNotProformaError, ProformaCannotBeConvertedToInvoiceError } from "../errors";
import {
  CustomerInvoiceIsProformaSpecification,
  ProformaCanTranstionToIssuedSpecification,
} from "../specs";
import { CustomerInvoiceNumber, CustomerInvoiceStatus } from "../value-objects";

/**
 * Servicio de dominio que encapsula la lógica de emisión de factura definitiva desde una proforma.
 */
export class IssueCustomerInvoiceDomainService {
  private readonly isProformaSpec = new CustomerInvoiceIsProformaSpecification();
  private readonly isApprovedSpec = new ProformaCanTranstionToIssuedSpecification();

  public linkWithProforma(
    invoice: CustomerInvoice,
    proforma: CustomerInvoice
  ): Result<CustomerInvoice, Error> {}

  /**
   * Convierte una proforma en factura definitiva.
   *
   * @param proforma - Entidad CustomerInvoice en estado proforma aprobada.
   * @param params.issueNumber - Número de la nueva factura.
   * @param params.issueDate - Fecha de emisión.
   * @returns Result<CustomerInvoice, Error> - Nueva factura emitida o error de dominio.
   */
  public async issueFromProforma(
    proforma: CustomerInvoice,
    params: {
      issueNumber: CustomerInvoiceNumber;
      issueDate: UtcDate;
    }
  ): Promise<Result<CustomerInvoice, Error>> {
    const { issueDate, issueNumber } = params;

    /** 1. Validar que la entidad origen es una proforma */
    if (!(await this.isProformaSpec.isSatisfiedBy(proforma))) {
      return Result.fail(new EntityIsNotProformaError(proforma.id.toString()));
    }

    /** 2. Validar que la proforma puede emitirse */
    if (!(await this.isApprovedSpec.isSatisfiedBy(proforma))) {
      return Result.fail(new ProformaCannotBeConvertedToInvoiceError(proforma.id.toString()));
    }

    /** 3. Generar la nueva factura definitiva (inmutable) */
    const proformaProps = proforma.getProps();
    const newInvoiceOrError = CustomerInvoice.create({
      ...proformaProps,
      isProforma: false,
      proformaId: Maybe.some(proforma.id),
      status: CustomerInvoiceStatus.createIssued(),
      invoiceNumber: issueNumber,
      invoiceDate: issueDate,
      description: proformaProps.description.isNone() ? Maybe.some(".") : proformaProps.description,
    });

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

    return Result.ok(newInvoiceOrError.data);
  }
}
