import { Result } from "@repo/rdx-utils";
import { CustomerInvoice } from "../aggregates";
import { EntityIsNotProformaError, InvalidProformaTransitionError } from "../errors";
import { CustomerInvoiceIsProformaSpecification } from "../specs";
import { CustomerInvoiceStatus, INVOICE_STATUS } from "../value-objects";

/**
 * Servicio de dominio que encapsula la lógica de emisión de factura definitiva desde una proforma.
 */
export class ProformaCustomerInvoiceDomainService {
  /** Aplica la transición si está permitida según INVOICE_TRANSITIONS. */
  async transition(
    proforma: CustomerInvoice,
    nextStatus: string
  ): Promise<Result<CustomerInvoice, Error>> {
    // Validar que la entidad es una proforma
    const isProformaSpec = new CustomerInvoiceIsProformaSpecification();
    if (!(await isProformaSpec.isSatisfiedBy(proforma))) {
      return Result.fail(new EntityIsNotProformaError(proforma.id.toString()));
    }

    const current = proforma.status.toString();
    const allowed = proforma.canTransitionTo(nextStatus);

    if (!allowed) {
      return Result.fail(
        new InvalidProformaTransitionError(current, nextStatus, proforma.id.toString())
      );
    }

    // Validaciones adicionales de dominio, si las hubiera
    // (por ejemplo, no aprobar si no hay líneas)
    // new ProformaHasLinesSpecification().isSatisfiedBy(proforma)

    return CustomerInvoice.create({
      ...proforma.getProps(),
      status: CustomerInvoiceStatus.create(nextStatus).data,
    });
  }

  /** Envía la proforma (draft → sent) */
  async send(proforma: CustomerInvoice): Promise<Result<CustomerInvoice, Error>> {
    return this.transition(proforma, INVOICE_STATUS.SENT);
  }

  /** Aprueba la proforma (sent → approved) */
  async approve(proforma: CustomerInvoice): Promise<Result<CustomerInvoice, Error>> {
    return this.transition(proforma, INVOICE_STATUS.APPROVED);
  }

  /** Rechaza la proforma (sent → rejected) */
  async reject(proforma: CustomerInvoice): Promise<Result<CustomerInvoice, Error>> {
    return this.transition(proforma, INVOICE_STATUS.REJECTED);
  }

  /** Reabre una proforma rechazada (rejected → draft) */
  async reopen(proforma: CustomerInvoice): Promise<Result<CustomerInvoice, Error>> {
    return this.transition(proforma, INVOICE_STATUS.DRAFT);
  }

  /** Marca la proforma como emitida (approved → issued) */
  async markAsIssued(proforma: CustomerInvoice): Promise<Result<CustomerInvoice, Error>> {
    return this.transition(proforma, INVOICE_STATUS.ISSUED);
  }
}
