<?php
/**
 * PDF Signature Processing System
 * Handles PDF signing, token generation, and document storage
 */
if (session_status() === PHP_SESSION_NONE) {
    session_start();
}

header('Content-Type: application/json');
require_once 'config.php';
require_once 'auth.php';
require_once 'billing.php';
require_once __DIR__ . '/app/database_schema.php';
require_once 'vendor/autoload.php'; // For FPDF or similar library 

use setasign\Fpdi\Tcpdf\Fpdi;

$auth = getAuth();
$auth->requireAuth();

// Enforce active subscription for signing actions (return JSON-friendly error) 
$db = getDatabaseConnection();
ensureDatabaseSchema($db);
$currentUser = $auth->getCurrentUser();
$sub = getUserSubscription($db, $currentUser['id']);
if (!isSubscriptionActive($sub)) {
    http_response_code(402);
    echo json_encode(['success' => false, 'message' => 'Se requiere una suscripción activa para firmar documentos.']);
    exit;
}

class PDFSigner {
    private $db;
    private $secretKey;
    private $uploadDir;
    private $baseUrl;
    private $pdfSigningCertificateFingerprint = '';
    
    public function __construct($db, $secretKey, $baseUrl) {
        $this->db = $db;
        $this->secretKey = $secretKey;
        $this->uploadDir = __DIR__ . '/signed_documents/';
        $this->baseUrl = $baseUrl;
           
        if (!file_exists($this->uploadDir)) {
            mkdir($this->uploadDir, 0755, true);
        }
    }
    
    public function processSignature($pdfFile, $signerName, $signatures, $totalPages) {
        try {
            // Validate input
            if (empty($signerName) || empty($signatures)) {
                throw new Exception('Datos incompletos');
            }
            
            // Generate unique token
            $token = $this->generateSecureToken();
            $documentId = uniqid('doc_', true);
            $timestamp = date('Y-m-d H:i:s');
            $dateFormatted = date('d/m/Y H:i');
            $signatureType = 'electronica_pdf';
            $originalHash = hash_file('sha256', $pdfFile['tmp_name']);
            
            // Process PDF with signatures
            $outputFilename = $documentId . '_signed.pdf';
            $outputPath = $this->uploadDir . $outputFilename;
            
            $this->addSignaturesToPDF($pdfFile, $signatures, $outputPath, $signerName, $dateFormatted, $token);
            $signedHash = hash_file('sha256', $outputPath);
            $evidence = $this->buildElectronicSignatureEvidence(
                $documentId,
                $token,
                $signerName,
                $timestamp,
                $originalHash,
                $signedHash,
                $signatureType
            );
            
            // Store in database
            $this->storeSignatureRecord($documentId, $token, $signerName, $timestamp, $outputFilename, $signatureType, $originalHash, $signedHash, $evidence);
            
            return [
                'success' => true,
                'message' => 'Documento firmado exitosamente',
                'documentId' => $documentId,
                'token' => $token,
                'downloadUrl' => 'download?doc=' . $documentId,
                'verificationUrl' => $this->baseUrl . '/verify?token=' . $token
            ];
            
        } catch (Exception $e) {
            return [
                'success' => false,
                'message' => $e->getMessage()
            ];
        }
    }
    
    private function generateSecureToken() {
        $data = uniqid(mt_rand(), true) . time() . microtime();
        return hash_hmac('sha256', $data, $this->secretKey);
    }

    private function buildElectronicSignatureEvidence($documentId, $token, $signerName, $timestamp, $originalHash, $signedHash, $signatureType) {
        $userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';

        return [
            'tipo_de_firma' => $signatureType,
            'descripcion' => 'Firma electronica PDF aplicada por la plataforma',
            'firma_pdf_aplicada' => true,
            'certificado_confiable_por_terceros' => false,
            'certificado_sha256' => $this->pdfSigningCertificateFingerprint,
            'id_de_documento' => $documentId,
            'token' => $token,
            'id_de_usuario' => isset($_SESSION['user_id']) ? (int)$_SESSION['user_id'] : null,
            'nombre_del_firmante' => $signerName,
            'firmado_el' => $timestamp,
            'direccion_ip' => $_SERVER['REMOTE_ADDR'] ?? '',
            'user_agent_sha256' => $userAgent !== '' ? hash('sha256', $userAgent) : '',
            'hash_documento_original' => $originalHash,
            'hash_documento_firmado' => $signedHash,
            'zona_horaria' => date_default_timezone_get()
        ];
    }

    private function applyPdfElectronicSignature($pdf, $signerName, $date, $token) {
        if (!method_exists($pdf, 'setSignature')) {
            throw new Exception('La libreria PDF no soporta firma electronica.');
        }

        $certificate = $this->ensurePdfSigningCertificate($signerName);
        $info = [
            'Name' => $signerName,
            'Location' => $_SERVER['REMOTE_ADDR'] ?? '',
            'Reason' => 'Firma electronica de documento PDF',
            'ContactInfo' => defined('MAIL_FROM') ? MAIL_FROM : ''
        ];

        if (method_exists($pdf, 'setSignatureAppearance')) {
            $pdf->setSignatureAppearance(0, 0, 0, 0, 1, $this->sanitizeCertificateCommonName($signerName));
        }

        $pdf->setSignature(
            $certificate,
            $certificate,
            defined('PDF_SIGNATURE_CERT_PASSWORD') ? PDF_SIGNATURE_CERT_PASSWORD : '',
            '',
            1,
            $info
        );
    }

    private function ensurePdfSigningCertificate($signerName) {
        if (!extension_loaded('openssl')) {
            throw new Exception('OpenSSL es requerido para firmar electronicamente el PDF.');
        }

        $configuredPath = defined('PDF_SIGNATURE_CERT_PATH') ? trim((string)PDF_SIGNATURE_CERT_PATH) : '';
        $certPath = $configuredPath !== ''
            ? $configuredPath
            : $this->getSignerCertificatePath($signerName);

        $certDir = dirname($certPath);
        if (!is_dir($certDir) && !mkdir($certDir, 0700, true)) {
            throw new Exception('No se pudo crear el directorio privado de certificados.');
        }

        $denyFile = $certDir . '/.htaccess';
        if (!file_exists($denyFile)) {
            @file_put_contents($denyFile, "Require all denied\n");
        }

        if (!is_file($certPath) || filesize($certPath) === 0) {
            $this->createSelfSignedPdfCertificate($certPath, $signerName);
        }

        $resolved = realpath($certPath);
        if (!$resolved || !is_readable($resolved)) {
            throw new Exception('No se pudo leer el certificado de firma PDF.');
        }

        $certContent = file_get_contents($resolved);
        $x509 = openssl_x509_read($certContent);
        if ($x509) {
            $fingerprint = openssl_x509_fingerprint($x509, 'sha256');
            $this->pdfSigningCertificateFingerprint = $fingerprint ?: '';
        }

        return 'file://' . str_replace('\\', '/', $resolved);
    }

    private function getSignerCertificatePath($signerName) {
        $userId = isset($_SESSION['user_id']) ? (int)$_SESSION['user_id'] : 0;
        $nameHash = substr(hash('sha256', $this->sanitizeCertificateCommonName($signerName)), 0, 16);

        return __DIR__ . '/app/private/pdf_signature_user_' . $userId . '_' . $nameHash . '.pem';
    }

    private function sanitizeCertificateCommonName($name) {
        $name = trim((string)$name);
        $name = preg_replace('/[\x00-\x1F\x7F]+/', ' ', $name);
        $name = preg_replace('/\s+/', ' ', $name);

        if ($name === '') {
            $name = defined('PDF_SIGNATURE_CERT_COMMON_NAME') ? PDF_SIGNATURE_CERT_COMMON_NAME : 'Firmador Codex Documentos PDF';
        }

        if (function_exists('mb_substr')) {
            return mb_substr($name, 0, 64, 'UTF-8');
        }

        return substr($name, 0, 64);
    }

    private function createSelfSignedPdfCertificate($certPath, $signerName) {
        $dn = [
            'countryName' => getenv('PDF_SIGNATURE_CERT_COUNTRY') ?: 'CO',
            'organizationName' => getenv('PDF_SIGNATURE_CERT_ORG') ?: 'Firmador Codex',
            'commonName' => $this->sanitizeCertificateCommonName($signerName),
            'emailAddress' => defined('MAIL_FROM') ? MAIL_FROM : 'no-reply@mercavilla.com'
        ];

        $config = [
            'digest_alg' => 'sha256',
            'private_key_bits' => 2048,
            'private_key_type' => OPENSSL_KEYTYPE_RSA
        ];

        $privateKey = openssl_pkey_new($config);
        if (!$privateKey) {
            throw new Exception('No se pudo generar la clave privada para firma PDF.');
        }

        $csr = openssl_csr_new($dn, $privateKey, ['digest_alg' => 'sha256']);
        if (!$csr) {
            throw new Exception('No se pudo generar la solicitud de certificado PDF.');
        }

        $certificate = openssl_csr_sign($csr, null, $privateKey, 3650, ['digest_alg' => 'sha256']);
        if (!$certificate) {
            throw new Exception('No se pudo generar el certificado de firma PDF.');
        }

        $certOut = '';
        $keyOut = '';
        openssl_x509_export($certificate, $certOut);
        openssl_pkey_export(
            $privateKey,
            $keyOut,
            defined('PDF_SIGNATURE_CERT_PASSWORD') ? PDF_SIGNATURE_CERT_PASSWORD : ''
        );

        if ($certOut === '' || $keyOut === '') {
            throw new Exception('No se pudo exportar el certificado de firma PDF.');
        }

        if (file_put_contents($certPath, $certOut . "\n" . $keyOut, LOCK_EX) === false) {
            throw new Exception('No se pudo guardar el certificado de firma PDF.');
        }

        @chmod($certPath, 0600);
    }
    
    private function addSignaturesToPDF($sourcePdf, $signatures, $outputPath, $signerName, $date, $token) {
        $pdf = new Fpdi();

        // Ensure overlay coordinates align with the imported page (no margins/headers)
        if (method_exists($pdf, 'SetPrintHeader')) { $pdf->SetPrintHeader(false); }
        if (method_exists($pdf, 'SetPrintFooter')) { $pdf->SetPrintFooter(false); }
        if (method_exists($pdf, 'SetMargins'))      { $pdf->SetMargins(0, 0, 0, false); }
        if (method_exists($pdf, 'SetHeaderMargin')) { $pdf->SetHeaderMargin(0); }
        if (method_exists($pdf, 'SetFooterMargin')) { $pdf->SetFooterMargin(0); }
        if (method_exists($pdf, 'SetAutoPageBreak')){ $pdf->SetAutoPageBreak(false, 0); }
        if (method_exists($pdf, 'SetCreator')) { $pdf->SetCreator('Firmador Codex'); }
        if (method_exists($pdf, 'SetAuthor')) { $pdf->SetAuthor($signerName); }
        if (method_exists($pdf, 'SetSubject')) { $pdf->SetSubject('Documento firmado electronicamente'); }
        if (method_exists($pdf, 'SetKeywords')) { $pdf->SetKeywords('firma electronica pdf, token de verificacion'); }
        $this->applyPdfElectronicSignature($pdf, $signerName, $date, $token);
        
        // Get temp path for source PDF
        $tempPdfPath = $sourcePdf['tmp_name'];
        $pageCount = $pdf->setSourceFile($tempPdfPath);
        
        $signaturesArray = json_decode($signatures, true);

        for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++) {
            // Import and size the output page to exactly match the source page (use CropBox)
            $tplId = $pdf->importPage($pageNo, '/CropBox');
            $size  = $pdf->getTemplateSize($tplId);

            $orientation = isset($size['orientation']) ? $size['orientation'] : ($size['width'] > $size['height'] ? 'L' : 'P');
            $pdf->AddPage($orientation, [$size['width'], $size['height']]);
            // Place template explicitly at 0,0 with exact size to avoid any auto-scaling
            $pdf->useTemplate($tplId, 0, 0, $size['width'], $size['height']);
            
            // Add signature image if exists for this page
            if (isset($signaturesArray[$pageNo])) {
                $signatureData = $signaturesArray[$pageNo];
                
                // Extract position and signature data
                $sigImage = $signatureData['image'] ?? '';
                $posX = $signatureData['x'] ?? 0;    // legacy device-px X
                $posY = $signatureData['y'] ?? 0;    // legacy device-px Y
                $width = $signatureData['width'] ?? 60;  // legacy device-px W
                $height = $signatureData['height'] ?? 30; // legacy device-px H
                // New normalized coordinates (fractions of canvas width/height)
                $xNorm = isset($signatureData['xNorm']) ? floatval($signatureData['xNorm']) : null;
                $yNorm = isset($signatureData['yNorm']) ? floatval($signatureData['yNorm']) : null;
                $wNorm = isset($signatureData['wNorm']) ? floatval($signatureData['wNorm']) : null;
                $hNorm = isset($signatureData['hNorm']) ? floatval($signatureData['hNorm']) : null;
                
                // Get page dimensions for validation
                // Page dimensions in TCPDF user units (top-left origin)
                // Page dimensions now exactly match the template
                $size       = $pdf->getTemplateSize($tplId);
                $pageWidth  = (float)$size['width'];
                $pageHeight = (float)$size['height'];

                // Values from client
                $canvasPixelW = (float)($signatureData['canvasPixelWidth']  ?? 0);
                $canvasPixelH = (float)($signatureData['canvasPixelHeight'] ?? 0);
                $dpr          = (float)($signatureData['dpr'] ?? 1.0);
                if ($dpr <= 0) { $dpr = 1.0; }

                if ($xNorm !== null && $yNorm !== null && $wNorm !== null && $hNorm !== null) {
                    // Use normalized (fractional) coordinates directly
                    $pdfX = max(0.0, min(1.0, $xNorm)) * $pageWidth;
                    $pdfY = max(0.0, min(1.0, $yNorm)) * $pageHeight;
                    $pdfW = max(0.1, max(0.0, min(1.0, $wNorm)) * $pageWidth);
                    $pdfH = max(0.1, max(0.0, min(1.0, $hNorm)) * $pageHeight);
                } else {
                    // Legacy mapping: device px → CSS px → PDF units
                    $canvasCssW = $canvasPixelW > 0 ? $canvasPixelW / $dpr : 0.0;
                    $canvasCssH = $canvasPixelH > 0 ? $canvasPixelH / $dpr : 0.0;

                    if ($canvasCssW <= 0 || $canvasCssH <= 0) {
                        $canvasCssW = $pageWidth;
                        $canvasCssH = $pageHeight;
                    }

                    $pxToUnitX = $canvasCssW > 0 ? ($pageWidth  / $canvasCssW) : 1.0;
                    $pxToUnitY = $canvasCssH > 0 ? ($pageHeight / $canvasCssH) : 1.0;

                    $xCss = ((float)($signatureData['x']      ?? 0)) / $dpr;
                    $yCss = ((float)($signatureData['y']      ?? 0)) / $dpr;
                    $wCss = ((float)($signatureData['width']  ?? 0)) / $dpr;
                    $hCss = ((float)($signatureData['height'] ?? 0)) / $dpr;

                    $pdfX = $xCss * $pxToUnitX;
                    $pdfY = $yCss * $pxToUnitY;
                    $pdfW = max(0.1, $wCss * $pxToUnitX);
                    $pdfH = max(0.1, $hCss * $pxToUnitY);
                }

                // Clamp to page to avoid off-page invisibility
                if ($pdfX < 0) $pdfX = 0;
                if ($pdfY < 0) $pdfY = 0;
                if ($pdfX + $pdfW > $pageWidth)  $pdfW  = max(0.1, $pageWidth  - $pdfX);
                if ($pdfY + $pdfH > $pageHeight) $pdfH  = max(0.1, $pageHeight - $pdfY);

                


                error_log(sprintf(
                    '[signature] page=%d pageW=%.2f pageH=%.2f canvasPx=%dx%d dpr=%.2f cssW=%.2f cssH=%.2f -> pdfXYWH=(%.2f,%.2f,%.2f,%.2f)',
                    $pageNo,
                    $pageWidth, $pageHeight,
                    $canvasPixelW, $canvasPixelH,
                    $dpr,
                    $canvasCssW, $canvasCssH,
                    $pdfX, $pdfY, $pdfW, $pdfH
                ));
                // If the placement appears vertically flipped, use this alternative for Y:
                // $pdfY = $pageHeight - (($posY + $imgH) * $scaleY);

                // Accept SVG data URLs with optional charset and/or base64
                if (preg_match('#^data:image/svg\+xml(?:;charset=[^;,]+)?(?:;base64)?,#i', $sigImage)) {
                    // SVG handling
                    $commaPos = strpos($sigImage, ',');
                    $payload  = substr($sigImage, $commaPos + 1);
                    $isBase64 = (stripos($sigImage, ';base64,') !== false);
                    
                    if ($isBase64) {
                        $svgContent = base64_decode(str_replace(' ', '+', $payload));
                    } else {
                        $svgContent = urldecode($payload);
                    }

                    // Normalize/boost stroke to keep appearance crisp in PDF
                    $svgContent = $this->enhanceSvgForPdf($svgContent, $pdfW, $pdfH);
                    
                    $tempSvgPath = sys_get_temp_dir() . '/sig_' . uniqid() . '.svg';
                    file_put_contents($tempSvgPath, $svgContent);
                    
                    if (method_exists($pdf, 'ImageSVG')) {
                        // Explicitly disable any border to avoid a visible box
                        $pdf->ImageSVG($tempSvgPath, $pdfX, $pdfY, $pdfW, $pdfH, '', '', '', 0, false);
                    }
                    
                    unlink($tempSvgPath);
                    
                } elseif (preg_match('/^data:image\/(png|jpeg);base64,/', $sigImage)) {
                    $base64  = preg_replace('/^data:image\/(png|jpeg);base64,/', '', $sigImage);
                    $imgData = base64_decode(str_replace(' ', '+', $base64), true);
                    if ($imgData === false || strlen($imgData) < 50) {
                        error_log('[signature] invalid image data: len='.(is_string($imgData)?strlen($imgData):0));
                    } else {
                    $tempPngPath = sys_get_temp_dir() . '/sig_' . uniqid() . '.png';
                    file_put_contents($tempPngPath, $imgData);

                        // Place it without interpolation artifacts if possible
                        if (method_exists($pdf, 'setImageScale')) {
                            // Maintain natural pixel density; adjust if needed
                            $pdf->setImageScale(1.0);
                        }
                        $pdf->Image($tempPngPath, $pdfX, $pdfY, $pdfW, $pdfH, 'PNG');

                        @unlink($tempPngPath);
                    }
                }
                // Add footer after signature
                $this->addSignatureFooter($pdf, $signerName, $date, $token, $pageHeight);
            }
        }
        
        $pdf->Output($outputPath,'F');
    }

    // Improve visual quality of thin strokes and ensure solid black lines
    private function enhanceSvgForPdf($svg, $targetW, $targetH) {
        // Parse SVG and normalize stroke properties robustly
        libxml_use_internal_errors(true);
        $doc = new DOMDocument();
        if (!@$doc->loadXML($svg)) {
            return $svg; // fallback if parsing fails
        }

        $xpath = new DOMXPath($doc);

        // Determine source viewBox size to scale stroke to final width
        $vbW = null; $vbH = null;
        $svgEl = $doc->getElementsByTagName('svg')->item(0);
        if ($svgEl) {
            $viewBox = $svgEl->getAttribute('viewBox');
            if ($viewBox) {
                $parts = preg_split('/\s+|,/', trim($viewBox));
                if (count($parts) === 4) {
                    $vbW = floatval($parts[2]);
                    $vbH = floatval($parts[3]);
                }
            }
            if ($vbW === null || $vbW <= 0) {
                $vbW = $svgEl->hasAttribute('width') ? floatval($svgEl->getAttribute('width')) : 700.0;
            }
            if ($vbH === null || $vbH <= 0) {
                $vbH = $svgEl->hasAttribute('height') ? floatval($svgEl->getAttribute('height')) : 270.0;
            }
            // Ensure root has no global opacity/fill that could wash strokes
            $rootStyle = $svgEl->getAttribute('style');
            $rootStyle = trim($rootStyle . ';stroke:#000000;fill:none;stroke-opacity:1');
            $svgEl->setAttribute('style', $rootStyle);
        } else {
            $vbW = 700.0; $vbH = 270.0;
        }

        // Compute scale from SVG space → PDF space for width
        $scaleX = ($vbW > 0) ? ($targetW / $vbW) : 1.0;
        if ($scaleX <= 0) { $scaleX = 1.0; }
        // Desired final stroke thickness in PDF user units (adjust if needed)
        $desiredPdfStroke = 0.2; // thinner stroke in PDF units
        // Back-calc stroke to set in SVG so final appearance matches
        $desiredSvgStroke = $desiredPdfStroke / $scaleX;

        // Remove any debug rectangles with red stroke
        foreach ($xpath->query('//*[local-name()="rect"]') as $rect) {
            $stroke = strtolower($rect->getAttribute('stroke'));
            $style  = strtolower($rect->getAttribute('style'));
            if (strpos($stroke, '#ff0000') !== false || strpos($style, 'stroke:#ff0000') !== false ||
                strpos($stroke, 'red') !== false || strpos($style, 'stroke:red') !== false) {
                $rect->parentNode->removeChild($rect);
            }
        }

        $targets = $xpath->query('//*[local-name()="path" or local-name()="polyline" or local-name()="line"]');
        foreach ($targets as $el) {
            // Extract style into associative array
            $styleArr = [];
            $styleStr = $el->getAttribute('style');
            if ($styleStr) {
                foreach (explode(';', $styleStr) as $pair) {
                    $pair = trim($pair);
                    if ($pair === '') continue;
                    $kv = explode(':', $pair, 2);
                    if (count($kv) === 2) {
                        $styleArr[strtolower(trim($kv[0]))] = trim($kv[1]);
                    }
                }
            }

            /// Determine stroke width and enforce a minimum
            $w = 0.0;
            if ($el->hasAttribute('stroke-width')) { $w = floatval($el->getAttribute('stroke-width')); }
            if (isset($styleArr['stroke-width'])) { $w = floatval($styleArr['stroke-width']); }
            // Pick the larger of desiredSvgStroke and existing width, avoid super-thin
            $minSvg = max($desiredSvgStroke, 0.2);
            if ($w <= 0) { $w = $minSvg; } else { $w = max($minSvg, $w); }
            $wFormatted = rtrim(rtrim(sprintf('%.2f', $w), '0'), '.');
       
            // Apply robust stroke settings
            $el->setAttribute('stroke', '#000000');
            $el->setAttribute('stroke-width', $wFormatted);
            $el->setAttribute('stroke-linecap', 'round');
            $el->setAttribute('stroke-linejoin', 'round');
            $el->setAttribute('stroke-opacity', '1');
            $el->setAttribute('fill', 'none');
            $el->removeAttribute('opacity');
            $el->removeAttribute('fill-opacity');
            // Improve joins and avoid hairline spikes
            $el->setAttribute('stroke-miterlimit', '2');
            // Hint for renderers supporting non-scaling-stroke (TCPDF may ignore)
            $el->setAttribute('vector-effect', 'non-scaling-stroke');
     
            // Reflect in style attribute as well (some renderers prioritize style)
            unset($styleArr['stroke'], $styleArr['stroke-width'], $styleArr['stroke-linecap'], $styleArr['stroke-linejoin'],
                  $styleArr['stroke-opacity'], $styleArr['fill'], $styleArr['opacity'], $styleArr['fill-opacity'], $styleArr['filter']);
            $styleArr['stroke'] = '#000000';
            $styleArr['stroke-width'] = $wFormatted;
            $styleArr['stroke-linecap'] = 'round';
            $styleArr['stroke-linejoin'] = 'round';
            $styleArr['stroke-opacity'] = '1';
            $styleArr['fill'] = 'none';
            
            // Rebuild style attribute
            $out = '';
            foreach ($styleArr as $k => $v) { if ($v === '') continue; $out .= $k . ':' . $v . ';'; }
            if ($out !== '') { $el->setAttribute('style', $out); } else { $el->removeAttribute('style'); }
        }

        // Clean group-level opacity and filters that could lighten strokes
        foreach ($xpath->query('//*[local-name()="g"]') as $g) {
            if ($g->hasAttribute('opacity')) { $g->removeAttribute('opacity'); }
            if ($g->hasAttribute('stroke-opacity')) { $g->removeAttribute('stroke-opacity'); }
            $gs = $g->getAttribute('style');
            if ($gs) {
                $pairs = array_filter(array_map('trim', explode(';', $gs)));
                $clean = [];
                foreach ($pairs as $p) {
                    if (stripos($p, 'opacity:') === 0) continue;
                    if (stripos($p, 'stroke-opacity:') === 0) continue;
                    if (stripos($p, 'filter:') === 0) continue;
                    $clean[] = $p;
                }
                if (count($clean)) $g->setAttribute('style', implode(';', $clean)); else $g->removeAttribute('style');
            }
        }

        return $doc->saveXML($doc->documentElement);
    }
        
    private function addSignatureFooter($pdf, $signerName, $date, $token, $pageHeight = null) {
        /// Save current position
        $currentX = $pdf->GetX();
        $currentY = $pdf->GetY();

        // Page size (TCPDF user units)
        if ($pageHeight === null && method_exists($pdf, 'GetPageHeight')) {
            $pageHeight = $pdf->GetPageHeight();
        } elseif ($pageHeight === null) {
            $pageHeight = 297; // fallback approx A4 height in current units
        }
        $pageWidth = method_exists($pdf, 'GetPageWidth') ? $pdf->GetPageWidth() : 210; // fallback A4 width

        // Verification URL and labels
        $verificationUrl = $this->baseUrl . '/verify?token=' . $token;
        $signatureText = "Firmado: {$signerName} - el: {$date}";
        $linkText = "Verificar autenticidad del documento";

        // Layout constants (in PDF units)
        $margin = 10;           // distance from page edges
        $qrSize = 20;           // QR side length
        $gap = 3;               // gap between text block and QR

        // Place QR at bottom-right corner 
        $qrX = $pageWidth - $margin - $qrSize;
        $qrY = $pageHeight - $margin - $qrSize;

        if (method_exists($pdf, 'write2DBarcode')) {
            $style = array(
                'border' => 0,
                'padding' => 0,
                'fgcolor' => array(0, 0, 0),
                'bgcolor' => false,
            ); 
            $pdf->write2DBarcode($verificationUrl, 'QRCODE,L', $qrX, $qrY, $qrSize, $qrSize, $style, 'N');
        }

        // Text to the left of the QR, right-aligned, bottom aligned to QR bottom
        $textRight = $qrX - $gap;

        // Compute widths and place so that the bottom of the two-line block
        // aligns with the QR bottom.
        $pdf->SetFont('helvetica', 'U', 8);
        $pdf->SetTextColor(0, 0, 255);
        $linkW = $pdf->GetStringWidth($linkText);
        $linkH = 4; // line height used in Cell below

        $pdf->SetFont('helvetica', 'I', 8);
        $pdf->SetTextColor(100, 100, 100);
        $sigW = $pdf->GetStringWidth($signatureText);
        $sigH = 4;

        $blockH = $linkH + $sigH;
        $bottom = $qrY + $qrSize;
        $linkY = $bottom - $blockH;       // top line
        $sigY  = $bottom - $sigH;         // second line baseline

        // Draw link (top line)
        $pdf->SetFont('helvetica', 'U', 8);
        $pdf->SetTextColor(0, 0, 255);
        $linkX = max(0, $textRight - $linkW);
        $pdf->SetXY($linkX, $linkY);
        $pdf->Cell($linkW, $linkH, $linkText, 0, 1, 'R', false, $verificationUrl);

        // Draw signer/date (bottom line), right-aligned to same edge
        $pdf->SetFont('helvetica', 'I', 8);
        $pdf->SetTextColor(100, 100, 100);
        $sigX = max(0, $textRight - $sigW);
        $pdf->SetXY($sigX, $sigY);
        $pdf->Cell($sigW, $sigH, $signatureText, 0, 0, 'R');

        // Restore position
        $pdf->SetXY($currentX, $currentY);
    }
    
    private function storeSignatureRecord($documentId, $token, $signerName, $timestamp, $filename, $signatureType, $originalHash, $signedHash, $evidence) {
        //error_log("Executing DB insert for signature record: docId=$documentId, token=$token, userId=" . $this->userId);
        $ip = $_SERVER['REMOTE_ADDR'] ?? '';
        $userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
        $evidenceJson = json_encode($evidence, JSON_UNESCAPED_SLASHES);

        $stmt = $this->db->prepare("
            INSERT INTO firmas (
                id_de_documento,
                token,
                nombre_del_firmante,
                firmado_el,
                nombre_de_archivo,
                estado_de_documento,
                tipo_de_firma,
                id_de_usuario,
                direccion_ip,
                user_agent,
                hash_documento_original,
                hash_documento_firmado,
                evidencia_firma
            )
            VALUES (?, ?, ?, ?, ?, 'valido', ?, ?, ?, ?, ?, ?, ?)
        ");
        
        $stmt->bind_param(
            'ssssssisssss',
            $documentId,
            $token,
            $signerName,
            $timestamp,
            $filename,
            $signatureType,
            $_SESSION['user_id'],
            $ip,
            $userAgent,
            $originalHash,
            $signedHash,
            $evidenceJson
        );
        if (!$stmt->execute()) {
            error_log("Database error: " . $stmt->error);
            throw new Exception('Error al guardar en base de datos');
        }
        
        $stmt->close();
    }
    
    public function verifyDocument($token) {
        $stmt = $this->db->prepare("
            SELECT id_de_documento, nombre_del_firmante, creado_el, estado_de_documento, nombre_de_archivo
            FROM firmas
            WHERE token = ? AND estado_de_documento = 'valido'
        ");
        
        $stmt->bind_param('s', $token);
        $stmt->execute();
        $result = $stmt->get_result();
        
        if ($row = $result->fetch_assoc()) {
            return [
                'valid' => true,
                'documentId' => $row['id_de_documento'],
                'signerName' => $row['nombre_del_firmante'],
                'signedAt' => $row['creado_el'],
                'status' => $row['estado_de_documento']
            ];
        }
        
        return ['valid' => false];
    }
}

// Main processing
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $db = getDatabaseConnection();
    $signer = new PDFSigner($db, SECRET_KEY, BASE_URL);
    
    $result = $signer->processSignature(
        $_FILES['pdf'],
        $_POST['signerName'],
        $_POST['signatures'],
        $_POST['totalPages']
    );
    
    echo json_encode($result);
    
    $db->close();
} else {
    echo json_encode(['success' => false, 'message' => 'Método no permitido']);
}
?>
