import { Invoice } from '../../types/invoice.types';
import fs from 'fs';

export class XRechnungGenerator {
  private invoice: Invoice;

  constructor(invoice: Invoice) {
    this.invoice = invoice;
  }

  /**
   * Generiert eine XRechnung-XML-Datei nach dem deutschen E-Rechnungs-Standard
   */
  public generateXML(): string {
    const xml = `<?xml version="1.0" encoding="UTF-8"?>
<rsm:CrossIndustryInvoice xmlns:rsm="urn:ferd:CrossIndustryDocument:invoice:2p0" xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
  <rsm:ExchangedDocumentContext>
    <ram:TestIndicator>
      <udt:Indicator>false</udt:Indicator>
    </ram:TestIndicator>
    <ram:BusinessProcessSpecifiedDocumentContextParameter>
      <ram:ID>urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_2.3</ram:ID>
    </ram:BusinessProcessSpecifiedDocumentContextParameter>
    <ram:GuidelineSpecifiedDocumentContextParameter>
      <ram:ID>urn:cen.eu:en16931:2017</ram:ID>
    </ram:GuidelineSpecifiedDocumentContextParameter>
  </rsm:ExchangedDocumentContext>
  <rsm:ExchangedDocument>
    <ram:ID>${this.escapeXml(this.invoice.details.invoiceNumber)}</ram:ID>
    <ram:TypeCode>380</ram:TypeCode>
    <ram:IssueDateTime>
      <udt:DateTimeString format="102">${this.formatDate(this.invoice.details.invoiceDate)}</udt:DateTimeString>
    </ram:IssueDateTime>
  </rsm:ExchangedDocument>
  <rsm:SupplyChainTradeTransaction>
    <ram:IncludedSupplyChainTradeLineItem>
${this.generateLineItems()}
    </ram:IncludedSupplyChainTradeLineItem>
    <ram:ApplicableHeaderTradeAgreement>
      <ram:SellerTradeParty>
        <ram:Name>${this.escapeXml(this.invoice.sender.name)}</ram:Name>
        <ram:PostalTradeAddress>
          <ram:LineOne>${this.escapeXml(this.invoice.sender.address.street)}</ram:LineOne>
          <ram:CityName>${this.escapeXml(this.invoice.sender.address.city)}</ram:CityName>
          <ram:PostcodeCode>${this.escapeXml(this.invoice.sender.address.postalCode)}</ram:PostcodeCode>
          <ram:CountryID>DE</ram:CountryID>
        </ram:PostalTradeAddress>
        <ram:SpecifiedTaxRegistration>
          <ram:ID schemeID="FC">${this.escapeXml(this.invoice.sender.vatId || '')}</ram:ID>
        </ram:SpecifiedTaxRegistration>
        <ram:DefinedTradeContact>
          <ram:PersonName>${this.escapeXml(this.invoice.sender.businessOwner || '')}</ram:PersonName>
          <ram:TelephoneUniversalCommunication>
            <ram:CompleteNumber>${this.escapeXml(this.invoice.sender.contactInfo.phone)}</ram:CompleteNumber>
          </ram:TelephoneUniversalCommunication>
          <ram:EmailURIUniversalCommunication>
            <ram:URIID>${this.escapeXml(this.invoice.sender.contactInfo.email)}</ram:URIID>
          </ram:EmailURIUniversalCommunication>
        </ram:DefinedTradeContact>
      </ram:SellerTradeParty>
      <ram:BuyerTradeParty>
        <ram:Name>${this.escapeXml(this.invoice.recipient.name)}</ram:Name>
        <ram:PostalTradeAddress>
          <ram:LineOne>${this.escapeXml(this.invoice.recipient.address.street)}</ram:LineOne>
          <ram:CityName>${this.escapeXml(this.invoice.recipient.address.city)}</ram:CityName>
          <ram:PostcodeCode>${this.escapeXml(this.invoice.recipient.address.postalCode)}</ram:PostcodeCode>
          <ram:CountryID>DE</ram:CountryID>
        </ram:PostalTradeAddress>
      </ram:BuyerTradeParty>
      <ram:BuyerOrderReferencedDocument>
        <ram:IssuerAssignedID>${this.escapeXml(this.invoice.details.customerNumber)}</ram:IssuerAssignedID>
      </ram:BuyerOrderReferencedDocument>
    </ram:ApplicableHeaderTradeAgreement>
    <ram:ApplicableHeaderTradeDelivery>
      <ram:ActualDeliverySupplyChainEvent>
        <ram:OccurrenceDateTime>
          <udt:DateTimeString format="102">${this.formatDate(this.invoice.details.deliveryDate)}</udt:DateTimeString>
        </ram:OccurrenceDateTime>
      </ram:ActualDeliverySupplyChainEvent>
    </ram:ApplicableHeaderTradeDelivery>
    <ram:ApplicableHeaderTradeSettlement>
      <ram:InvoiceCurrencyCode>${this.invoice.totals.currency}</ram:InvoiceCurrencyCode>
      <ram:SpecifiedTradeSettlementPaymentMeans>
        <ram:TypeCode>30</ram:TypeCode>
        <ram:Information>${this.escapeXml(this.invoice.paymentInfo.bank)}</ram:Information>
        <ram:PayerPartyDebtorFinancialAccount>
          <ram:IBANID>${this.escapeXml(this.invoice.paymentInfo.iban)}</ram:IBANID>
        </ram:PayerPartyDebtorFinancialAccount>
        <ram:PayerSpecifiedDebtorFinancialInstitution>
          <ram:BICID>${this.escapeXml(this.invoice.paymentInfo.bic)}</ram:BICID>
        </ram:PayerSpecifiedDebtorFinancialInstitution>
      </ram:SpecifiedTradeSettlementPaymentMeans>
      <ram:ApplicableTradeSettlementFinancialMonetarySummation>
        <ram:LineTotalAmount>${this.invoice.totals.netAmount.toFixed(2)}</ram:LineTotalAmount>
        <ram:TaxBasisTotalAmount>${this.invoice.totals.netAmount.toFixed(2)}</ram:TaxBasisTotalAmount>
        <ram:TaxTotalAmount currencyID="${this.invoice.totals.currency}">${this.invoice.totals.vatAmount.toFixed(2)}</ram:TaxTotalAmount>
        <ram:GrandTotalAmount>${this.invoice.totals.grossAmount.toFixed(2)}</ram:GrandTotalAmount>
        <ram:TotalPrepaidAmount>0.00</ram:TotalPrepaidAmount>
        <ram:DuePayableAmount>${this.invoice.totals.grossAmount.toFixed(2)}</ram:DuePayableAmount>
      </ram:ApplicableTradeSettlementFinancialMonetarySummation>
      <ram:ApplicableTradeTax>
        <ram:CalculatedAmount>${this.invoice.totals.vatAmount.toFixed(2)}</ram:CalculatedAmount>
        <ram:TypeCode>VAT</ram:TypeCode>
        <ram:BasisAmount>${this.invoice.totals.netAmount.toFixed(2)}</ram:BasisAmount>
        <ram:CategoryCode>S</ram:CategoryCode>
        <ram:RateApplicablePercent>${this.invoice.totals.vatRate.toFixed(2)}</ram:RateApplicablePercent>
      </ram:ApplicableTradeTax>
    </ram:ApplicableHeaderTradeSettlement>
  </rsm:SupplyChainTradeTransaction>
</rsm:CrossIndustryInvoice>`;

    return xml;
  }

  /**
   * Generiert die XML-Datei und speichert sie
   */
  public async generateXMLFile(outputPath: string): Promise<void> {
    const xml = this.generateXML();
    
    return new Promise((resolve, reject) => {
      fs.writeFile(outputPath, xml, 'utf8', (err) => {
        if (err) {
          reject(err);
        } else {
          resolve();
        }
      });
    });
  }

  private generateLineItems(): string {
    return this.invoice.items.map((item, index) => `
      <ram:AssociatedDocumentLineDocument>
        <ram:LineID>${index + 1}</ram:LineID>
      </ram:AssociatedDocumentLineDocument>
      <ram:SpecifiedTradeProduct>
        <ram:Name>${this.escapeXml(item.description)}</ram:Name>
      </ram:SpecifiedTradeProduct>
      <ram:SpecifiedLineTradeAgreement>
        <ram:GrossPriceProductTradePrice>
          <ram:ChargeAmount>${item.unitPrice.toFixed(2)}</ram:ChargeAmount>
          <ram:BasisQuantity unitCode="${item.unit}">${item.quantity}</ram:BasisQuantity>
        </ram:GrossPriceProductTradePrice>
      </ram:SpecifiedLineTradeAgreement>
      <ram:SpecifiedLineTradeDelivery>
        <ram:BilledQuantity unitCode="${item.unit}">${item.quantity}</ram:BilledQuantity>
      </ram:SpecifiedLineTradeDelivery>
      <ram:SpecifiedLineTradeSettlement>
        <ram:ApplicableTradeTax>
          <ram:TypeCode>VAT</ram:TypeCode>
          <ram:CategoryCode>S</ram:CategoryCode>
          <ram:RateApplicablePercent>${item.vatRate.toFixed(2)}</ram:RateApplicablePercent>
        </ram:ApplicableTradeTax>
        <ram:SpecifiedTradeSettlementLineMonetarySummation>
          <ram:LineTotalAmount>${item.total.toFixed(2)}</ram:LineTotalAmount>
        </ram:SpecifiedTradeSettlementLineMonetarySummation>
      </ram:SpecifiedLineTradeSettlement>`).join('');
  }

  private escapeXml(text: string): string {
    return text
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&apos;');
  }

  private formatDate(dateString: string): string {
    // Konvertiert "DD.MM.YY" zu "YYYYMMDD"
    const parts = dateString.split('.');
    if (parts.length === 3) {
      const day = parts[0].padStart(2, '0');
      const month = parts[1].padStart(2, '0');
      const year = '20' + parts[2]; // Annahme: YY ist 20YY
      return `${year}${month}${day}`;
    }
    return dateString;
  }
}

/**
 * Convenience-Funktionen für einfache Verwendung
 */
export async function generateXRechnungXML(invoice: Invoice): Promise<string> {
  const generator = new XRechnungGenerator(invoice);
  return generator.generateXML();
}

export async function generateXRechnungXMLFile(invoice: Invoice, outputPath: string): Promise<void> {
  const generator = new XRechnungGenerator(invoice);
  return generator.generateXMLFile(outputPath);
} 