import {
  AggregateRoot,
  CurrencyCode,
  DomainValidationError,
  LanguageCode,
  Percentage,
  TextValue,
  UniqueID,
  UtcDate,
} from "@repo/rdx-ddd";
import { Maybe, Result } from "@repo/rdx-utils";
import { CustomerInvoiceItems, InvoicePaymentMethod, InvoiceTaxTotal } from "../entities";
import {
  CustomerInvoiceNumber,
  CustomerInvoiceSerie,
  CustomerInvoiceStatus,
  InvoiceAmount,
  InvoiceRecipient,
} from "../value-objects";

export interface CustomerInvoiceProps {
  companyId: UniqueID;

  isProforma: boolean;
  status: CustomerInvoiceStatus;

  proformaId: Maybe<UniqueID>;

  series: Maybe<CustomerInvoiceSerie>;
  invoiceNumber: CustomerInvoiceNumber;

  invoiceDate: UtcDate;
  operationDate: Maybe<UtcDate>;

  customerId: UniqueID;
  recipient: Maybe<InvoiceRecipient>;

  reference: Maybe<string>;
  description: Maybe<string>;
  notes: Maybe<TextValue>;

  languageCode: LanguageCode;
  currencyCode: CurrencyCode;

  items: CustomerInvoiceItems;

  paymentMethod: Maybe<InvoicePaymentMethod>;

  discountPercentage: Percentage;

  /*verifactu_qr: string;
  verifactu_url: string;
  verifactu_status: string;*/
}

export type CustomerInvoicePatchProps = Partial<
  Omit<CustomerInvoiceProps, "companyId" | "items">
> & {
  items?: CustomerInvoiceItems;
};

export interface ICustomerInvoice {
  canTransitionTo(nextStatus: string): boolean;

  hasRecipient: boolean;
  hasPaymentMethod: boolean;

  getTaxes(): InvoiceTaxTotal[];
  getProps(): CustomerInvoiceProps;
}

export class CustomerInvoice
  extends AggregateRoot<CustomerInvoiceProps>
  implements ICustomerInvoice
{
  private _items!: CustomerInvoiceItems;

  protected constructor(props: CustomerInvoiceProps, id?: UniqueID) {
    super(props, id);
    this._items =
      props.items ||
      CustomerInvoiceItems.create({
        languageCode: props.languageCode,
        currencyCode: props.currencyCode,
      });
  }

  getHeaderDiscountAmount(): InvoiceAmount {
    throw new Error("Method not implemented.");
  }

  getTaxableAmount(): InvoiceAmount {
    throw new Error("Method not implemented.");
  }

  getTaxesAmount(): InvoiceAmount {
    throw new Error("Method not implemented.");
  }

  getTotalAmount(): InvoiceAmount {
    throw new Error("Method not implemented.");
  }

  static create(props: CustomerInvoiceProps, id?: UniqueID): Result<CustomerInvoice, Error> {
    const customerInvoice = new CustomerInvoice(props, id);

    // Reglas de negocio / validaciones

    if (!customerInvoice.isProforma && !customerInvoice.hasRecipient) {
      return Result.fail(
        new DomainValidationError(
          "MISSING_CUSTOMER_DATA",
          "recipient",
          "Customer data must be provided for non-proforma invoices"
        )
      );
    }

    // 🔹 Disparar evento de dominio "CustomerInvoiceAuthenticatedEvent"
    //const { customerInvoice } = props;
    //user.addDomainEvent(new CustomerInvoiceAuthenticatedEvent(id, customerInvoice.toString()));

    return Result.ok(customerInvoice);
  }

  public update(partialInvoice: CustomerInvoicePatchProps): Result<CustomerInvoice, Error> {
    const { items, ...rest } = partialInvoice;

    const updatedProps = {
      ...this.props,
      ...rest,
    } as CustomerInvoiceProps;

    /*if (partialAddress) {
          const updatedAddressOrError = this.address.update(partialAddress);
          if (updatedAddressOrError.isFailure) {
            return Result.fail(updatedAddressOrError.error);
          }
    
          updatedProps.address = updatedAddressOrError.data;
        }*/

    return CustomerInvoice.create(updatedProps, this.id);
  }

  public get companyId(): UniqueID {
    return this.props.companyId;
  }

  public get customerId(): UniqueID {
    return this.props.customerId;
  }

  public get isProforma(): boolean {
    return this.props.isProforma;
  }

  public get proformaId(): Maybe<UniqueID> {
    return this.props.proformaId;
  }

  public get status(): CustomerInvoiceStatus {
    return this.props.status;
  }

  canTransitionTo(nextStatus: string): boolean {
    return this.props.status.canTransitionTo(nextStatus);
  }

  public get series(): Maybe<CustomerInvoiceSerie> {
    return this.props.series;
  }

  public get invoiceNumber() {
    return this.props.invoiceNumber;
  }

  public get invoiceDate(): UtcDate {
    return this.props.invoiceDate;
  }

  public get operationDate(): Maybe<UtcDate> {
    return this.props.operationDate;
  }

  public get reference(): Maybe<string> {
    return this.props.reference;
  }

  public get description(): Maybe<string> {
    return this.props.description;
  }

  public get notes(): Maybe<TextValue> {
    return this.props.notes;
  }

  public get recipient(): Maybe<InvoiceRecipient> {
    return this.props.recipient;
  }

  public get paymentMethod(): Maybe<InvoicePaymentMethod> {
    return this.props.paymentMethod;
  }

  public get languageCode(): LanguageCode {
    return this.props.languageCode;
  }

  public get currencyCode(): CurrencyCode {
    return this.props.currencyCode;
  }

  public get discountPercentage(): Percentage {
    return this.props.discountPercentage;
  }

  // Method to get the complete list of line items
  public get items(): CustomerInvoiceItems {
    return this._items;
  }

  public get hasRecipient() {
    return this.recipient.isSome();
  }

  public get hasPaymentMethod() {
    return this.paymentMethod.isSome();
  }

  private _getHeaderDiscountAmount(
    subtotalAmount: InvoiceAmount,
    itemsDiscountAmount: InvoiceAmount
  ): InvoiceAmount {
    return subtotalAmount.subtract(itemsDiscountAmount).percentage(this.discountPercentage);
  }

  private _getTaxableAmount(
    subtotalAmount: InvoiceAmount,
    itemsDiscountAmount: InvoiceAmount,
    headerDiscountAmount: InvoiceAmount
  ): InvoiceAmount {
    return subtotalAmount.subtract(itemsDiscountAmount).subtract(headerDiscountAmount);
  }

  private _getTaxesAmount(taxableAmount: InvoiceAmount): InvoiceAmount {
    let amount = InvoiceAmount.zero(this.currencyCode.code);

    const itemTaxes = this.items.getTaxesAmountByTaxes();

    for (const taxItem of itemTaxes) {
      amount = amount.add(
        InvoiceAmount.create({
          value: taxItem.taxesAmount.convertScale(2).value,
          currency_code: this.currencyCode.code,
        }).data
      );
    }
    return amount;
  }

  private _getTotalAmount(taxableAmount: InvoiceAmount, taxesAmount: InvoiceAmount): InvoiceAmount {
    return taxableAmount.add(taxesAmount);
  }

  public _getSubtotalAmount(): InvoiceAmount {
    const itemsSubtotal = this.items.getSubtotalAmount().convertScale(2);

    return InvoiceAmount.create({
      value: itemsSubtotal.value,
      currency_code: this.currencyCode.code,
    }).data as InvoiceAmount;
  }

  public _getItemsDiscountAmount(): InvoiceAmount {
    const itemsDiscountAmount = this.items.getDiscountAmount().convertScale(2);

    return InvoiceAmount.create({
      value: itemsDiscountAmount.value,
      currency_code: this.currencyCode.code,
    }).data as InvoiceAmount;
  }

  /*public getHeaderDiscountAmount(): InvoiceAmount {
    return this._getHeaderDiscountAmount(this.getSubtotalAmount());
  }

  public getTaxableAmount(): InvoiceAmount {
    return this._getTaxableAmount(this.getSubtotalAmount(), this.getHeaderDiscountAmount());
  }

  public getTaxesAmount(): InvoiceAmount {
    return this._getTaxesAmount(this.getTaxableAmount());
  }

  public getTotalAmount(): InvoiceAmount {
    const taxableAmount = this.getTaxableAmount();
    const taxesAmount = this._getTaxesAmount(taxableAmount);

    return this._getTotalAmount(taxableAmount, taxesAmount);
  }*/

  public getTaxes(): InvoiceTaxTotal[] {
    const itemTaxes = this.items.getTaxesAmountByTaxes();

    return itemTaxes.map((item) => {
      return {
        tax: item.tax,
        taxableAmount: InvoiceAmount.create({
          value: item.taxableAmount.convertScale(2).value,
          currency_code: this.currencyCode.code,
        }).data,
        taxesAmount: InvoiceAmount.create({
          value: item.taxesAmount.convertScale(2).value,
          currency_code: this.currencyCode.code,
        }).data,
      };
    });
  }

  public getAllAmounts() {
    const subtotalAmount = this._getSubtotalAmount(); // Sin IVA ni dtos de línea
    const itemDiscountAmount = this._getItemsDiscountAmount(); // Suma de los Importes de descuentos de linea
    const headerDiscountAmount = this._getHeaderDiscountAmount(subtotalAmount, itemDiscountAmount); // Importe de descuento de cabecera

    const taxableAmount = this._getTaxableAmount(
      subtotalAmount,
      itemDiscountAmount,
      headerDiscountAmount
    ); //

    const taxesAmount = this._getTaxesAmount(taxableAmount);
    const totalAmount = this._getTotalAmount(taxableAmount, taxesAmount);

    return {
      subtotalAmount,
      itemDiscountAmount,
      headerDiscountAmount,
      taxableAmount,
      taxesAmount,
      totalAmount,
    };
  }

  public getProps(): CustomerInvoiceProps {
    return this.props;
  }
}
