import { UBLBaseDecoder } from '../ubl.decoder.js';
import type { TInvoice, TCreditNote, TDebitNote } from '../../../interfaces/common.js';
import { business, finance } from '@tsclass/tsclass';
import { UBLDocumentType } from '../ubl.types.js';

/**
 * Decoder for XRechnung (UBL) format
 * Implements decoding of XRechnung invoices to TInvoice
 */
export class XRechnungDecoder extends UBLBaseDecoder {
  /**
   * Decodes a UBL credit note
   * @returns Promise resolving to a TCreditNote object
   */
  protected async decodeCreditNote(): Promise<TCreditNote> {
    // Extract common data
    const commonData = await this.extractCommonData();
    
    // Return the invoice data as a credit note
    return {
      ...commonData,
      invoiceType: 'creditnote'
    } as TCreditNote;
  }
  
  /**
   * Decodes a UBL debit note (invoice)
   * @returns Promise resolving to a TDebitNote object
   */
  protected async decodeDebitNote(): Promise<TDebitNote> {
    // Extract common data
    const commonData = await this.extractCommonData();
    
    // Return the invoice data as a debit note
    return {
      ...commonData,
      invoiceType: 'debitnote'
    } as TDebitNote;
  }
  
  /**
   * Extracts common invoice data from XRechnung XML
   * @returns Common invoice data
   */
  private async extractCommonData(): Promise<Partial<TInvoice>> {
    try {
      // Default values
      const invoiceId = this.getText('//cbc:ID', this.doc) || `INV-${Date.now()}`;
      const issueDateText = this.getText('//cbc:IssueDate', this.doc);
      const issueDate = issueDateText ? new Date(issueDateText).getTime() : Date.now();
      const currencyCode = this.getText('//cbc:DocumentCurrencyCode', this.doc) || 'EUR';
      
      // Extract payment terms
      let dueInDays = 30; // Default
      const dueDateText = this.getText('//cac:PaymentTerms/cbc:PaymentDueDate', this.doc);
      if (dueDateText) {
        const dueDateObj = new Date(dueDateText);
        const issueDateObj = new Date(issueDate);
        const diffTime = Math.abs(dueDateObj.getTime() - issueDateObj.getTime());
        dueInDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
      }
      
      // Extract items
      const items: finance.TInvoiceItem[] = [];
      const invoiceLines = this.select('//cac:InvoiceLine', this.doc);
      
      if (invoiceLines && Array.isArray(invoiceLines)) {
        for (let i = 0; i < invoiceLines.length; i++) {
          const line = invoiceLines[i];
          
          const position = i + 1;
          const name = this.getText('./cac:Item/cbc:Name', line) || `Item ${position}`;
          const articleNumber = this.getText('./cac:Item/cac:SellersItemIdentification/cbc:ID', line) || '';
          const unitType = this.getText('./cbc:InvoicedQuantity/@unitCode', line) || 'EA';
          
          let unitQuantity = 1;
          const quantityText = this.getText('./cbc:InvoicedQuantity', line);
          if (quantityText) {
            unitQuantity = parseFloat(quantityText) || 1;
          }
          
          let unitNetPrice = 0;
          const priceText = this.getText('./cac:Price/cbc:PriceAmount', line);
          if (priceText) {
            unitNetPrice = parseFloat(priceText) || 0;
          }
          
          let vatPercentage = 0;
          const percentText = this.getText('./cac:Item/cac:ClassifiedTaxCategory/cbc:Percent', line);
          if (percentText) {
            vatPercentage = parseFloat(percentText) || 0;
          }
          
          items.push({
            position,
            name,
            articleNumber,
            unitType,
            unitQuantity,
            unitNetPrice,
            vatPercentage
          });
        }
      }
      
      // Extract notes
      const notes: string[] = [];
      const noteNodes = this.select('//cbc:Note', this.doc);
      if (noteNodes && Array.isArray(noteNodes)) {
        for (let i = 0; i < noteNodes.length; i++) {
          const noteText = noteNodes[i].textContent || '';
          if (noteText) {
            notes.push(noteText);
          }
        }
      }
      
      // Extract seller and buyer information
      const seller = this.extractParty('//cac:AccountingSupplierParty/cac:Party');
      const buyer = this.extractParty('//cac:AccountingCustomerParty/cac:Party');
      
      // Create the common invoice data
      return {
        type: 'invoice',
        id: invoiceId,
        invoiceId: 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: false,
        currency: currencyCode as finance.TCurrency,
        notes: notes,
        objectActions: []
      };
    } catch (error) {
      console.error('Error extracting common data:', error);
      // Return default data
      return {
        type: 'invoice',
        id: `INV-${Date.now()}`,
        invoiceId: `INV-${Date.now()}`,
        date: Date.now(),
        status: 'invoice',
        versionInfo: {
          type: 'final',
          version: '1.0.0'
        },
        language: 'en',
        incidenceId: `INV-${Date.now()}`,
        from: this.createEmptyContact(),
        to: this.createEmptyContact(),
        subject: 'Invoice',
        items: [],
        dueInDays: 30,
        reverseCharge: false,
        currency: 'EUR',
        notes: [],
        objectActions: []
      };
    }
  }
  
  /**
   * Extracts party information from XML
   * @param partyPath XPath to the party element
   * @returns TContact object
   */
  private extractParty(partyPath: string): business.TContact {
    try {
      // Default values
      let name = '';
      let streetName = '';
      let houseNumber = '0';
      let city = '';
      let postalCode = '';
      let country = '';
      let countryCode = '';
      let vatId = '';
      let registrationId = '';
      let registrationName = '';
      
      // Try to extract party information
      const partyNodes = this.select(partyPath, this.doc);
      
      if (partyNodes && Array.isArray(partyNodes) && partyNodes.length > 0) {
        const party = partyNodes[0];
        
        // Extract name
        name = this.getText('./cac:PartyName/cbc:Name', party) || '';
        
        // Extract address
        const addressNodes = this.select('./cac:PostalAddress', party);
        if (addressNodes && Array.isArray(addressNodes) && addressNodes.length > 0) {
          const address = addressNodes[0];
          
          streetName = this.getText('./cbc:StreetName', address) || '';
          houseNumber = this.getText('./cbc:BuildingNumber', address) || '0';
          city = this.getText('./cbc:CityName', address) || '';
          postalCode = this.getText('./cbc:PostalZone', address) || '';
          
          const countryNodes = this.select('./cac:Country', address);
          if (countryNodes && Array.isArray(countryNodes) && countryNodes.length > 0) {
            const countryNode = countryNodes[0];
            country = this.getText('./cbc:Name', countryNode) || '';
            countryCode = this.getText('./cbc:IdentificationCode', countryNode) || '';
          }
        }
        
        // Extract tax information
        const taxSchemeNodes = this.select('./cac:PartyTaxScheme', party);
        if (taxSchemeNodes && Array.isArray(taxSchemeNodes) && taxSchemeNodes.length > 0) {
          vatId = this.getText('./cbc:CompanyID', taxSchemeNodes[0]) || '';
        }
        
        // Extract registration information
        const legalEntityNodes = this.select('./cac:PartyLegalEntity', party);
        if (legalEntityNodes && Array.isArray(legalEntityNodes) && legalEntityNodes.length > 0) {
          registrationId = this.getText('./cbc:CompanyID', legalEntityNodes[0]) || '';
          registrationName = this.getText('./cbc:RegistrationName', legalEntityNodes[0]) || name;
        }
      }
      
      return {
        type: 'company',
        name: name,
        description: '',
        address: {
          streetName: streetName,
          houseNumber: houseNumber,
          city: city,
          postalCode: postalCode,
          country: country,
          countryCode: countryCode
        },
        status: 'active',
        foundedDate: {
          year: 2000,
          month: 1,
          day: 1
        },
        registrationDetails: {
          vatId: vatId,
          registrationId: registrationId,
          registrationName: registrationName
        }
      };
    } catch (error) {
      console.error('Error extracting party information:', error);
      return this.createEmptyContact();
    }
  }
  
  /**
   * Creates an empty TContact object
   * @returns Empty TContact object
   */
  private createEmptyContact(): business.TContact {
    return {
      type: 'company',
      name: '',
      description: '',
      address: {
        streetName: '',
        houseNumber: '0',
        city: '',
        country: '',
        postalCode: ''
      },
      status: 'active',
      foundedDate: {
        year: 2000,
        month: 1,
        day: 1
      },
      registrationDetails: {
        vatId: '',
        registrationId: '',
        registrationName: ''
      }
    };
  }
}