import { Invoice, InvoiceNumber, InvoiceSerie, InvoiceStatus } from "@contexts/invoices/domain/";
import { UniqueID, UtcDate } from "core/common/domain";
import { Result } from "core/common/helpers";
import {
  ISequelizeMapper,
  MapperParamsType,
  SequelizeMapper,
} from "core/common/infrastructure/sequelize/sequelize-mapper";
import { InvoiceCreationAttributes, InvoiceModel } from "../sequelize";
import { InvoiceItemMapper } from "./invoice-item.mapper"; // Importar el mapper de items

export interface IInvoiceMapper
  extends ISequelizeMapper<InvoiceModel, InvoiceCreationAttributes, Invoice> {}

export class InvoiceMapper
  extends SequelizeMapper<InvoiceModel, InvoiceCreationAttributes, Invoice>
  implements IInvoiceMapper
{
  private invoiceItemMapper: InvoiceItemMapper;

  constructor() {
    super();
    this.invoiceItemMapper = new InvoiceItemMapper(); // Instanciar el mapper de items
  }

  public mapToDomain(source: InvoiceModel, params?: MapperParamsType): Result<Invoice, Error> {
    const idOrError = UniqueID.create(source.id);
    const statusOrError = InvoiceStatus.create(source.invoice_status);
    const invoiceSeriesOrError = InvoiceSerie.create(source.invoice_series);
    const invoiceNumberOrError = InvoiceNumber.create(source.invoice_number);
    const issueDateOrError = UtcDate.create(source.issue_date);
    const operationDateOrError = UtcDate.create(source.operation_date);

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

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

    // Mapear los items de la factura
    const itemsOrErrors = this.invoiceItemMapper.mapArrayToDomain(source.items, {
      sourceParent: source,
      ...params,
    });

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

    const invoiceCurrency = source.invoice_currency || "EUR";

    return Invoice.create(
      {
        status: statusOrError.data,
        invoiceSeries: invoiceSeriesOrError.data,
        invoiceNumber: invoiceNumberOrError.data,
        issueDate: issueDateOrError.data,
        operationDate: operationDateOrError.data,
        invoiceCurrency,
        items: itemsOrErrors.data,
      },
      idOrError.data
    );
  }

  public mapToPersistence(source: Invoice, params?: MapperParamsType): InvoiceCreationAttributes {
    const subtotal = source.calculateSubtotal();
    const total = source.calculateTotal();

    const items = this.invoiceItemMapper.mapCollectionToPersistence(source.items, params);

    return {
      id: source.id.toString(),
      invoice_status: source.status.toPrimitive(),
      invoice_series: source.invoiceSeries.toPrimitive(),
      invoice_number: source.invoiceNumber.toPrimitive(),
      issue_date: source.issueDate.toPrimitive(),
      operation_date: source.operationDate.toPrimitive(),
      invoice_language: "es",
      invoice_currency: source.invoiceCurrency || "EUR",

      subtotal_amount: subtotal.amount,
      subtotal_scale: subtotal.scale,

      total_amount: total.amount,
      total_scale: total.scale,

      items,
    };
  }
}

const invoiceMapper: InvoiceMapper = new InvoiceMapper();
export { invoiceMapper };
