<?php

// +-------------------------------------------------+
// © 2002-2004 PMB Services / www.sigb.net pmb@sigb.net et contributeurs (voir www.sigb.net)
// +-------------------------------------------------+
// $Id: PDFRenderer.php,v 1.2.8.3 2026/03/31 14:34:03 jparis Exp $
namespace Pmb\DSI\Models\View\WYSIWYGPDFView\Render;

use Pmb\Common\Helper\Helper;
use Pmb\DSI\Models\View\WYSIWYGView\Render\HTML5Renderer;
use Pmb\Common\Library\Image\Image;

class PDFRenderer extends HTML5Renderer
{

    public const CONTAINER_ELEMENT_TEMPLATE = '!!content!!';

    public const BLOC_ELEMENT_TEMPLATE = '<div style="!!style!!">!!content!!</div>';

    public const ROOT_BLOC_ELEMENT_TEMPLATE = '
    <style>
        * {
            box-sizing: border-box;
        }
        .pdf-container {
            width: 100%;
        }
        td {
            padding: 0;
        }
    </style>
    <div class="pdf-container" style="!!style!!">
        !!content!!
    </div>';

    public function render($currentElement, $root = false): string
    {
        $currentElement->root = $root ?? false;
        $html = parent::render($currentElement);

        // Si c'est le rendu racine, optimiser le HTML pour le PDF
        if ($root) {
            // Convertir les images en base64 pour eviter les rechargements multiples
            $html = Image::convertHTMLImagesToBase64($html, 'png');

            // Nettoyer le HTML pour le rendre compatible avec TCPDF
            $html = $this->cleanHTMLForPDF($html);
        }

        return $html;
    }

    protected function renderBlockElement($currentElement)
    {
        if (! isset($currentElement->style->width)) {
            $currentElement->style->width = "100%";
        }

        if ($currentElement->root || empty($currentElement->blocks) || count($currentElement->blocks) == 1) {
            return $this->renderBlockWithoutChild($currentElement);
        } else {
            return $this->renderBlockWithChild($currentElement);
        }
    }

    protected function renderBlockWithoutChild($currentElement)
    {
        $content = "";
        foreach ($currentElement->blocks as $block) {
            $content .= $this->render($block);
        }

        if ($currentElement->root) {
            unset($currentElement->style->width);
        } else {
            $currentElement->style->maxWidth = $currentElement->style->width;
            $currentElement->style->position = "relative";
        }

        return str_replace([
            '!!style!!',
            '!!content!!'
        ], [
            $this->getStyleString($currentElement->style),
            $content
        ], $currentElement->root ? static::ROOT_BLOC_ELEMENT_TEMPLATE : static::BLOC_ELEMENT_TEMPLATE);
    }

    protected function renderBlockWithChild($currentElement)
    {
        if ($currentElement->style->flexDirection == "column") {
            $html = "<table style='!!style!!' class='pdf-grid' cellpadding='0' cellspacing='0'>!!content!!</table>";
        } else {
            $html = "<table style='!!style!!' class='pdf-grid' cellpadding='0' cellspacing='0'><tr>!!content!!</tr></table>";
        }

        $width = 100;
        if (! empty($currentElement->blocks) && $currentElement->style->flexDirection != "column") {
            $width = 100 / count($currentElement->blocks);
            $width = round($width, 2, PHP_ROUND_HALF_ODD);
        }

        $content = '';
        foreach ($currentElement->blocks as $block) {
            if ($currentElement->style->flexDirection == "column") {
                $content .= "<tr><td style='width:{$width}%;max-width:{$width}%;'>!!content!!</td></tr>";
            } else {
                $content .= "<td style='width:{$width}%;max-width:{$width}%;'>!!content!!</td>";
            }
            $content = str_replace('!!content!!', $this->render($block), $content);
        }
        return str_replace([
            '!!style!!',
            '!!content!!'
        ], [
            $this->getStyleString($currentElement->style),
            $content
        ], $html);
    }

    protected function renderVideoElement($currentElement)
    {
        return "<!-- videos not supported -->";
    }

    protected function getStyleString($style): string
    {
        if (!is_object($style)) {
            return "";
        }

        if (isset($style->block)) {
            $style = $style->block;
        }

        $style = get_object_vars($style);
        $style = $this->convertToXHTML($style);

        array_walk($style, function (&$value, $attribute) {
            $value = "{$attribute}:{$value}";
        });

        return implode(';', $style);
    }

    protected function convertToXHTML($style)
    {
        $convertedStyle = [];

        foreach ($style as $attribute => $value) {
            $attribute = Helper::camelize_to_kebab($attribute);

            switch ($attribute) {
                case 'display':
                    if ($value !== 'flex') {
                        $convertedStyle['display'] = $value;
                    }
                    break;

                case 'justify-content':
                    $convertedStyle['position'] = 'relative';
                    switch ($value) {
                        default:
                        case 'start':
                            $convertedStyle['text-align'] = 'left';
                            break;
                        case 'center':
                            $convertedStyle['text-align'] = 'center';
                            break;
                        case 'end':
                            $convertedStyle['text-align'] = 'right';
                            break;
                    }
                    break;

                case 'align-items':
                    $convertedStyle['position'] = 'relative';
                    switch ($value) {
                        default:
                        case 'start':
                            $convertedStyle['vertical-align'] = 'top';
                            break;
                        case 'center':
                            $convertedStyle['vertical-align'] = 'middle';
                            break;
                        case 'end':
                            $convertedStyle['vertical-align'] = 'bottom';
                            break;
                    }
                    break;

                case 'min-height':
                case 'flex-direction':
                case 'flex-grow':
                case 'flex':
                    // not compatible
                    break;

                default:
                    $convertedStyle[$attribute] = $value;
                    break;
            }
        }

        return $convertedStyle;
    }

    protected function renderImageElement($currentElement)
    {
        $currentElement->style->block->maxWidth = "100%";
        $currentElement->style->image->maxWidth = "100%";

        return str_replace([
            '!!style!!',
            '!!content!!'
        ], [
            $this->getStyleString($currentElement->style->block),
            parent::renderImageElement($currentElement)
        ], static::BLOC_ELEMENT_TEMPLATE);
    }

    protected function renderTextElement($currentElement)
    {
        return str_replace([
            '!!style!!',
            '!!content!!'
        ], [
            "width:100%;",
            parent::renderTextElement($currentElement)
        ], static::BLOC_ELEMENT_TEMPLATE);
    }

    protected function renderRichTextElement($currentElement)
    {
        return str_replace([
            '!!style!!',
            '!!content!!'
        ], [
            "width:100%;",
            parent::renderRichTextElement($currentElement)
        ], static::BLOC_ELEMENT_TEMPLATE);
    }

    protected function renderViewElement($currentElement)
    {
        // Recuperer le HTML de la vue imbriquee
        $viewHTML = parent::renderViewElement($currentElement);

        // Nettoyer le HTML pour le rendre compatible avec Html2Pdf/TCPDF
        $viewHTML = $this->cleanHTMLForPDF($viewHTML);

        return str_replace([
            '!!style!!',
            '!!content!!'
        ], [
            "width:100%;position:relative;",
            $viewHTML
        ], static::BLOC_ELEMENT_TEMPLATE);
    }

    /**
     * Nettoie le HTML pour le rendre compatible avec Html2Pdf/TCPDF
     * Supprime les proprietes CSS incompatibles qui empechent l'affichage correct
     *
     * @param string $html
     * @return string
     */
    protected function cleanHTMLForPDF(string $html): string
    {
        if (empty($html)) {
            return $html;
        }

        // Utiliser DOMDocument pour parser et manipuler le HTML proprement
        $dom = new \DOMDocument();

        // Desactiver les erreurs pour le HTML mal forme
        libxml_use_internal_errors(true);

        // Charger le HTML avec encodage UTF-8
        $dom->loadHTML('<?xml encoding="UTF-8">' . $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

        // Recuperer tous les elements avec un attribut style
        $xpath = new \DOMXPath($dom);
        $elements = $xpath->query('//*[@style]');

        foreach ($elements as $element) {
            $style = $element->getAttribute('style');

            // Supprimer les proprietes CSS incompatibles avec Html2Pdf/TCPDF
            $cleanedStyle = $this->cleanInlineStyle($style);

            if (!empty($cleanedStyle)) {
                $element->setAttribute('style', $cleanedStyle);
            } else {
                $element->removeAttribute('style');
            }
        }

        // Restaurer les erreurs
        libxml_clear_errors();
        libxml_use_internal_errors(false);

        // Sauvegarder le HTML nettoye
        $cleanedHTML = $dom->saveHTML();

        // Supprimer la declaration XML ajoutee
        $cleanedHTML = preg_replace('/^<\?xml[^>]+>\s*/', '', $cleanedHTML);

        return $cleanedHTML;
    }

    /**
     * Nettoie les styles inline en supprimant les proprietes incompatibles
     *
     * @param string $style Style CSS inline
     * @return string
     */
    protected function cleanInlineStyle(string $style): string
    {
        // Liste des proprietes CSS incompatibles avec Html2Pdf/TCPDF
        $incompatibleProperties = [
            'overflow', // overflow: hidden cache les images dans les PDFs
            'text-overflow',
            'overflow-x',
            'overflow-y',
        ];

        // Parser le style
        $declarations = explode(';', $style);
        $cleanedDeclarations = [];

        foreach ($declarations as $declaration) {
            $declaration = trim($declaration);
            if (empty($declaration)) {
                continue;
            }

            // Separer propriete et valeur
            $parts = explode(':', $declaration, 2);
            if (count($parts) !== 2) {
                continue;
            }

            $property = trim($parts[0]);
            $value = trim($parts[1]);

            // Verifier si la propriete est compatible
            if (!in_array(strtolower($property), $incompatibleProperties)) {
                $cleanedDeclarations[] = $property . ': ' . $value;
            }
        }

        return implode('; ', $cleanedDeclarations);
    }
}
