import {
  City,
  Country,
  Name,
  PostalCode,
  Province,
  Street,
  TINNumber,
  ValidationErrorCollection,
  ValidationErrorDetail,
  extractOrPushError,
  maybeFromNullableVO,
} from "@repo/rdx-ddd";

import { MapperParamsType } from "@erp/core/api";
import { Maybe, Result } from "@repo/rdx-utils";
import { CustomerInvoiceProps, InvoiceRecipient } from "../../../domain";
import { CustomerInvoiceModel } from "../../sequelize";

export class InvoiceRecipientDomainMapper {
  public mapToDomain(
    source: CustomerInvoiceModel,
    params?: MapperParamsType
  ): Result<Maybe<InvoiceRecipient>, Error> {
    /**
     * - Factura === proforma -> datos de "current_customer"
     * - Factura !== proforma -> snapshot de los datos (campos customer_*)
     */

    const { errors, attributes } = params as {
      errors: ValidationErrorDetail[];
      attributes: Partial<CustomerInvoiceProps>;
    };

    const { isProforma } = attributes;

    if (isProforma && !source.current_customer) {
      errors.push({
        path: "current_customer",
        message: "Current customer not included in query (InvoiceRecipientMapper)",
      });
    }

    const _name = isProforma ? source.current_customer.name : source.customer_name!;
    const _tin = isProforma ? source.current_customer.tin : source.customer_tin!;
    const _street = isProforma ? source.current_customer.street : source.customer_street!;
    const _street2 = isProforma ? source.current_customer.street2 : source.customer_street2!;
    const _city = isProforma ? source.current_customer.city : source.customer_city!;
    const _postal_code = isProforma
      ? source.current_customer.postal_code
      : source.customer_postal_code!;
    const _province = isProforma ? source.current_customer.province : source.customer_province!;
    const _country = isProforma ? source.current_customer.country : source.customer_country!;

    // Customer (snapshot)
    const customerName = extractOrPushError(Name.create(_name!), "customer_name", errors);

    const customerTin = extractOrPushError(TINNumber.create(_tin!), "customer_tin", errors);

    const customerStreet = extractOrPushError(
      maybeFromNullableVO(_street, (value) => Street.create(value)),
      "customer_street",
      errors
    );

    const customerStreet2 = extractOrPushError(
      maybeFromNullableVO(_street2, (value) => Street.create(value)),
      "customer_street2",
      errors
    );

    const customerCity = extractOrPushError(
      maybeFromNullableVO(_city, (value) => City.create(value)),
      "customer_city",
      errors
    );

    const customerProvince = extractOrPushError(
      maybeFromNullableVO(_province, (value) => Province.create(value)),
      "customer_province",
      errors
    );

    const customerPostalCode = extractOrPushError(
      maybeFromNullableVO(_postal_code, (value) => PostalCode.create(value)),
      "customer_postal_code",
      errors
    );

    const customerCountry = extractOrPushError(
      maybeFromNullableVO(_country, (value) => Country.create(value)),
      "customer_country",
      errors
    );

    const createResult = InvoiceRecipient.create({
      name: customerName!,
      tin: customerTin!,
      street: customerStreet!,
      street2: customerStreet2!,
      city: customerCity!,
      postalCode: customerPostalCode!,
      province: customerProvince!,
      country: customerCountry!,
    });

    if (createResult.isFailure) {
      return Result.fail(
        new ValidationErrorCollection("Invoice recipient entity creation failed", [
          { path: "recipient", message: createResult.error.message },
        ])
      );
    }

    return Result.ok(Maybe.some(createResult.data));
  }
}
