<?php defined('BASEPATH') OR exit('No direct script access allowed');

class Evatsa extends BaseEinvoice implements EinvoiceInterface
{
	public function __construct($manifest)
	{
		//print_r($manifest);exit;
		parent::__construct($manifest);
	}

	public function validate($data)
	{
	}

	public function post($data)
	{
		$transId = $data["id"];

		$this->validate($data);
		$config = $this->config;
		$formattedData = $this->format($data,$config);
		$perviousHash = $formattedData['invoice']['previous_hash'];
		$flow = $formattedData['flow'];
	//	print_R($flow);exit;
		$urlSuffix = ($flow === "CLEARANCE")
       ? "clearance/single"
       : "reporting/single";

		$savexml= ($flow === "CLEARANCE")
		?  $this->buildZatcaB2BPayload($formattedData,$config):$this->buildZatcaB2CPayload($formattedData,$config);

	//print_R($savexml);exit;
		$result = $this->call_request( $urlSuffix, $savexml,$flow);
		if ($result && !empty($result["response"]))
		{
			// $result["status"]
			// $result["response"]
			//print_r($result);exit;
			$resp = $result["response"];
			//print_r($resp);exit;
			if (!empty($resp["response"]))
			{
				$apiResp = $resp["response"];
			// /	$perviousHash= ;

				if ($apiResp["dispositionStatus"] == "ACCEPTED")
				{
					require_once(APPPATH . "third_party/einvoice/TransIrnInfo.php");

					// Url of online verficiation link
					// $qrContent = $apiResp["qr_code"];

					$info = array(
						"S_IRN" => new TransIrnInfo(array(
							"SignedInvoice" => $qrContent,
							"SignedQRCode" => $qrContent
						)),
						"rawResp" => $apiResp
					);
					$saveData = array(
						"id" => $data["id"],
						"cId" => $data["cId"],
						"info" => $info
					);

					$this->save($saveData);
					// $qr_image = $this->get_qr($apiResp["qr_code"]);
					return $info;
				}
			}
			else
			{
				$msg = "E-Vat error : ";
				$msg .= empty($resp["code"]) ? "" : ($resp["code"] . " - ");
				$msg .= empty($resp["message"]) ? "" : $resp["message"];
				$msg .= empty($resp["errors"]) ? "" : implode(PHP_EOL, $resp["errors"]);

				throw new Exception($msg);
			}
		}
		else
		{
			return false;
		}
	}

	public function cancel($invoiceId)
	{
		
	}

	public function get_status($invoiceId)
	{
		
	}

	public function get_details($invoiceId)
	{
		
	}

	public function get_taxNo_details($taxNo="")
	{
		try {

			$result = $this->call_request("/single" , );

			if ($result && !empty($result["data"]))
			{
				$taxNoContact = $result["data"];

				$taxNoContact["name"];
				$taxNoContact["address"];
			}
		} catch (Exception $e) {
			$this->message = $e->getMessage();
			return false;
		}
	}

	private function call_request($urlSuffix, $payload,$flow)
	{
		$manifest = $this->manifest;

		$config = $this->config;

		if (empty($config["cr_number"]))
		{
			throw new Exception("Commercial Registration Number required.");
			return false;
		}  
		if (empty($config["seller_name_ar"]))
		{
			throw new Exception("Seller Name in Arabic required.");
			return false;
		}  
		if (empty($config["csid"]))
		{
			throw new Exception("ZATCA CSID required.");
			return false;
		}  
		if (empty($config["secret"]))
		{
			throw new Exception("ZATCA Secret required.");
			return false;
		}  
		
		$url = $manifest["api_url"]['production'] . $urlSuffix;
      //  $url ='https://gw-fatoora.zatca.gov.sa/e-invoicing/sandbox/compliance/csids';

		$headers = [
			"Content-Type: application/json",
			"Accept: application/json",
			 "Accept-Version: V2",
			 "Authorization: Basic " . base64_encode($config["csid"] . ":" . $config["secret"])
		];
		
		// Add ONLY if B2B clearance
		if ($flow==="CLEARANCE") {
			$headers[] = "Clearance-Status: 1";
		}
		
//  print_R($url);
	//  print_R($payload );
	//  print_R($headers); exit;

		return $this->send_request1($url, $payload, $headers);
	}

	private function send_request1(string $url, array $payload, array $headers = [])
{
    $ch = curl_init();

	$json = json_encode($payload, JSON_UNESCAPED_SLASHES);

    if ($json === false) {
        throw new Exception('JSON encode failed: ' . json_last_error_msg());
    }
    // print_R($headers);exit;
 
    curl_setopt_array($ch, [
        CURLOPT_URL            => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST           => true,
        CURLOPT_HTTPHEADER     => $headers,
        CURLOPT_POSTFIELDS     => json_encode($payload, JSON_UNESCAPED_SLASHES),
        CURLOPT_CONNECTTIMEOUT => 30,
        CURLOPT_TIMEOUT        => 60,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_SSL_VERIFYHOST => 2,
    ]);
   
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    
    if ($response === false) {
        $error = curl_error($ch);
        curl_close($ch);

        return [
            "success" => false,
            "error"   => "cURL Error: " . $error,
            "code"    => null,
        ];
    }

    curl_close($ch);
    return [
        "success"   => ($httpCode >= 200 && $httpCode < 300),
        "http_code" => $httpCode,
        "response"  => json_decode($response, true) ?? $response,
        "raw"       => $response
    ];
}



	// private function saveXml($data){
	// 			/* =========================
	// 	* NAMESPACES
	// 	* ========================= */
	// 	define('INV_NS', 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2');
	// 	define('CBC_NS', 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2');
	// 	define('CAC_NS', 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2');

	// 	/* =========================
	// 	* CREATE XML
	// 	* ========================= */
	// 	$dom = new DOMDocument('1.0', 'UTF-8');
	// 	$dom->formatOutput = true;

	// 	/* Root Invoice */
	// 	$invoice = $dom->createElementNS(INV_NS, 'Invoice');
	// 	$invoice->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:cbc', CBC_NS);
	// 	$invoice->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:cac', CAC_NS);
	// 	$dom->appendChild($invoice);

	// 	/* =========================
	// 	* INVOICE HEADER
	// 	* ========================= */
	// 	$invoice->appendChild(
	// 		$dom->createElementNS(CBC_NS, 'cbc:ID', $data['invoice']['invoice_number'])
	// 	);

	// 	$invoice->appendChild(
	// 		$dom->createElementNS(CBC_NS, 'cbc:IssueDate', $data['invoice']['issue_date'])
	// 	);

	// 	$invoice->appendChild(
	// 		$dom->createElementNS(CBC_NS, 'cbc:IssueTime', $data['invoice']['issue_time'])
	// 	);

	// 	$invoice->appendChild(
	// 		$dom->createElementNS(CBC_NS, 'cbc:DocumentCurrencyCode', $data['invoice']['currency'])
	// 	);

	// 	/* =========================
	// 	* SUPPLIER (SELLER)
	// 	* ========================= */
	// 	$supplier = $dom->createElementNS(CAC_NS, 'cac:AccountingSupplierParty');
	// 	$party = $dom->createElementNS(CAC_NS, 'cac:Party');

	// 	$partyName = $dom->createElementNS(CAC_NS, 'cac:PartyName');
	// 	$partyName->appendChild(
	// 		$dom->createElementNS(CBC_NS, 'cbc:Name', $data['company']['seller_name'])
	// 	);

	// 	$party->appendChild($partyName);

	// 	$taxScheme = $dom->createElementNS(CAC_NS, 'cac:PartyTaxScheme');
	// 	$taxScheme->appendChild(
	// 		$dom->createElementNS(CBC_NS, 'cbc:CompanyID', $data['company']['vat_number'])
	// 	);

	// 	$party->appendChild($taxScheme);
	// 	$supplier->appendChild($party);
	// 	$invoice->appendChild($supplier);

	// 	/* =========================
	// 	* INVOICE LINES
	// 	* ========================= */
	// 	$lineId = 1;

	// 	foreach ($data['items'] as $item) {
	// 		$line = $dom->createElementNS(CAC_NS, 'cac:InvoiceLine');

	// 		$line->appendChild(
	// 			$dom->createElementNS(CBC_NS, 'cbc:ID', $lineId++)
	// 		);

	// 		$line->appendChild(
	// 			$dom->createElementNS(CBC_NS, 'cbc:InvoicedQuantity', $item['quantity'])
	// 		);

	// 		$line->appendChild(
	// 			$dom->createElementNS(CBC_NS, 'cbc:LineExtensionAmount',
	// 				number_format($item['quantity'] * $item['unit_price'], 2, '.', '')
	// 			)
	// 		);

	// 		$itemNode = $dom->createElementNS(CAC_NS, 'cac:Item');
	// 		$itemNode->appendChild(
	// 			$dom->createElementNS(CBC_NS, 'cbc:Name', $item['description'])
	// 		);

	// 		$price = $dom->createElementNS(CAC_NS, 'cac:Price');
	// 		$price->appendChild(
	// 			$dom->createElementNS(CBC_NS, 'cbc:PriceAmount', number_format($item['unit_price'], 2, '.', ''))
	// 		);

	// 		$line->appendChild($itemNode);
	// 		$line->appendChild($price);

	// 		$invoice->appendChild($line);
	// 	}

	// 	/* =========================
	// 	* TOTALS
	// 	* ========================= */
	// 	$monetaryTotal = $dom->createElementNS(CAC_NS, 'cac:LegalMonetaryTotal');

	// 	$monetaryTotal->appendChild(
	// 		$dom->createElementNS(CBC_NS, 'cbc:TaxExclusiveAmount', $data['totals']['total_exclusive_vat'])
	// 	);

	// 	$monetaryTotal->appendChild(
	// 		$dom->createElementNS(CBC_NS, 'cbc:TaxInclusiveAmount', $data['totals']['payable_amount'])
	// 	);

	// 	$monetaryTotal->appendChild(
	// 		$dom->createElementNS(CBC_NS, 'cbc:PayableAmount', $data['totals']['payable_amount'])
	// 	);

	// 	$invoice->appendChild($monetaryTotal);


	// 	/* =========================
	// 	* FINAL XML
	// 	* ========================= */
	// 	$xml = $dom->saveXML();

	// 	$invoiceHash = base64_encode(hash('sha256', $xml, true));
	// 	$invoiceBase64 = base64_encode($xml);
	// 	$uuid   = $data['invoice']['uuid'];   

	// 	$payload = [
	// 		"invoiceHash" => $invoiceHash,
	// 		"uuid"        => $uuid,
	// 		"invoice"     => $invoiceBase64
	// 	];

	// 	return $payload;


	// }


	private function buildZatcaB2CPayload(array $return, $config)
{
    // Enforce B2C rules
    $return["customer"] = [];

    // Generate QR (B2C mandatory)
    $qrBase64 = $this->generateZatcaQrBase64(
        $return["company"]["seller_name_ar"] ?: $return["company"]["seller_name"],
        $return["company"]["vat_number"],
        $return["invoice"]["issue_date"] . "T" . $return["invoice"]["issue_time"] . "Z",
        $return["totals"]["total_inclusive_vat"],
        $return["totals"]["total_vat"]
    );

	$uuid = $this->uuid_v4();
    $return['invoice']['uuid'] = $uuid;

    // Build invoice ONCE
	//$certificateDerBase64 = 'MIIB8TCCAZegAwIBAgI...'; 
    $invoiceData = $this->buildZatcaInvoice($return, $config,$qrBase64);
    $dom = $invoiceData['dom'];

    // Canonical XML (before signing)
    $canonicalXml = $dom->C14N(true, false);

    // Correct invoice hash
    $invoiceHash = base64_encode(
        hash('sha256', $canonicalXml, true)
    );

    // Final XML
    $xml = $dom->saveXML();

    return [
        "uuid"        => $uuid,
        "icv"         => 1,
        "invoiceHash" => $invoiceHash,         
        "invoice"     => base64_encode($xml)    
    ];
	  
}


private function buildZatcaB2BPayload(array $return,$config )
{
    // Validate customer (MANDATORY) 
    if (empty($return["customer"]["vat_number"])) {
        throw new Exception("B2B invoice requires customer VAT number");
    }

    // Build XML (QR optional for B2B → not included)
	$uuid = $this->uuid_v4();

	$return['invoice']['uuid'] = $uuid; 
	//$certificateDerBase64 = 'MIIB8TCCAZegAwIBAgI...'; 
	 
    $xml = $this->buildZatcaInvoice($return,$config);

    return [
        "invoiceHash" => base64_encode(hash("sha256", $xml['xml'], true)),
        "uuid"        => $uuid,
        "invoice"     => base64_encode($xml['xml'])
    ];
}


function uuid_v4(): string
{
    $data = random_bytes(16);

    // Set version to 0100
    $data[6] = chr((ord($data[6]) & 0x0f) | 0x40);
    // Set variant to 10
    $data[8] = chr((ord($data[8]) & 0x3f) | 0x80);

    return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}

private function buildZatcaInvoice(array $data,array $config, $qr_base64 = ""):array
{
	 $certPem = $config['certi'];

	$certDerBase64 = trim(
		str_replace(
			["-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----", "\r", "\n"],
			"",
			$certPem
		)
	);
	$certificateDerBase64  = $certDerBase64;
// cert is already Base64 DER
// $raw = trim($config['certi']);

// // 1️⃣ Remove anything that is NOT Base64 or PEM markers
// $raw = preg_replace('/[^\x20-\x7E]/', '', $raw);

// // 2️⃣ If PEM markers exist, extract Base64 safely
// if (strpos($raw, 'BEGIN CERTIFICATE') !== false) {
//     $base64 = preg_replace(
//         '/-----BEGIN CERTIFICATE-----|-----END CERTIFICATE-----|\s+/',
//         '',
//         $raw
//     );
// } else {
//     // Already Base64 DER
//     $base64 = preg_replace('/\s+/', '', $raw);
// }

// // 3️⃣ Strict Base64 validation (this MUST pass)
// if (base64_decode($base64, true) === false) {
//     die('❌ Certificate Base64 is irreparably INVALID');
// }

// // 4️⃣ Rebuild CLEAN PEM
// $certPem =
//     "-----BEGIN CERTIFICATE-----\n" .
//     chunk_split($base64, 64, "\n") .
//     "-----END CERTIFICATE-----\n";

// // 5️⃣ Final sanity check (this MUST succeed)
// if (openssl_x509_parse($certPem) === false) {
//     while ($e = openssl_error_string()) {
//         echo $e, PHP_EOL;
//     }
//     exit;
// }

// echo "✅ Certificate is now valid PEM";





    // 	$privateKeyPem = <<<PEM
    // -----BEGIN PRIVATE KEY-----
    // MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgYkRk
    // c2pRZXF1ZUZJY1h5TzFQaERHV2xRMnNjZ3RZQ2x1NkNDb0NqRk5H
    // ... (FULL base64, many lines, no placeholders)
    // -----END PRIVATE KEY-----
    // PEM;

    
    $privateKeyPem=$config['pem'];
   // var_dump($privateKeyPem);
   $keyString = "-----BEGIN PRIVATE KEY-----\n" .
   "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDJm...\n" .
   "..." . // Ensure actual data lines are here
   "\n-----END PRIVATE KEY-----";

//     // Extract only the base64 part, clean it, and rebuild the PEM format
//     $lines = explode("\n", str_replace(["\r", "\t"], ["\n", ""], trim($keyString)));
//     $base64Data = "";
//     foreach ($lines as $line) {
//         if (strpos($line, '---') === false) {
//             $base64Data .= trim($line);
//         }
//     }

//     // Re-wrap to exactly 64 characters per line (standard PEM requirement)
//     $formattedKey = "-----BEGIN PRIVATE KEY-----\n" . 
//                     wordwrap($base64Data, 64, "\n", true) . 
//                     "\n-----END PRIVATE KEY-----";
//                     $cleanKey = trim($formattedKey);
//     $privateKey = openssl_pkey_get_private($cleanKey);



// 	if (!$privateKey) {
// 		throw new Exception('Invalid EC private key');
// 	}

// 1. Remove all non-base64 characters from the inner content
// 1. Remove ONLY the header/footer lines first to isolate the data
//$privateKeyPem = file_get_contents(__DIR__ . '/private.pem');


// $config1 = [
//     "private_key_type" => $config['pem'],
//     "curve_name"       => "prime256v1", // ✅ ZATCA compliant
// ];

// $key = openssl_pkey_new($config1);

// if ($key === false) {
//     throw new Exception(openssl_error_string());
// }

$key= file_get_contents('/home/administrator/sandbox_private_key.pem');
$certPem  = file_get_contents('/home/administrator/sandbox_certificate.pem');

openssl_pkey_export($key, $pem);

$privateKey = openssl_pkey_get_private($pem);


if ($privateKey === false) {
    throw new Exception(openssl_error_string());
}

//echo "EC key generated successfully\n";


    $INV_NS   = 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2';
    $CBC_NS   = 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2';
    $CAC_NS   = 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2';
    $EXT_NS   = 'urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2';
    $DS_NS    = 'http://www.w3.org/2000/09/xmldsig#';
    $XADES_NS = 'http://uri.etsi.org/01903/v1.3.2#';

    /** @var OpenSSLAsymmetricKey $privateKey */
    /** @var string $certificateDerBase64 */

    $dom = new DOMDocument('1.0', 'UTF-8');
    $dom->formatOutput = false;

    /* ================= ROOT ================= */
    $invoice = $dom->createElementNS($INV_NS, 'Invoice');
    $invoice->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:cbc', $CBC_NS);
    $invoice->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:cac', $CAC_NS);
    $invoice->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:ext', $EXT_NS);
    $invoice->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:ds', $DS_NS);
    $invoice->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xades', $XADES_NS);
    $dom->appendChild($invoice);

    /* ================= UBLExtensions (FIRST) ================= */
    $exts = $dom->createElementNS($EXT_NS, 'ext:UBLExtensions');
    $ext  = $dom->createElementNS($EXT_NS, 'ext:UBLExtension');
    $extContent = $dom->createElementNS($EXT_NS, 'ext:ExtensionContent');

    $signature = $dom->createElementNS($DS_NS, 'ds:Signature');
    $signature->setAttribute('Id', 'signature');

    /* ================= SignedInfo ================= */
    $signedInfo = $dom->createElementNS($DS_NS, 'ds:SignedInfo');

    $canon = $dom->createElementNS($DS_NS, 'ds:CanonicalizationMethod');
    $canon->setAttribute('Algorithm', 'http://www.w3.org/2001/10/xml-exc-c14n#');
    $signedInfo->appendChild($canon);

    $sigMethod = $dom->createElementNS($DS_NS, 'ds:SignatureMethod');
    $sigMethod->setAttribute('Algorithm', 'http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256');
    $signedInfo->appendChild($sigMethod);

    /* ---- Reference: Invoice ---- */
    $refInvoice = $dom->createElementNS($DS_NS, 'ds:Reference');
    $refInvoice->setAttribute('URI', '');

    $transforms = $dom->createElementNS($DS_NS, 'ds:Transforms');
    $transform = $dom->createElementNS($DS_NS, 'ds:Transform');
    $transform->setAttribute('Algorithm', 'http://www.w3.org/2001/10/xml-exc-c14n#');
    $transforms->appendChild($transform);
    $refInvoice->appendChild($transforms);

    $digestMethod = $dom->createElementNS($DS_NS, 'ds:DigestMethod');
    $digestMethod->setAttribute('Algorithm', 'http://www.w3.org/2001/04/xmlenc#sha256');
    $refInvoice->appendChild($digestMethod);

    $invoiceDigestNode = $dom->createElementNS($DS_NS, 'ds:DigestValue');
    $refInvoice->appendChild($invoiceDigestNode);

    $signedInfo->appendChild($refInvoice);

    /* ---- Reference: SignedProperties ---- */
    $refProps = $dom->createElementNS($DS_NS, 'ds:Reference');
    $refProps->setAttribute('URI', '#SignedProperties');
    $refProps->setAttribute('Type', 'http://uri.etsi.org/01903#SignedProperties');

    $digestMethod2 = $dom->createElementNS($DS_NS, 'ds:DigestMethod');
    $digestMethod2->setAttribute('Algorithm', 'http://www.w3.org/2001/04/xmlenc#sha256');
    $refProps->appendChild($digestMethod2);

    $signedPropsDigestNode = $dom->createElementNS($DS_NS, 'ds:DigestValue');
    $refProps->appendChild($signedPropsDigestNode);

    $signedInfo->appendChild($refProps);

    $signature->appendChild($signedInfo);

    $signatureValueNode = $dom->createElementNS(
        $DS_NS,
        'ds:SignatureValue',
        'REPLACE_SIGNATURE_VALUE'
    );
    $signature->appendChild($signatureValueNode);

    /* ================= KeyInfo ================= */
    $keyInfo = $dom->createElementNS($DS_NS, 'ds:KeyInfo');
    $x509Data = $dom->createElementNS($DS_NS, 'ds:X509Data');
    $x509Cert = $dom->createElementNS($DS_NS, 'ds:X509Certificate', $certificateDerBase64);
    $x509Data->appendChild($x509Cert);
    $keyInfo->appendChild($x509Data);
    $signature->appendChild($keyInfo);

    /* ================= XAdES ================= */
    $object = $dom->createElementNS($DS_NS, 'ds:Object');

    $qualifyingProps = $dom->createElementNS($XADES_NS, 'xades:QualifyingProperties');
    $qualifyingProps->setAttribute('Target', '#signature');

    $signedProps = $dom->createElementNS($XADES_NS, 'xades:SignedProperties');
    $signedProps->setAttribute('Id', 'SignedProperties');

    $signedSigProps = $dom->createElementNS($XADES_NS, 'xades:SignedSignatureProperties');
    $signedSigProps->appendChild(
        $dom->createElementNS(
            $XADES_NS,
            'xades:SigningTime',
            gmdate('Y-m-d\TH:i:s\Z')
        )
    );

    $signedProps->appendChild($signedSigProps);
    $qualifyingProps->appendChild($signedProps);
    $object->appendChild($qualifyingProps);
    $signature->appendChild($object);

    $extContent->appendChild($signature);
    $ext->appendChild($extContent);
    $exts->appendChild($ext);
    $invoice->appendChild($exts);

    /* HEADER */
    $invoice->appendChild($dom->createElementNS($CBC_NS, 'cbc:ID', $data["invoice"]['invoice_number']));
    $invoice->appendChild($dom->createElementNS($CBC_NS, 'cbc:UUID', $data["invoice"]['uuid']));
	$invoiceTypeCode = $dom->createElementNS(
		$CBC_NS,
		'cbc:InvoiceTypeCode',
		$data['flow']==="CLEARANCE"?380:388
	);
	
	$invoiceTypeCode->setAttribute('listID', 'UN/ECE 1001');
	$invoiceTypeCode->setAttribute('name', $data['flow']==="CLEARANCE"?'0100000':'0200000'); // B2B
	
	$invoice->appendChild($invoiceTypeCode);
	
    $invoice->appendChild($dom->createElementNS($CBC_NS, 'cbc:IssueDate', $data["invoice"]['issue_date']));
    $invoice->appendChild($dom->createElementNS($CBC_NS, 'cbc:IssueTime', $data["invoice"]['issue_time']));
    $invoice->appendChild($dom->createElementNS($CBC_NS, 'cbc:DocumentCurrencyCode', 'SAR'));

    /* PREVIOUS INVOICE HASH */
    $pih = $dom->createElementNS($CAC_NS, 'cac:AdditionalDocumentReference');
    $pih->appendChild($dom->createElementNS($CBC_NS, 'cbc:ID', 'PIH'));
    $att = $dom->createElementNS($CAC_NS, 'cac:Attachment');
    $att->appendChild(
        $dom->createElementNS(
            $CBC_NS,
            'cbc:EmbeddedDocumentBinaryObject',
            $data['invoice']['previous_hash'] ?? '0'
        )
    );
    $pih->appendChild($att);
    $invoice->appendChild($pih);

    /* SUPPLIER */
    $supplier = $dom->createElementNS($CAC_NS, 'cac:AccountingSupplierParty');
    $sParty = $dom->createElementNS($CAC_NS, 'cac:Party');
	$this->addPostalAddress($dom, $sParty, [
		"street"      => $data["company"]["street"],
		"city"        => $data["company"]["city"],
		"postal_code" => $data["company"]["postal_code"],
		"country"     => $data["company"]["country"] ?? "SA",
	]);
	
    $sPartyName = $dom->createElementNS($CAC_NS, 'cac:PartyName');
    $sPartyName->appendChild($dom->createElementNS($CBC_NS, 'cbc:Name', $data['company']['seller_name']));
    $sParty->appendChild($sPartyName);

    $sTax = $dom->createElementNS($CAC_NS, 'cac:PartyTaxScheme');
    $sTax->appendChild($dom->createElementNS($CBC_NS, 'cbc:CompanyID', $data['company']['vat_number']));
    $sScheme = $dom->createElementNS($CAC_NS, 'cac:TaxScheme');
    $sScheme->appendChild($dom->createElementNS($CBC_NS, 'cbc:ID', 'VAT'));
    $sTax->appendChild($sScheme);
    $sParty->appendChild($sTax);

    $supplier->appendChild($sParty);
    $invoice->appendChild($supplier);


	if ( !empty($data['customer']))
	 {
    /* CUSTOMER (MANDATORY FOR B2B) */
    $customer = $dom->createElementNS($CAC_NS, 'cac:AccountingCustomerParty');
    $cParty = $dom->createElementNS($CAC_NS, 'cac:Party');
	$this->addPostalAddress($dom, $cParty, [
		"street"      => $data["customer"]["street"],
		"city"        => $data["customer"]["city"],
		"postal_code" => $data["customer"]["postal_code"],
		"country"     => $data["customer"]["country"] ?? "SA",
	]);


    $cPartyName = $dom->createElementNS($CAC_NS, 'cac:PartyName');
    $cPartyName->appendChild($dom->createElementNS($CBC_NS, 'cbc:Name', $data['customer']['name']));
    $cParty->appendChild($cPartyName);
    if ($data['flow'] === 'CLEARANCE') {
    $cTax = $dom->createElementNS($CAC_NS, 'cac:PartyTaxScheme');
    $cTax->appendChild($dom->createElementNS($CBC_NS, 'cbc:CompanyID', $data['customer']['vat_number']));
    $cScheme = $dom->createElementNS($CAC_NS, 'cac:TaxScheme');
    $cScheme->appendChild($dom->createElementNS($CBC_NS, 'cbc:ID', 'VAT'));
    $cTax->appendChild($cScheme);
    $cParty->appendChild($cTax);
	}
     
	$customer->appendChild($cParty);
    $invoice->appendChild($customer);

    }
	// ADD QR only for B2C (REPORTING)
    if ($data['flow'] === 'REPORTING') {

    $qr = $dom->createElementNS($CAC_NS, 'cac:AdditionalDocumentReference');
    $qr->appendChild($dom->createElementNS($CBC_NS, 'cbc:ID', 'QR'));

    $att = $dom->createElementNS($CAC_NS, 'cac:Attachment');
    $att->appendChild(
        $dom->createElementNS(
            $CBC_NS,
            'cbc:EmbeddedDocumentBinaryObject',
            $qr_base64
        )
    );

    $qr->appendChild($att);
    $invoice->appendChild($qr);
}
    /* LINES */
    $lineTotal = 0;
    $vatTotal  = 0;
    $id = 1;

    foreach ($data['items'] as $item) {
        $amount = $item['quantity'] * $item['unit_price'];
        $vat    = $amount * ($item['vat_rate'] / 100);

        $lineTotal += $amount;
        $vatTotal  += $vat;

        $line = $dom->createElementNS($CAC_NS, 'cac:InvoiceLine');
        $line->appendChild($dom->createElementNS($CBC_NS, 'cbc:ID', $id++));

        $qty = $dom->createElementNS($CBC_NS, 'cbc:InvoicedQuantity', $item['quantity']);
        $qty->setAttribute('unitCode', 'EA');
        $line->appendChild($qty);

        $line->appendChild(
            $dom->createElementNS($CBC_NS, 'cbc:LineExtensionAmount', number_format($amount, 2, '.', ''))
        );

        $itemNode = $dom->createElementNS($CAC_NS, 'cac:Item');
        $itemNode->appendChild($dom->createElementNS($CBC_NS, 'cbc:Name', $item['description']));

        $taxCat = $dom->createElementNS($CAC_NS, 'cac:ClassifiedTaxCategory');
        $taxCat->appendChild($dom->createElementNS($CBC_NS, 'cbc:ID', $item['vat_category']));
        $taxCat->appendChild($dom->createElementNS($CBC_NS, 'cbc:Percent', $item['vat_rate']));
        $taxScheme = $dom->createElementNS($CAC_NS, 'cac:TaxScheme');
        $taxScheme->appendChild($dom->createElementNS($CBC_NS, 'cbc:ID', 'VAT'));
        $taxCat->appendChild($taxScheme);
        $itemNode->appendChild($taxCat);

        $line->appendChild($itemNode);

        $price = $dom->createElementNS($CAC_NS, 'cac:Price');
        $price->appendChild(
            $dom->createElementNS($CBC_NS, 'cbc:PriceAmount', number_format($item['unit_price'], 2, '.', ''))
        );
        $line->appendChild($price);

        $invoice->appendChild($line);
    }

    /* TAX TOTAL */
    $taxTotal = $dom->createElementNS($CAC_NS, 'cac:TaxTotal');
    $taxTotal->appendChild(
        $dom->createElementNS($CBC_NS, 'cbc:TaxAmount', number_format($data['totals']['total_vat'], 2, '.', ''))
    );
    $invoice->appendChild($taxTotal);

    /* MONETARY TOTAL */
    $total = $dom->createElementNS($CAC_NS, 'cac:LegalMonetaryTotal');
    $total->appendChild($dom->createElementNS($CBC_NS, 'cbc:TaxExclusiveAmount', number_format($data['totals']['total_exclusive_vat'], 2, '.', '')));
    $total->appendChild($dom->createElementNS($CBC_NS, 'cbc:TaxInclusiveAmount', number_format($data['totals']['total_inclusive_vat'], 2, '.', '')));
    $total->appendChild($dom->createElementNS($CBC_NS, 'cbc:PayableAmount', number_format($data['totals']['payable_amount'], 2, '.', '')));
    $invoice->appendChild($total);

    /* ================= FINAL HASH ================= */
    $unsignedDom = $dom->cloneNode(true);
    $xp = new DOMXPath($unsignedDom);
    $xp->registerNamespace('ds', $DS_NS);
    foreach ($xp->query('//ds:Signature') as $s) {
        $s->parentNode->removeChild($s);
    }

    $canonicalInvoice = $unsignedDom->C14N(true, false);
    $invoiceDigest = base64_encode(hash('sha256', $canonicalInvoice, true));
    $invoiceDigestNode->nodeValue = $invoiceDigest;

    /* ================= SIGN SignedInfo ================= */
   /* ================= SIGN SignedInfo (ZATCA FINAL) ================= */

// 1. Canonicalize SignedInfo
$signedInfoCanon = $signedInfo->C14N(true, false);

// 2. Sign → DER (OpenSSL)
openssl_sign(
    $signedInfoCanon,
    $derSig,
    $privateKey,
    OPENSSL_ALGO_SHA256
);

// 3. Convert DER → RAW (64 bytes for XML)
$rawSig = $this->ecdsaDerToRaw($derSig);

// 4. Set SignatureValue (BASE64 RAW)
$signatureValueNode->nodeValue = base64_encode($rawSig);

// 5. (Optional debug verify)




  // Build certificate PEM
if (str_contains($certificateDerBase64, 'BEGIN CERTIFICATE')) {
    $certPem = $certificateDerBase64;
} else {
    if (base64_decode($certificateDerBase64, true) === false) {
        die('Certificate Base64 is INVALID');
    }

    $certPem =
        "-----BEGIN CERTIFICATE-----\n" .
        chunk_split(trim($certificateDerBase64), 64, "\n") .
        "-----END CERTIFICATE-----\n";
}

//var_dump(openssl_x509_check_private_key($certPem, $privateKey));

//file_put_contents('/home/administrator/signature.der', base64_decode($signatureValue));

// Load public key from certificate
// $certPem ='-----BEGIN CERTIFICATE-----
// MIIB8TCCAZegAwIBAgIUHnG0Jzv6gYtM8lE8P7YBv+Y9hXAwCgYI
// KoZIzj0EAwIwGDEWMBQGA1UEAwwNVGVzdCBDZXJ0aWZpY2F0ZTAe
// Fw0yNTAxMDEwMDAwMDBaFw0yNjAxMDEwMDAwMDBaMBgxFjAUBgNV
// BAMMDVRlc3QgQ2VydGlmaWNhdGUwWTATBgcqhkjOPQIBBggqhkjO
// PQMBBwNCAATZ0m0j8K6G+2Qf+0Gx1qKj6w2xg4ZpN0XJrQhWlX9o
// 8g9Y7A2qZp8mY2vZz1Z4Q5p6k0s8Rz5s7k6Zo0IwQDAOBgNVHQ8B
// Af8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwCgYIKoZIzj0E
// AwIDSAAwRQIgZ7sZ2m8+O1pF5oCzR3l8z1zVdKJ5n8U6z0ZKp0cC
// IQD1YHn6GZ9oJ8X1pX6p6Kz0WZ8s2yG2J4K5y9G5Xw==
// -----END CERTIFICATE-----';



// var_dump(openssl_x509_read($certPem));          // must NOT be false
// var_dump(openssl_pkey_get_public($certPem));   // must NOT be false
// exit;
// print_R($certPem);exit;
// $pubKey = openssl_pkey_get_public($certPem);

if ($pubKey === false) {
    throw new Exception(openssl_error_string());
}

// Canonicalize SignedInfo (EXCLUSIVE, no comments)
//$signedInfoCanon = $signedInfo->C14N(true, false);

// Decode SignatureValue
$signatureBinary = base64_decode($signatureValue, true);
if ($signatureBinary === false) {
    die('Invalid SignatureValue base64');
}

$pk = openssl_pkey_get_private($privateKeyPem);
if (!$pk) {
    die("Private key error: " . openssl_error_string());
}
$cert = openssl_x509_read($certPem);
if (!$cert) {
    die("Certificate error: " . openssl_error_string());
}
if (!openssl_x509_check_private_key($certPem, $privateKeyPem)) {
    die("Private key does NOT match certificate");
}

//putenv('RANDFILE=/tmp/.rnd');


// $pubKey = openssl_pkey_get_public($certPem);
// $result = openssl_verify(
//     $signedInfoCanon,
//     $derSig,
//     $pubKey,
//     OPENSSL_ALGO_SHA256
// );

// assert($result === 1);



    return [
        'xml'         => $dom->saveXML(),
        'invoiceHash' => $invoiceDigest,
        'dom'=> $dom
    ];
}

function ecdsaDerToRaw(string $derSig, int $keySize = 32): string
{
    $len = strlen($derSig);
    if ($len < 8) {
        throw new Exception('Invalid DER signature (too short)');
    }

    $pos = 0;

    // Expect SEQUENCE (0x30)
    if (ord($derSig[$pos++]) !== 0x30) {
        throw new Exception('Invalid DER signature (no SEQUENCE)');
    }

    // Read SEQUENCE length
    $seqLen = ord($derSig[$pos++]);
    if ($seqLen & 0x80) {
        $n = $seqLen & 0x7F;
        $seqLen = 0;
        for ($i = 0; $i < $n; $i++) {
            $seqLen = ($seqLen << 8) | ord($derSig[$pos++]);
        }
    }

    // INTEGER r
    if (ord($derSig[$pos++]) !== 0x02) {
        throw new Exception('Invalid DER signature (no INTEGER r)');
    }
    $rLen = ord($derSig[$pos++]);
    $r = substr($derSig, $pos, $rLen);
    $pos += $rLen;

    // INTEGER s
    if (ord($derSig[$pos++]) !== 0x02) {
        throw new Exception('Invalid DER signature (no INTEGER s)');
    }
    $sLen = ord($derSig[$pos++]);
    $s = substr($derSig, $pos, $sLen);

    // Remove sign byte if present
    $r = ltrim($r, "\x00");
    $s = ltrim($s, "\x00");

    // Pad to fixed size (32 bytes for P-256)
    $r = str_pad($r, $keySize, "\x00", STR_PAD_LEFT);
    $s = str_pad($s, $keySize, "\x00", STR_PAD_LEFT);

    return $r . $s;
}





private function generateZatcaQrBase64(
    string $sellerName,
    string $vatNumber,
    string $timestamp,
    float $invoiceTotal,
    float $vatAmount
) {
    $tlv = function ($tag, $value) {
        return chr($tag) . chr(strlen($value)) . $value;
    };

    $data =
        $tlv(1, $sellerName) .
        $tlv(2, $vatNumber) .
        $tlv(3, $timestamp) .
        $tlv(4, number_format($invoiceTotal, 2, '.', '')) .
        $tlv(5, number_format($vatAmount, 2, '.', ''));

    return base64_encode($data);
}

private function addPostalAddress(
    DOMDocument $dom,
    DOMElement $partyNode,
    array $address
) {
    $CBC_NS = 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2';
    $CAC_NS = 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2';

    $postal = $dom->createElementNS($CAC_NS, 'cac:PostalAddress');

    $postal->appendChild(
        $dom->createElementNS($CBC_NS, 'cbc:StreetName', $address['street'])
    );

    $postal->appendChild(
        $dom->createElementNS($CBC_NS, 'cbc:CityName', $address['city'])
    );

    $postal->appendChild(
        $dom->createElementNS($CBC_NS, 'cbc:PostalZone', (string)$address['postal_code'])
    );

    $country = $dom->createElementNS($CAC_NS, 'cac:Country');
    $country->appendChild(
        $dom->createElementNS($CBC_NS, 'cbc:IdentificationCode', $address['country'] ?? 'SA')
    );

    $postal->appendChild($country);
    $partyNode->appendChild($postal);
}



}

