import { Customer, CustomerInvoice } from "@/contexts/customer-billing/domain";
import { InvoiceStatus } from "@/contexts/customer-billing/domain/value-objetcs";
import { PostalAddress, TINNumber, UniqueID, UtcDate } from "@/core/common/domain";
import {
  type ISequelizeMapper,
  type MapperParamsType,
  SequelizeMapper,
} from "@/core/common/infrastructure/sequelize/sequelize-mapper";
import { Maybe, Result } from "@repo/rdx-utils";
import {
  CustomerInvoiceCreationAttributes,
  CustomerInvoiceModel,
} from "../sequelize/customer-invoice.model";

export interface ICustomerInvoiceMapper
  extends ISequelizeMapper<
    CustomerInvoiceModel,
    CustomerInvoiceCreationAttributes,
    CustomerInvoice
  > {}

export class CustomerInvoiceMapper
  extends SequelizeMapper<CustomerInvoiceModel, CustomerInvoiceCreationAttributes, CustomerInvoice>
  implements ICustomerInvoiceMapper
{
  public mapToDomain(
    source: CustomerInvoiceModel,
    params?: MapperParamsType
  ): Result<CustomerInvoice, Error> {
    const idOrError = UniqueID.create(source.id);
    const statusOrError = InvoiceStatus.create(source.status);
    const issueDateOrError = UtcDate.create(source.issue_date);

    const result = Result.combine([idOrError, statusOrError, issueDateOrError]);

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

    // Customer
    const customerIdOrError = UniqueID.create(source.customer_id);
    const tinOrError = TINNumber.create(source.customer_tin);
    const postalAddressOrError = PostalAddress.create({
      street: source.customer_street,
      street2: source.customer_street2,
      city: source.customer_city,
      state: source.customer_state,
      postalCode: source.customer_postal_code,
      country: source.customer_country,
    });

    const check2 = Result.combine([idOrError, tinOrError, postalAddressOrError]);

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

    const customerOrError = Customer.create(
      {
        name: source.customer_name,
        tin: tinOrError.data,
        address: postalAddressOrError.data,
      },
      customerIdOrError.data
    );

    return CustomerInvoice.create(
      {
        status: statusOrError.data,
        issueDate: issueDateOrError.data,
        invoiceNumber: source.invoice_number,
        invoiceType: source.invoice_type,
        invoiceCustomerReference: Maybe.fromNullable(source.invoice_customer_reference),
        customer: customerOrError.data,
        items: [],
      },
      idOrError.data
    );
  }

  public mapToPersistence(
    source: CustomerInvoice,
    params?: MapperParamsType
  ): Result<CustomerInvoiceCreationAttributes, Error> {
    return Result.ok({
      id: source.id.toString(),
      status: source.status.toString(),

      issue_date: source.issueDate.toDateString(),
      invoice_number: source.invoiceNumber,
      invoice_type: source.invoiceType,
      invoice_customer_reference: source.invoiceCustomerReference.getOrUndefined(),

      lang_code: "es",
      currency_code: "EUR",

      customer_id: source.customer.id.toString(),
      customer_name: source.customer.name,
      customer_tin: source.customer.tin.toString(),
      customer_street: source.customer.address.street,
      customer_street2: source.customer.address.street2.getOrUndefined(),
      customer_city: source.customer.address.city,
      customer_postal_code: source.customer.address.postalCode,
      customer_state: source.customer.address.state,
      customer_country: source.customer.address.country,
    });
  }
}

const customerInvoiceMapper: CustomerInvoiceMapper = new CustomerInvoiceMapper();
export { customerInvoiceMapper };
