import { CIIBaseDecoder } from '../cii.decoder.js';
import type { TInvoice, TCreditNote, TDebitNote } from '../../../interfaces/common.js';
import { business, finance } from '../../../plugins.js';

/**
 * Decoder for ZUGFeRD invoice format
 */
export class ZUGFeRDDecoder extends CIIBaseDecoder {
  /**
   * Decodes a ZUGFeRD credit note
   * @returns Promise resolving to a TCreditNote object
   */
  protected async decodeCreditNote(): Promise<TCreditNote> {
    // Get common invoice data
    const commonData = await this.extractCommonData();

    // Create a credit note with the common data
    return {
      ...commonData,
      invoiceType: 'creditnote'
    } as TCreditNote;
  }

  /**
   * Decodes a ZUGFeRD debit note (invoice)
   * @returns Promise resolving to a TDebitNote object
   */
  protected async decodeDebitNote(): Promise<TDebitNote> {
    // Get common invoice data
    const commonData = await this.extractCommonData();

    // Create a debit note with the common data
    return {
      ...commonData,
      invoiceType: 'debitnote'
    } as TDebitNote;
  }

  /**
   * Extracts common invoice data from ZUGFeRD XML
   * @returns Common invoice data
   */
  private async extractCommonData(): Promise<Partial<TInvoice>> {
    // Extract invoice ID
    const invoiceId = this.getText('//rsm:ExchangedDocument/ram:ID');

    // Extract issue date
    const issueDateStr = this.getText('//ram:IssueDateTime/udt:DateTimeString');
    const issueDate = issueDateStr ? new Date(issueDateStr).getTime() : Date.now();

    // Extract seller information
    const seller = this.extractParty('//ram:SellerTradeParty');

    // Extract buyer information
    const buyer = this.extractParty('//ram:BuyerTradeParty');

    // Extract items
    const items = this.extractItems();

    // Extract due date
    const dueDateStr = this.getText('//ram:SpecifiedTradePaymentTerms/ram:DueDateDateTime/udt:DateTimeString');
    const dueDate = dueDateStr ? new Date(dueDateStr).getTime() : Date.now();
    const dueInDays = Math.round((dueDate - issueDate) / (1000 * 60 * 60 * 24));

    // Extract currency
    const currencyCode = this.getText('//ram:InvoiceCurrencyCode') || 'EUR';

    // Extract total amount (not used in this implementation but could be useful)
    // const totalAmount = this.getNumber('//ram:GrandTotalAmount');

    // Extract notes
    const notes = this.extractNotes();

    // Check for reverse charge
    const reverseCharge = this.exists('//ram:SpecifiedTradeAllowanceCharge/ram:ReasonCode[text()="62"]');

    // Create the common invoice data
    return {
      type: 'invoice',
      id: invoiceId,
      date: issueDate,
      status: 'invoice',
      versionInfo: {
        type: 'final',
        version: '1.0.0'
      },
      language: 'en',
      incidenceId: invoiceId,
      from: seller,
      to: buyer,
      subject: `Invoice ${invoiceId}`,
      items: items,
      dueInDays: dueInDays,
      reverseCharge: reverseCharge,
      currency: currencyCode as finance.TCurrency,
      notes: notes,
      deliveryDate: issueDate,
      objectActions: [],
      invoiceType: 'debitnote' // Default to debit note, will be overridden in decode methods
    };
  }

  /**
   * Extracts party information from ZUGFeRD XML
   * @param partyXPath XPath to the party node
   * @returns Party information as TContact
   */
  private extractParty(partyXPath: string): business.TContact {
    // Extract name
    const name = this.getText(`${partyXPath}/ram:Name`);

    // Extract address
    const streetName = this.getText(`${partyXPath}/ram:PostalTradeAddress/ram:LineOne`);
    const city = this.getText(`${partyXPath}/ram:PostalTradeAddress/ram:CityName`);
    const postalCode = this.getText(`${partyXPath}/ram:PostalTradeAddress/ram:PostcodeCode`);
    const country = this.getText(`${partyXPath}/ram:PostalTradeAddress/ram:CountryID`);

    // Try to extract house number from street if possible
    let houseNumber = '';
    const streetParts = streetName.match(/^(.*?)\s+(\d+.*)$/);
    if (streetParts) {
      // If we can split into street name and house number
      houseNumber = streetParts[2];
    }

    // Create address object
    const address = {
      streetName: streetName,
      houseNumber: houseNumber,
      city: city,
      postalCode: postalCode,
      country: country
    };

    // Extract VAT ID
    const vatId = this.getText(`${partyXPath}/ram:SpecifiedTaxRegistration/ram:ID[@schemeID="VA"]`) || '';

    // Extract registration ID
    const registrationId = this.getText(`${partyXPath}/ram:SpecifiedTaxRegistration/ram:ID[@schemeID="FC"]`) || '';

    // Create contact object
    return {
      type: 'company',
      name: name,
      description: '',
      address: address,
      status: 'active',
      foundedDate: this.createDefaultDate(),
      registrationDetails: {
        vatId: vatId,
        registrationId: registrationId,
        registrationName: ''
      }
    } as business.TContact;
  }

  /**
   * Extracts invoice items from ZUGFeRD XML
   * @returns Array of invoice items
   */
  private extractItems(): finance.TInvoiceItem[] {
    const items: finance.TInvoiceItem[] = [];

    // Get all item nodes
    const itemNodes = this.select('//ram:IncludedSupplyChainTradeLineItem', this.doc);

    // Process each item
    if (Array.isArray(itemNodes)) {
      for (let i = 0; i < itemNodes.length; i++) {
        const itemNode = itemNodes[i];

        // Extract item data
        const name = this.getText('ram:SpecifiedTradeProduct/ram:Name', itemNode);
        const articleNumber = this.getText('ram:SpecifiedTradeProduct/ram:SellerAssignedID', itemNode);
        const unitQuantity = this.getNumber('ram:SpecifiedLineTradeDelivery/ram:BilledQuantity', itemNode);
        const unitType = this.getText('ram:SpecifiedLineTradeDelivery/ram:BilledQuantity/@unitCode', itemNode) || 'EA';
        const unitNetPrice = this.getNumber('ram:SpecifiedLineTradeAgreement/ram:NetPriceProductTradePrice/ram:ChargeAmount', itemNode);
        const vatPercentage = this.getNumber('ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax/ram:RateApplicablePercent', itemNode);

        // Create item object
        items.push({
          position: i + 1,
          name: name,
          articleNumber: articleNumber,
          unitType: unitType,
          unitQuantity: unitQuantity,
          unitNetPrice: unitNetPrice,
          vatPercentage: vatPercentage
        });
      }
    }

    return items;
  }

  /**
   * Extracts notes from ZUGFeRD XML
   * @returns Array of notes
   */
  private extractNotes(): string[] {
    const notes: string[] = [];

    // Get all note nodes
    const noteNodes = this.select('//ram:IncludedNote', this.doc);

    // Process each note
    if (Array.isArray(noteNodes)) {
      for (let i = 0; i < noteNodes.length; i++) {
        const noteNode = noteNodes[i];
        const noteText = this.getText('ram:Content', noteNode);

        if (noteText) {
          notes.push(noteText);
        }
      }
    }

    return notes;
  }

  /**
   * Creates a default date for empty date fields
   * @returns Default date as timestamp
   */
  private createDefaultDate(): any {
    // Create a date object that will be compatible with TContact
    return {
      year: 2000,
      month: 1,
      day: 1
    };
  }
}
