// ==================== ZUGFeRD / FACTUR-X XML (CII, EN 16931) ====================
// Erzeugt das Cross-Industry-Invoice-XML (UN/CEFACT CII) für eine Gutschrift
// (Dokumenttyp 381). Profil: EN 16931 ("urn:cen.eu:en16931:2017").
//
// WICHTIG: Muss vor produktivem Einsatz gegen einen ZUGFeRD-/Factur-X-
// Validator geprüft werden. Verkäufer = ausstellende Firma (CompanyProfile),
// Käufer = Kunde. Beträge positiv (der Typcode 381 kennzeichnet die Gutschrift).

export interface ZugferdParty {
  name: string;
  street: string;
  houseNumber: string;
  postalCode: string;
  city: string;
  country: string; // ISO-2
  vatId?: string;
  taxNumber?: string;
}

export interface ZugferdData {
  number: string;
  issueDate: Date;
  currency: string;
  seller: ZugferdParty;
  buyer: Omit<ZugferdParty, 'vatId' | 'taxNumber'> & { vatId?: string };
  lineName: string;
  vatRelevant: boolean;
  vatRatePercent: number;
  amountNet: number;
  amountVat: number;
  amountGross: number;
}

function esc(s: string | undefined | null): string {
  return (s ?? '')
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;');
}

const n2 = (n: number) => (Math.round((n + Number.EPSILON) * 100) / 100).toFixed(2);
const date102 = (d: Date) =>
  `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, '0')}${String(d.getDate()).padStart(2, '0')}`;

function addressBlock(p: { street: string; houseNumber: string; postalCode: string; city: string; country: string }): string {
  const lineOne = `${p.street} ${p.houseNumber}`.trim();
  return `        <ram:PostalTradeAddress>
          <ram:PostcodeCode>${esc(p.postalCode)}</ram:PostcodeCode>
          <ram:LineOne>${esc(lineOne)}</ram:LineOne>
          <ram:CityName>${esc(p.city)}</ram:CityName>
          <ram:CountryID>${esc(p.country || 'DE')}</ram:CountryID>
        </ram:PostalTradeAddress>`;
}

export function buildZugferdXml(d: ZugferdData): string {
  // Steuerkategorie: S = Regelsatz, E = steuerbefreit (ohne USt-Ausweis).
  const categoryCode = d.vatRelevant ? 'S' : 'E';
  const ratePct = d.vatRelevant ? d.vatRatePercent : 0;
  const exemptionReason = d.vatRelevant ? '' : 'Kein gesonderter Umsatzsteuerausweis';

  const sellerTax: string[] = [];
  if (d.seller.vatId) {
    sellerTax.push(`          <ram:SpecifiedTaxRegistration>
            <ram:ID schemeID="VA">${esc(d.seller.vatId)}</ram:ID>
          </ram:SpecifiedTaxRegistration>`);
  }
  if (d.seller.taxNumber) {
    sellerTax.push(`          <ram:SpecifiedTaxRegistration>
            <ram:ID schemeID="FC">${esc(d.seller.taxNumber)}</ram:ID>
          </ram:SpecifiedTaxRegistration>`);
  }

  const buyerTax = d.buyer.vatId
    ? `          <ram:SpecifiedTaxRegistration>
            <ram:ID schemeID="VA">${esc(d.buyer.vatId)}</ram:ID>
          </ram:SpecifiedTaxRegistration>`
    : '';

  const lineTaxCategory = `            <ram:ApplicableTradeTax>
              <ram:TypeCode>VAT</ram:TypeCode>
              <ram:CategoryCode>${categoryCode}</ram:CategoryCode>
              <ram:RateApplicablePercent>${n2(ratePct)}</ram:RateApplicablePercent>
            </ram:ApplicableTradeTax>`;

  const headerTax = `        <ram:ApplicableTradeTax>
          <ram:CalculatedAmount>${n2(d.amountVat)}</ram:CalculatedAmount>
          <ram:TypeCode>VAT</ram:TypeCode>${exemptionReason ? `\n          <ram:ExemptionReason>${esc(exemptionReason)}</ram:ExemptionReason>` : ''}
          <ram:BasisAmount>${n2(d.amountNet)}</ram:BasisAmount>
          <ram:CategoryCode>${categoryCode}</ram:CategoryCode>
          <ram:RateApplicablePercent>${n2(ratePct)}</ram:RateApplicablePercent>
        </ram:ApplicableTradeTax>`;

  return `<?xml version="1.0" encoding="UTF-8"?>
<rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100" xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100" xmlns:qdt="urn:un:unece:uncefact:data:standard:QualifiedDataType:100">
  <rsm:ExchangedDocumentContext>
    <ram:GuidelineSpecifiedDocumentContextParameter>
      <ram:ID>urn:cen.eu:en16931:2017</ram:ID>
    </ram:GuidelineSpecifiedDocumentContextParameter>
  </rsm:ExchangedDocumentContext>
  <rsm:ExchangedDocument>
    <ram:ID>${esc(d.number)}</ram:ID>
    <ram:TypeCode>381</ram:TypeCode>
    <ram:IssueDateTime>
      <udt:DateTimeString format="102">${date102(d.issueDate)}</udt:DateTimeString>
    </ram:IssueDateTime>
  </rsm:ExchangedDocument>
  <rsm:SupplyChainTradeTransaction>
    <ram:IncludedSupplyChainTradeLineItem>
      <ram:AssociatedDocumentLineDocument>
        <ram:LineID>1</ram:LineID>
      </ram:AssociatedDocumentLineDocument>
      <ram:SpecifiedTradeProduct>
        <ram:Name>${esc(d.lineName)}</ram:Name>
      </ram:SpecifiedTradeProduct>
      <ram:SpecifiedLineTradeAgreement>
        <ram:NetPriceProductTradePrice>
          <ram:ChargeAmount>${n2(d.amountNet)}</ram:ChargeAmount>
        </ram:NetPriceProductTradePrice>
      </ram:SpecifiedLineTradeAgreement>
      <ram:SpecifiedLineTradeDelivery>
        <ram:BilledQuantity unitCode="C62">1</ram:BilledQuantity>
      </ram:SpecifiedLineTradeDelivery>
      <ram:SpecifiedLineTradeSettlement>
${lineTaxCategory}
        <ram:SpecifiedTradeSettlementLineMonetarySummation>
          <ram:LineTotalAmount>${n2(d.amountNet)}</ram:LineTotalAmount>
        </ram:SpecifiedTradeSettlementLineMonetarySummation>
      </ram:SpecifiedLineTradeSettlement>
    </ram:IncludedSupplyChainTradeLineItem>
    <ram:ApplicableHeaderTradeAgreement>
      <ram:SellerTradeParty>
        <ram:Name>${esc(d.seller.name)}</ram:Name>
${addressBlock(d.seller)}
${sellerTax.join('\n')}
      </ram:SellerTradeParty>
      <ram:BuyerTradeParty>
        <ram:Name>${esc(d.buyer.name)}</ram:Name>
${addressBlock(d.buyer)}${buyerTax ? `\n${buyerTax}` : ''}
      </ram:BuyerTradeParty>
    </ram:ApplicableHeaderTradeAgreement>
    <ram:ApplicableHeaderTradeDelivery/>
    <ram:ApplicableHeaderTradeSettlement>
      <ram:InvoiceCurrencyCode>${esc(d.currency)}</ram:InvoiceCurrencyCode>
${headerTax}
      <ram:SpecifiedTradeSettlementHeaderMonetarySummation>
        <ram:LineTotalAmount>${n2(d.amountNet)}</ram:LineTotalAmount>
        <ram:TaxBasisTotalAmount>${n2(d.amountNet)}</ram:TaxBasisTotalAmount>
        <ram:TaxTotalAmount currencyID="${esc(d.currency)}">${n2(d.amountVat)}</ram:TaxTotalAmount>
        <ram:GrandTotalAmount>${n2(d.amountGross)}</ram:GrandTotalAmount>
        <ram:DuePayableAmount>${n2(d.amountGross)}</ram:DuePayableAmount>
      </ram:SpecifiedTradeSettlementHeaderMonetarySummation>
    </ram:ApplicableHeaderTradeSettlement>
  </rsm:SupplyChainTradeTransaction>
</rsm:CrossIndustryInvoice>`;
}
