import * as plugins from './plugins.js';
import * as xmldom from 'xmldom';

/**
 * A class to convert a given XML string (ZUGFeRD/Factur-X, UBL or fatturaPA)
 * into a structured ILetter with invoice data.
 * 
 * Handles different invoice XML formats:
 * - ZUGFeRD/Factur-X (CII)
 * - UBL
 * - FatturaPA
 */
export class ZUGFeRDXmlDecoder {
  private xmlString: string;
  private xmlFormat: string;
  private xmlDoc: Document | null = null;

  constructor(xmlString: string) {
    if (!xmlString) {
      throw new Error('No XML string provided to decoder');
    }
    
    this.xmlString = xmlString;
    
    // Simple format detection based on string contents
    this.xmlFormat = this.detectFormat();
    
    // Parse XML to DOM
    try {
      const parser = new xmldom.DOMParser();
      this.xmlDoc = parser.parseFromString(this.xmlString, 'text/xml');
    } catch (error) {
      console.error('Error parsing XML:', error);
    }
  }

  /**
   * Detects the XML invoice format using simple string checks
   */
  private detectFormat(): string {
    // ZUGFeRD/Factur-X (CII format)
    if (this.xmlString.includes('CrossIndustryInvoice') || 
        this.xmlString.includes('un/cefact') ||
        this.xmlString.includes('rsm:')) {
      return 'CII';
    }
    
    // UBL format
    if (this.xmlString.includes('Invoice') || 
        this.xmlString.includes('oasis:names:specification:ubl')) {
      return 'UBL';
    }
    
    // FatturaPA format
    if (this.xmlString.includes('FatturaElettronica') || 
        this.xmlString.includes('fatturapa.gov.it')) {
      return 'FatturaPA';
    }
    
    // Default to generic
    return 'unknown';
  }

  /**
   * Extracts text from the first element matching the XPath-like selector
   */
  private getElementText(tagName: string): string {
    if (!this.xmlDoc) {
      return '';
    }
    
    try {
      // Basic handling for namespaced tags
      let namespace = '';
      let localName = tagName;
      
      if (tagName.includes(':')) {
        const parts = tagName.split(':');
        namespace = parts[0];
        localName = parts[1];
      }
      
      // Find all elements with this name
      const elements = this.xmlDoc.getElementsByTagName(tagName);
      if (elements.length > 0) {
        return elements[0].textContent || '';
      }
      
      // Try with just the local name if we didn't find it with the namespace
      if (namespace) {
        const elements = this.xmlDoc.getElementsByTagName(localName);
        if (elements.length > 0) {
          return elements[0].textContent || '';
        }
      }
      
      return '';
    } catch (error) {
      console.error(`Error extracting element ${tagName}:`, error);
      return '';
    }
  }

  /**
   * Converts XML to a structured letter object
   */
  public async getLetterData(): Promise<plugins.tsclass.business.ILetter> {
    try {
      if (this.xmlFormat === 'CII') {
        return this.parseCII();
      } else if (this.xmlFormat === 'UBL') {
        // For now, use the default implementation
        return this.parseGeneric();
      } else if (this.xmlFormat === 'FatturaPA') {
        // For now, use the default implementation
        return this.parseGeneric();
      } else {
        return this.parseGeneric();
      }
    } catch (error) {
      console.error('Error converting XML to letter data:', error);
      
      // If all else fails, return a minimal letter object
      return this.createDefaultLetter();
    }
  }
  
  /**
   * Parse CII (ZUGFeRD/Factur-X) formatted XML
   */
  private parseCII(): plugins.tsclass.business.ILetter {
    // Extract invoice ID
    let invoiceId = this.getElementText('ram:ID');
    if (!invoiceId) {
      // Try alternative locations
      invoiceId = this.getElementText('rsm:ExchangedDocument ram:ID') || 'Unknown';
    }
    
    // Extract seller name
    let sellerName = this.getElementText('ram:Name');
    if (!sellerName) {
      sellerName = this.getElementText('ram:SellerTradeParty ram:Name') || 'Unknown Seller';
    }
    
    // Extract buyer name
    let buyerName = '';
    // Try to find BuyerTradeParty Name specifically
    if (this.xmlDoc) {
      const buyerParties = this.xmlDoc.getElementsByTagName('ram:BuyerTradeParty');
      if (buyerParties.length > 0) {
        const nameElements = buyerParties[0].getElementsByTagName('ram:Name');
        if (nameElements.length > 0) {
          buyerName = nameElements[0].textContent || '';
        }
      }
    }
    
    if (!buyerName) {
      buyerName = 'Unknown Buyer';
    }
    
    // Create seller
    const seller: plugins.tsclass.business.IContact = {
      name: sellerName,
      type: 'company',
      description: sellerName,
      address: {
        streetName: this.getElementText('ram:LineOne') || 'Unknown',
        houseNumber: '0',  // Required by IAddress interface
        city: this.getElementText('ram:CityName') || 'Unknown',
        country: this.getElementText('ram:CountryID') || 'Unknown',
        postalCode: this.getElementText('ram:PostcodeCode') || 'Unknown',
      },
    };
    
    // Create buyer
    const buyer: plugins.tsclass.business.IContact = {
      name: buyerName,
      type: 'company',
      description: buyerName,
      address: {
        streetName: 'Unknown',
        houseNumber: '0',
        city: 'Unknown',
        country: 'Unknown',
        postalCode: 'Unknown',
      },
    };
    
    // Extract invoice type
    let invoiceType = 'debitnote';
    const typeCode = this.getElementText('ram:TypeCode');
    if (typeCode === '381') {
      invoiceType = 'creditnote';
    }
    
    // Create invoice data
    const invoiceData: plugins.tsclass.finance.IInvoice = {
      id: invoiceId,
      status: null,
      type: invoiceType as 'debitnote' | 'creditnote',
      billedBy: seller,
      billedTo: buyer,
      deliveryDate: Date.now(),
      dueInDays: 30,
      periodOfPerformance: null,
      printResult: null,
      currency: (this.getElementText('ram:InvoiceCurrencyCode') || 'EUR') as plugins.tsclass.finance.TCurrency,
      notes: [],
      items: [
        {
          name: 'Item from XML',
          unitQuantity: 1,
          unitNetPrice: 0,
          vatPercentage: 0,
          position: 0,
          unitType: 'units',
        }
      ],
      reverseCharge: false,
    };
    
    // Return a letter
    return {
      versionInfo: {
        type: 'draft',
        version: '1.0.0',
      },
      type: 'invoice',
      date: Date.now(),
      subject: `Invoice: ${invoiceId}`,
      from: seller,
      to: buyer,
      content: {
        invoiceData: invoiceData,
        textData: null,
        timesheetData: null,
        contractData: null,
      },
      needsCoverSheet: false,
      objectActions: [],
      pdf: null,
      incidenceId: null,
      language: null,
      legalContact: null,
      logoUrl: null,
      pdfAttachments: null,
      accentColor: null,
    };
  }
  
  /**
   * Parse generic XML using default approach
   */
  private parseGeneric(): plugins.tsclass.business.ILetter {
    // Create a default letter with some extraction attempts
    return this.createDefaultLetter();
  }
  
  /**
   * Creates a default letter object with minimal data
   */
  private createDefaultLetter(): plugins.tsclass.business.ILetter {
    // Create a default seller
    const seller: plugins.tsclass.business.IContact = {
      name: 'Unknown Seller',
      type: 'company',
      description: 'Unknown Seller',  // Required by IContact interface
      address: {
        streetName: 'Unknown',
        houseNumber: '0',  // Required by IAddress interface
        city: 'Unknown',
        country: 'Unknown',
        postalCode: 'Unknown',
      },
    };
    
    // Create a default buyer
    const buyer: plugins.tsclass.business.IContact = {
      name: 'Unknown Buyer',
      type: 'company',
      description: 'Unknown Buyer',  // Required by IContact interface
      address: {
        streetName: 'Unknown',
        houseNumber: '0',  // Required by IAddress interface
        city: 'Unknown',
        country: 'Unknown',
        postalCode: 'Unknown',
      },
    };
    
    // Create default invoice data
    const invoiceData: plugins.tsclass.finance.IInvoice = {
      id: 'Unknown',
      status: null,
      type: 'debitnote',
      billedBy: seller,
      billedTo: buyer,
      deliveryDate: Date.now(),
      dueInDays: 30,
      periodOfPerformance: null,
      printResult: null,
      currency: 'EUR' as plugins.tsclass.finance.TCurrency,
      notes: [],
      items: [
        {
          name: 'Unknown Item',
          unitQuantity: 1,
          unitNetPrice: 0,
          vatPercentage: 0,
          position: 0,
          unitType: 'units',
        }
      ],
      reverseCharge: false,
    };
    
    // Return a default letter
    return {
      versionInfo: {
        type: 'draft',
        version: '1.0.0',
      },
      type: 'invoice',
      date: Date.now(),
      subject: `Extracted Invoice (${this.xmlFormat} format)`,
      from: seller,
      to: buyer,
      content: {
        invoiceData: invoiceData,
        textData: null,
        timesheetData: null,
        contractData: null,
      },
      needsCoverSheet: false,
      objectActions: [],
      pdf: null,
      incidenceId: null,
      language: null,
      legalContact: null,
      logoUrl: null,
      pdfAttachments: null,
      accentColor: null,
    };
  }
}