<?php

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class Freefactu_Signer {

    /**
     * Sign the XML content using the provided certificate and password.
     *
     * What this function does:
     * - Reads and validates the .p12 certificate file
     * - Extracts private key and X509 certificate
     * - Canonicalizes the XML (C14N)
     * - Calculates SHA-1 digest of the XML
     * - Creates SignedInfo element with digest
     * - Signs the SignedInfo with private key
     * - Appends Signature element to XML
     * - Returns signed XML or WP_Error on failure
     *
     * Where it's called from:
     * - class-freefactu-client.php: sign_xml() method
     *
     * @param string $xml_content The XML string to sign.
     * @param string $cert_path Path to the .p12 certificate file.
     * @param string $password Password for the certificate.
     * @return string|WP_Error Signed XML string or WP_Error on failure.
     */
    public function sign_xml( $xml_content, $cert_path, $password ) {
        if ( ! file_exists( $cert_path ) ) {
            return new WP_Error( 'cert_not_found', 'Certificate file not found.' );
        }

        $pkcs12 = file_get_contents( $cert_path );
        $certs = array();
        if ( ! openssl_pkcs12_read( $pkcs12, $certs, $password ) ) {
            return new WP_Error( 'cert_read_error', 'Could not read certificate. Check password.' );
        }

        $x509_cert = $certs['cert'];
        $private_key = $certs['pkey'];

        // Parse XML
        $dom = new DOMDocument();
        $dom->preserveWhiteSpace = true;
        $dom->formatOutput = false;
        $dom->loadXML( $xml_content );

        // Canonicalize the XML (C14N)
        $canonical_xml = $dom->C14N();

        // Calculate Digest (SHA-1 is standard for FacturaE/Verifactu, though SHA-256 is preferred if supported)
        // Verifactu specs usually accept SHA-1 for now, but let's try to be modern if possible.
        // However, manual implementation is tricky. Let's stick to a standard structure.

        // We need to sign the whole document or a specific element.
        // Verifactu expects the signature to be enveloped or enveloping.
        // Usually it's an enveloped signature over the main element.

        // Let's assume we are signing the root element.
        // We need to add an ID to the root element if it doesn't have one, or reference it.
        // But typically Verifactu uses an empty URI reference "" meaning the root of the doc.

        $digest_value = base64_encode( sha1( $canonical_xml, true ) );

        // Prepare SignedInfo
        $signed_info_template =
'<ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>' . $digest_value . '</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>';

        // Canonicalize SignedInfo
        $dom_signed_info = new DOMDocument();
        $dom_signed_info->loadXML( $signed_info_template );
        $canonical_signed_info = $dom_signed_info->C14N();

        // Calculate Signature
        $signature_value = '';
        if ( ! openssl_sign( $canonical_signed_info, $signature_value, $private_key, OPENSSL_ALGO_SHA1 ) ) {
             return new WP_Error( 'signing_error', 'OpenSSL signing failed.' );
        }
        $signature_value_b64 = base64_encode( $signature_value );

        // Get X509 Certificate data (strip headers)
        $x509_cert_clean = str_replace( array( '-----BEGIN CERTIFICATE-----', '-----END CERTIFICATE-----', "\n", "\r" ), '', $x509_cert );

        // Construct the Signature Node
        $signature_node_xml = '<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
' . $canonical_signed_info . '
<ds:SignatureValue>' . $signature_value_b64 . '</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>' . $x509_cert_clean . '</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>';

        // Import and append Signature to Root
        $signature_fragment = $dom->createDocumentFragment();
        $signature_fragment->appendXML( $signature_node_xml );
        $dom->documentElement->appendChild( $signature_fragment );

        return $dom->saveXML();
    }
}
