<?php
/**
 * UBL XML Generator - FIXED VERSION
 * Matches the working original code structure
 */

require_once __DIR__ . '/chilkat/chilkat.php';
class UBLGenerator {
    private $config;
    private $xml;
    
    public function __construct($config) {
        $this->config = $config;
    }
    
    /**
     * Generate UBL XML (base version before signing)
     */
    public function generateUBL($invoiceData) {
        $this->xml = new CkXml();
        
        $this->xml->put_Tag('Invoice');
        $this->xml->put_EmitXmlDecl(true);
        $this->xml->put_Encoding('utf-8');
        $this->xml->put_Utf8(true);
        
        // Add namespaces
        $this->addNamespaces();
        
        // Add UBL Extensions structure (for signature placeholder)
        $this->addExtensions();
        
        // Add invoice header
        $this->addInvoiceHeader($invoiceData);
        
        // Add document references (ICV and PIH - NO QR YET)
        $this->addDocumentReferences($invoiceData);
        
        // Add signature element placeholder
        $this->addSignatureElement($invoiceData['supplier']);
        
        // Add parties
        $this->addSupplierParty($invoiceData['supplier']);
        $this->addCustomerParty($invoiceData['customer'], $invoiceData['invoice_type']['is_b2c']);
        
        // Add delivery and payment
        $this->addDelivery($invoiceData['issue_date']);
        $this->addPaymentMeans($invoiceData['payment_by']);
        
        // Add tax and monetary totals
        $this->addTaxTotal($invoiceData['totals']);
        $this->addLegalMonetaryTotal($invoiceData['totals']);
        
        // Add invoice lines
        $this->addInvoiceLines($invoiceData['items']);
        
        return $this->xml->getXml();
    }
    
    private function addNamespaces() {
        $this->xml->AddAttribute('xmlns', 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2');
        $this->xml->AddAttribute('xmlns:cac', 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2');
        $this->xml->AddAttribute('xmlns:cbc', 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2');
        $this->xml->AddAttribute('xmlns:ext', 'urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2');
    }
    
    private function addExtensions() {
        // Extension URI
        $this->xml->UpdateChildContent('ext:UBLExtensions|ext:UBLExtension|ext:ExtensionURI', 
            'urn:oasis:names:specification:ubl:dsig:enveloped:xades');
        
        // Extension Content with signature information structure
        $this->xml->UpdateAttrAt('ext:UBLExtensions|ext:UBLExtension|ext:ExtensionContent|sig:UBLDocumentSignatures', 
            true, 'xmlns:sac', 'urn:oasis:names:specification:ubl:schema:xsd:SignatureAggregateComponents-2');
        $this->xml->UpdateAttrAt('ext:UBLExtensions|ext:UBLExtension|ext:ExtensionContent|sig:UBLDocumentSignatures', 
            true, 'xmlns:sbc', 'urn:oasis:names:specification:ubl:schema:xsd:SignatureBasicComponents-2');
        $this->xml->UpdateAttrAt('ext:UBLExtensions|ext:UBLExtension|ext:ExtensionContent|sig:UBLDocumentSignatures', 
            true, 'xmlns:sig', 'urn:oasis:names:specification:ubl:schema:xsd:CommonSignatureComponents-2');
        
        $this->xml->UpdateChildContent('ext:UBLExtensions|ext:UBLExtension|ext:ExtensionContent|sig:UBLDocumentSignatures|sac:SignatureInformation|cbc:ID', 
            'urn:oasis:names:specification:ubl:signature:1');
        $this->xml->UpdateChildContent('ext:UBLExtensions|ext:UBLExtension|ext:ExtensionContent|sig:UBLDocumentSignatures|sac:SignatureInformation|sbc:ReferencedSignatureID', 
            'urn:oasis:names:specification:ubl:signature:Invoice');
        
        // Signature will be inserted here by the signer
    }
    
    private function addInvoiceHeader($invoiceData) {
        $this->xml->UpdateChildContent('cbc:ProfileID', 'reporting:1.0');
        $this->xml->UpdateChildContent('cbc:ID', $invoiceData['invoice_number']);
        $this->xml->UpdateChildContent('cbc:UUID', $invoiceData['uuid']);
        
        // Parse date/time
        $datetime = new DateTime($invoiceData['issue_date']);
        $this->xml->UpdateChildContent('cbc:IssueDate', $datetime->format('Y-m-d'));
        $this->xml->UpdateChildContent('cbc:IssueTime', $datetime->format('H:i:s'));
        
        // Invoice type code
        $this->xml->UpdateAttrAt('cbc:InvoiceTypeCode', true, 'name', $invoiceData['invoice_type']['invoice_type_xml_code']);
        $this->xml->UpdateChildContent('cbc:InvoiceTypeCode', $invoiceData['invoice_type']['invoice_type_code']);
        
        // Currency
        $this->xml->UpdateChildContent('cbc:DocumentCurrencyCode', $this->config['currency']);
        $this->xml->UpdateChildContent('cbc:TaxCurrencyCode', $this->config['currency']);
    }
    
    /**
     * Add document references - ICV and PIH (QR added later)
     */
    private function addDocumentReferences($invoiceData) {
        // ICV (Invoice Counter Value)
        $icv = $invoiceData['icv_counter'] ?? 1;
        $this->xml->UpdateChildContent('cac:AdditionalDocumentReference|cbc:ID', 'ICV');
        $this->xml->UpdateChildContent('cac:AdditionalDocumentReference|cbc:UUID', $icv);
        
        // PIH (Previous Invoice Hash)
        $this->xml->UpdateChildContent('cac:AdditionalDocumentReference[1]|cbc:ID', 'PIH');
        $this->xml->UpdateAttrAt('cac:AdditionalDocumentReference[1]|cac:Attachment|cbc:EmbeddedDocumentBinaryObject', 
            true, 'mimeCode', 'text/plain');
        $this->xml->UpdateChildContent('cac:AdditionalDocumentReference[1]|cac:Attachment|cbc:EmbeddedDocumentBinaryObject', 
            $invoiceData['pih']);
        
        // QR will be added after signing as AdditionalDocumentReference[2]
    }
    
    private function addSignatureElement($supplier) {
        $this->xml->UpdateChildContent('cac:Signature|cbc:ID', 'urn:oasis:names:specification:ubl:signature:Invoice');
        $this->xml->UpdateChildContent('cac:Signature|cbc:SignatureMethod', 'urn:oasis:names:specification:ubl:dsig:enveloped:xades');
    }
    
    private function addSupplierParty($supplier) {
        $this->xml->UpdateAttrAt('cac:AccountingSupplierParty|cac:Party|cac:PartyIdentification|cbc:ID', 
            true, 'schemeID', 'CRN');
        $this->xml->UpdateChildContent('cac:AccountingSupplierParty|cac:Party|cac:PartyIdentification|cbc:ID', 
            $supplier['crn']);
        
        // Address
        $this->xml->UpdateChildContent('cac:AccountingSupplierParty|cac:Party|cac:PostalAddress|cbc:StreetName', 
            $supplier['address']['street']);
        $this->xml->UpdateChildContent('cac:AccountingSupplierParty|cac:Party|cac:PostalAddress|cbc:BuildingNumber', 
            $supplier['address']['building_no']);
        $this->xml->UpdateChildContent('cac:AccountingSupplierParty|cac:Party|cac:PostalAddress|cbc:PlotIdentification', 
            $supplier['address']['plot_id'] ?? '');
        $this->xml->UpdateChildContent('cac:AccountingSupplierParty|cac:Party|cac:PostalAddress|cbc:CitySubdivisionName', 
            'District');
        $this->xml->UpdateChildContent('cac:AccountingSupplierParty|cac:Party|cac:PostalAddress|cbc:CityName', 
            $supplier['address']['city']);
        $this->xml->UpdateChildContent('cac:AccountingSupplierParty|cac:Party|cac:PostalAddress|cbc:PostalZone', 
            $supplier['address']['postal_code']);
        $this->xml->UpdateChildContent('cac:AccountingSupplierParty|cac:Party|cac:PostalAddress|cbc:CountrySubentity', 
            'Riyadh Province');
        $this->xml->UpdateChildContent('cac:AccountingSupplierParty|cac:Party|cac:PostalAddress|cac:Country|cbc:IdentificationCode', 
            $supplier['address']['country']);
        
        // VAT
        $this->xml->UpdateChildContent('cac:AccountingSupplierParty|cac:Party|cac:PartyTaxScheme|cbc:CompanyID', 
            $supplier['vat_number']);
        $this->xml->UpdateChildContent('cac:AccountingSupplierParty|cac:Party|cac:PartyTaxScheme|cac:TaxScheme|cbc:ID', 
            'VAT');
        
        // Legal entity
        $this->xml->UpdateChildContent('cac:AccountingSupplierParty|cac:Party|cac:PartyLegalEntity|cbc:RegistrationName', 
            $supplier['name']);
    }
    
    private function addCustomerParty($customer, $isB2C) {
        if ($isB2C) {
            // B2C - Minimal info with dots
            $this->xml->UpdateChildContent('cac:AccountingCustomerParty|cac:Party|cac:PostalAddress|cbc:StreetName', '.');
            $this->xml->UpdateChildContent('cac:AccountingCustomerParty|cac:Party|cac:PostalAddress|cbc:BuildingNumber', '.');
            $this->xml->UpdateChildContent('cac:AccountingCustomerParty|cac:Party|cac:PostalAddress|cbc:PlotIdentification', '.');
            $this->xml->UpdateChildContent('cac:AccountingCustomerParty|cac:Party|cac:PostalAddress|cbc:CitySubdivisionName', '.');
            $this->xml->UpdateChildContent('cac:AccountingCustomerParty|cac:Party|cac:PostalAddress|cbc:CityName', '.');
            $this->xml->UpdateChildContent('cac:AccountingCustomerParty|cac:Party|cac:PostalAddress|cbc:PostalZone', '.');
            $this->xml->UpdateChildContent('cac:AccountingCustomerParty|cac:Party|cac:PostalAddress|cbc:CountrySubentity', '.');
            $this->xml->UpdateChildContent('cac:AccountingCustomerParty|cac:Party|cac:PostalAddress|cac:Country|cbc:IdentificationCode', 'SA');
            $this->xml->UpdateChildContent('cac:AccountingCustomerParty|cac:Party|cac:PartyTaxScheme|cac:TaxScheme|cbc:ID', 'VAT');
            $this->xml->UpdateChildContent('cac:AccountingCustomerParty|cac:Party|cac:PartyLegalEntity|cbc:RegistrationName', '.');
        } else {
            // B2B - Full customer details
            // Implement B2B details here
        }
    }
    
    private function addDelivery($issueDate) {
        $datetime = new DateTime($issueDate);
        $this->xml->UpdateChildContent('cac:Delivery|cbc:ActualDeliveryDate', $datetime->format('Y-m-d'));
    }
    
    private function addPaymentMeans($paymentBy) {
        $this->xml->UpdateChildContent('cac:PaymentMeans|cbc:PaymentMeansCode', $paymentBy['payment_method_code']);
        $this->xml->UpdateChildContent('cac:PaymentMeans|cbc:InstructionNote', $paymentBy['payment_method']);
    }
    
    private function addTaxTotal($totals) {
        $currency = $this->config['currency'];
        
        // Header level tax total
        $this->xml->UpdateAttrAt('cac:TaxTotal|cbc:TaxAmount', true, 'currencyID', $currency);
        $this->xml->UpdateChildContent('cac:TaxTotal|cbc:TaxAmount', 
            number_format($totals['vat_total'], 2, '.', ''));
        
        // Tax subtotal
        $this->xml->UpdateAttrAt('cac:TaxTotal|cac:TaxSubtotal|cbc:TaxableAmount', true, 'currencyID', $currency);
        $this->xml->UpdateChildContent('cac:TaxTotal|cac:TaxSubtotal|cbc:TaxableAmount', 
            number_format($totals['net_amount'], 2, '.', ''));
        
        $this->xml->UpdateAttrAt('cac:TaxTotal|cac:TaxSubtotal|cbc:TaxAmount', true, 'currencyID', $currency);
        $this->xml->UpdateChildContent('cac:TaxTotal|cac:TaxSubtotal|cbc:TaxAmount', 
            number_format($totals['vat_total'], 2, '.', ''));
        
        $this->xml->UpdateChildContent('cac:TaxTotal|cac:TaxSubtotal|cac:TaxCategory|cbc:ID', 'S');
        $this->xml->UpdateChildContent('cac:TaxTotal|cac:TaxSubtotal|cac:TaxCategory|cbc:Percent', '15.00');
        $this->xml->UpdateChildContent('cac:TaxTotal|cac:TaxSubtotal|cac:TaxCategory|cac:TaxScheme|cbc:ID', 'VAT');
        
        // Second tax total for tax currency (required by ZATCA)
        $this->xml->UpdateAttrAt('cac:TaxTotal[1]|cbc:TaxAmount', true, 'currencyID', $currency);
        $this->xml->UpdateChildContent('cac:TaxTotal[1]|cbc:TaxAmount', 
            number_format($totals['vat_total'], 2, '.', ''));
    }
    
    private function addLegalMonetaryTotal($totals) {
        $currency = $this->config['currency'];
        
        $this->xml->UpdateAttrAt('cac:LegalMonetaryTotal|cbc:LineExtensionAmount', true, 'currencyID', $currency);
        $this->xml->UpdateChildContent('cac:LegalMonetaryTotal|cbc:LineExtensionAmount', 
            number_format($totals['net_amount'], 2, '.', ''));
        
        $this->xml->UpdateAttrAt('cac:LegalMonetaryTotal|cbc:TaxExclusiveAmount', true, 'currencyID', $currency);
        $this->xml->UpdateChildContent('cac:LegalMonetaryTotal|cbc:TaxExclusiveAmount', 
            number_format($totals['net_amount'], 2, '.', ''));
        
        $this->xml->UpdateAttrAt('cac:LegalMonetaryTotal|cbc:TaxInclusiveAmount', true, 'currencyID', $currency);
        $this->xml->UpdateChildContent('cac:LegalMonetaryTotal|cbc:TaxInclusiveAmount', 
            number_format($totals['grand_total'], 2, '.', ''));
        
        $this->xml->UpdateAttrAt('cac:LegalMonetaryTotal|cbc:PayableAmount', true, 'currencyID', $currency);
        $this->xml->UpdateChildContent('cac:LegalMonetaryTotal|cbc:PayableAmount', 
            number_format($totals['grand_total'], 2, '.', ''));
    }
    
    private function addInvoiceLines($items) {
        $currency = $this->config['currency'];
        $lineIndex = 0;
        
        foreach ($items as $item) {
            $lineTotal = round($item['qty'] * $item['price'], 2);
            $vatAmount = round($lineTotal * ($item['vat'] / 100), 2);
            $lineWithVat = $lineTotal + $vatAmount;
            
            $this->xml->UpdateChildContent("cac:InvoiceLine[$lineIndex]|cbc:ID", $lineIndex + 1);
            
            $this->xml->UpdateAttrAt("cac:InvoiceLine[$lineIndex]|cbc:InvoicedQuantity", 
                true, 'unitCode', $item['uom']);
            $this->xml->UpdateChildContent("cac:InvoiceLine[$lineIndex]|cbc:InvoicedQuantity", $item['qty']);
            
            $this->xml->UpdateAttrAt("cac:InvoiceLine[$lineIndex]|cbc:LineExtensionAmount", 
                true, 'currencyID', $currency);
            $this->xml->UpdateChildContent("cac:InvoiceLine[$lineIndex]|cbc:LineExtensionAmount", 
                number_format($lineTotal, 2, '.', ''));
            
            // Line tax total
            $this->xml->UpdateAttrAt("cac:InvoiceLine[$lineIndex]|cac:TaxTotal|cbc:TaxAmount", 
                true, 'currencyID', $currency);
            $this->xml->UpdateChildContent("cac:InvoiceLine[$lineIndex]|cac:TaxTotal|cbc:TaxAmount", 
                number_format($vatAmount, 2, '.', ''));
            
            $this->xml->UpdateAttrAt("cac:InvoiceLine[$lineIndex]|cac:TaxTotal|cbc:RoundingAmount", 
                true, 'currencyID', $currency);
            $this->xml->UpdateChildContent("cac:InvoiceLine[$lineIndex]|cac:TaxTotal|cbc:RoundingAmount", 
                number_format($lineWithVat, 2, '.', ''));
            
            // Item details
            $this->xml->UpdateChildContent("cac:InvoiceLine[$lineIndex]|cac:Item|cbc:Name", $item['name']);
            $this->xml->UpdateChildContent("cac:InvoiceLine[$lineIndex]|cac:Item|cac:ClassifiedTaxCategory|cbc:ID", 'S');
            $this->xml->UpdateChildContent("cac:InvoiceLine[$lineIndex]|cac:Item|cac:ClassifiedTaxCategory|cbc:Percent", 
                number_format($item['vat'], 2, '.', ''));
            $this->xml->UpdateChildContent("cac:InvoiceLine[$lineIndex]|cac:Item|cac:ClassifiedTaxCategory|cac:TaxScheme|cbc:ID", 
                'VAT');
            
            // Price
            $this->xml->UpdateAttrAt("cac:InvoiceLine[$lineIndex]|cac:Price|cbc:PriceAmount", 
                true, 'currencyID', $currency);
            $this->xml->UpdateChildContent("cac:InvoiceLine[$lineIndex]|cac:Price|cbc:PriceAmount", 
                number_format($item['price'], 2, '.', ''));
            
            $lineIndex++;
        }
    }
}
?>