import {
  CurrencyCode,
  DomainError,
  LanguageCode,
  TextValue,
  UniqueID,
  UtcDate,
  ValidationErrorCollection,
  ValidationErrorDetail,
  extractOrPushError,
  maybeFromNullableVO,
} from "@repo/rdx-ddd";
import { Result, isNullishOrEmpty, toPatchField } from "@repo/rdx-utils";

import { UpdateCustomerInvoiceByIdRequestDTO } from "../../../../common/dto";
import { CustomerInvoicePatchProps, CustomerInvoiceSerie } from "../../../domain";

/**
 * mapDTOToUpdateCustomerInvoicePatchProps
 * Convierte el DTO a las props validadas (CustomerInvoiceProps).
 * No construye directamente el agregado.
 * Tri-estado:
 *  - campo omitido → no se cambia
 *  - campo con valor null/"" → se quita el valor -> set(None()),
 *  - campo con valor no-vacío → se pone el nuevo valor -> set(Some(VO)).
 *
 * @param dto - DTO con los datos a cambiar en la factura de cliente
 * @returns Cambios en las propiedades de la factura de cliente
 *
 */

export function mapDTOToUpdateCustomerInvoicePatchProps(dto: UpdateCustomerInvoiceByIdRequestDTO) {
  try {
    const errors: ValidationErrorDetail[] = [];
    const props: CustomerInvoicePatchProps = {};

    toPatchField(dto.series).ifSet((series) => {
      props.series = extractOrPushError(
        maybeFromNullableVO(series, (value) => CustomerInvoiceSerie.create(value)),
        "reference",
        errors
      );
    });

    toPatchField(dto.invoice_date).ifSet((invoice_date) => {
      if (isNullishOrEmpty(invoice_date)) {
        errors.push({ path: "invoice_date", message: "Invoice date cannot be empty" });
        return;
      }
      props.invoiceDate = extractOrPushError(
        UtcDate.createFromISO(invoice_date!),
        "invoice_date",
        errors
      );
    });

    toPatchField(dto.operation_date).ifSet((operation_date) => {
      props.operationDate = extractOrPushError(
        maybeFromNullableVO(operation_date, (value) => UtcDate.createFromISO(value)),
        "operation_date",
        errors
      );
    });

    toPatchField(dto.customer_id).ifSet((customer_id) => {
      if (isNullishOrEmpty(customer_id)) {
        errors.push({ path: "customer_id", message: "Customer cannot be empty" });
        return;
      }
      props.customerId = extractOrPushError(UniqueID.create(customer_id!), "customer_id", errors);
    });

    toPatchField(dto.reference).ifSet((reference) => {
      props.reference = extractOrPushError(
        maybeFromNullableVO(reference, (value) => Result.ok(String(value))),
        "reference",
        errors
      );
    });

    toPatchField(dto.description).ifSet((description) => {
      props.description = extractOrPushError(
        maybeFromNullableVO(description, (value) => Result.ok(String(value))),
        "description",
        errors
      );
    });

    toPatchField(dto.notes).ifSet((notes) => {
      props.notes = extractOrPushError(
        maybeFromNullableVO(notes, (value) => TextValue.create(value)),
        "notes",
        errors
      );
    });

    toPatchField(dto.language_code).ifSet((languageCode) => {
      if (isNullishOrEmpty(languageCode)) {
        errors.push({ path: "language_code", message: "Language code cannot be empty" });
        return;
      }

      props.languageCode = extractOrPushError(
        LanguageCode.create(languageCode!),
        "language_code",
        errors
      );
    });

    toPatchField(dto.currency_code).ifSet((currencyCode) => {
      if (isNullishOrEmpty(currencyCode)) {
        errors.push({ path: "currency_code", message: "Currency code cannot be empty" });
        return;
      }

      props.currencyCode = extractOrPushError(
        CurrencyCode.create(currencyCode!),
        "currency_code",
        errors
      );
    });

    if (errors.length > 0) {
      return Result.fail(
        new ValidationErrorCollection("Customer invoice props mapping failed (update)", errors)
      );
    }

    return Result.ok(props);
  } catch (err: unknown) {
    return Result.fail(new DomainError("Customer invoice props mapping failed", { cause: err }));
  }
}
