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;

  series: Maybe<CustomerInvoiceSerie>;
  invoiceNumber: Maybe<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 {
  hasRecipient: boolean;
  hasPaymentMethod: boolean;

  getSubtotalAmount(): InvoiceAmount;
  getDiscountAmount(): InvoiceAmount;

  getTaxableAmount(): InvoiceAmount;
  getTaxesAmount(): InvoiceAmount;
  getTotalAmount(): InvoiceAmount;

  getTaxes(): InvoiceTaxTotal[];

  issueInvoice(newInvoiceNumber: CustomerInvoiceNumber): Result<CustomerInvoice, Error>;
}

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,
      });
  }

  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 status(): CustomerInvoiceStatus {
    return this.props.status;
  }

  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 _getDiscountAmount(subtotalAmount: InvoiceAmount): InvoiceAmount {
    return subtotalAmount.percentage(this.discountPercentage);
  }

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

  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 getDiscountAmount(): InvoiceAmount {
    return this._getDiscountAmount(this.getSubtotalAmount());
  }

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

  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();
    const discountAmount = this._getDiscountAmount(subtotalAmount);
    const taxableAmount = this._getTaxableAmount(subtotalAmount, discountAmount);
    const taxesAmount = this._getTaxesAmount(taxableAmount);
    const totalAmount = this._getTotalAmount(taxableAmount, taxesAmount);

    return {
      subtotalAmount,
      discountAmount,
      taxableAmount,
      taxesAmount,
      totalAmount,
    };
  }

  public issueInvoice(newInvoiceNumber: CustomerInvoiceNumber) {
    return CustomerInvoice.create(
      {
        ...this.props,
        status: CustomerInvoiceStatus.createIssued(),
        isProforma: false,
        invoiceNumber: Maybe.some(newInvoiceNumber),
      },
      this.id
    );
  }
}
