import { Tax } from "@erp/core/api";
import { CurrencyCode, LanguageCode } from "@repo/rdx-ddd";
import { Collection } from "@repo/rdx-utils";
import { ItemAmount } from "../../value-objects";
import { CustomerInvoiceItem } from "./customer-invoice-item";

export interface CustomerInvoiceItemsProps {
  items?: CustomerInvoiceItem[];
  languageCode: LanguageCode;
  currencyCode: CurrencyCode;
}

export class CustomerInvoiceItems extends Collection<CustomerInvoiceItem> {
  private _languageCode!: LanguageCode;
  private _currencyCode!: CurrencyCode;

  constructor(props: CustomerInvoiceItemsProps) {
    const { items = [], languageCode, currencyCode } = props;
    super(items);
    this._languageCode = languageCode;
    this._currencyCode = currencyCode;
  }

  public static create(props: CustomerInvoiceItemsProps): CustomerInvoiceItems {
    return new CustomerInvoiceItems(props);
  }

  add(item: CustomerInvoiceItem): boolean {
    // Antes de añadir un nuevo item, debo comprobar que el item a añadir
    // tiene el mismo "currencyCode" y "languageCode" que la colección de items.
    if (
      !this._languageCode.equals(item.languageCode) ||
      !this._currencyCode.equals(item.currencyCode)
    ) {
      return false;
    }
    return super.add(item);
  }

  public getSubtotalAmount(): ItemAmount {
    return this.getAll().reduce(
      (total, tax) => total.add(tax.getSubtotalAmount()),
      ItemAmount.zero(this._currencyCode.code)
    );
  }

  public getDiscountAmount(): ItemAmount {
    return this.getAll().reduce(
      (total, item) => total.add(item.getDiscountAmount()),
      ItemAmount.zero(this._currencyCode.code)
    );
  }

  public getTaxableAmount(): ItemAmount {
    return this.getAll().reduce(
      (total, item) => total.add(item.getTaxableAmount()),
      ItemAmount.zero(this._currencyCode.code)
    );
  }

  public getTaxesAmount(): ItemAmount {
    return this.getAll().reduce(
      (total, item) => total.add(item.getTaxesAmount()),
      ItemAmount.zero(this._currencyCode.code)
    );
  }

  public getTotalAmount(): ItemAmount {
    return this.getAll().reduce(
      (total, item) => total.add(item.getTotalAmount()),
      ItemAmount.zero(this._currencyCode.code)
    );
  }

  public getTaxesAmountByTaxes() {
    const resultMap = new Map<Tax, { taxableAmount: ItemAmount; taxesAmount: ItemAmount }>();

    const currencyCode = this._currencyCode.code;

    for (const item of this.getAll()) {
      for (const { taxableAmount, tax, taxesAmount } of item.getTaxesAmountByTaxes()) {
        const { taxableAmount: taxableCurrent, taxesAmount: taxesCurrent } = resultMap.get(tax) ?? {
          taxableAmount: ItemAmount.zero(currencyCode),
          taxesAmount: ItemAmount.zero(currencyCode),
        };
        resultMap.set(tax, {
          taxableAmount: taxableCurrent.add(taxableAmount),
          taxesAmount: taxesCurrent.add(taxesAmount),
        });
      }
    }

    const items = [];
    for (const [tax, { taxableAmount, taxesAmount }] of resultMap) {
      items.push({
        taxableAmount,
        tax,
        taxesAmount,
      });
    }

    return items;
  }
}
