<?php
namespace EasyChat\Library;

class MessageTextRenderer {

    private const ALLOWED_TAGS = [
        'a' => true,
        'strong' => true,
        'em' => true,
        'code' => true,
        'pre' => true,
        'ul' => true,
        'ol' => true,
        'li' => true,
        'p' => true,
        'br' => true,
    ];

    public function toHtml(string $text): string {
        $text = str_replace(["\r\n", "\r"], "\n", $text);
        if (trim($text) === '') {
            return '';
        }

        $parsedown = new \Parsedown();
        if (method_exists($parsedown, 'setSafeMode')) {
            $parsedown->setSafeMode(true);
        }
        if (method_exists($parsedown, 'setMarkupEscaped')) {
            $parsedown->setMarkupEscaped(true);
        }
        if (method_exists($parsedown, 'setUrlsLinked')) {
            $parsedown->setUrlsLinked(true);
        }

        $html = $parsedown->text($text);
        $html = $this->sanitizeHtml($html);
        $html = preg_replace('/<li>\s*<p>(.*?)<\/p>\s*<\/li>/is', '<li>$1</li>', $html);
        $html = preg_replace('/(<\/p>|<\/li>|<ul>|<ol>)\s+(<p>|<li>|<\/ul>|<\/ol>)/is', '$1$2', $html);

        return trim($html);
    }

    




    public static function withoutCitationMarkers(string $text): string {
        $text = preg_replace('/[ \t]*\[S[1-4](?:\s*,\s*S[1-4])*\]/u', '', $text);
        $text = preg_replace('/[ \t]+([.,;:!?])/u', '$1', (string)$text);
        return trim((string)$text);
    }

    public static function isSafeHttpUrl(string $url): bool {
        $url = html_entity_decode(trim($url), ENT_QUOTES | ENT_HTML5, 'UTF-8');
        if ($url === '' || preg_match('/[\x00-\x1F\x7F\s"\'<>]/u', $url)) {
            return false;
        }

        $parts = parse_url($url);
        if (!is_array($parts)) {
            return false;
        }

        $scheme = strtolower((string)($parts['scheme'] ?? ''));
        if ($scheme !== 'http' && $scheme !== 'https') {
            return false;
        }

        if (isset($parts['user']) || isset($parts['pass'])) {
            return false;
        }
        $port = $parts['port'] ?? null;
        if ($port !== null && (!is_numeric($port) || (int)$port < 1 || (int)$port > 65535)) {
            return false;
        }
        return (string)($parts['host'] ?? '') !== '';
    }

    private function sanitizeHtml(string $html): string {
        if (!class_exists('\DOMDocument')) {
            return $this->sanitizeHtmlWithoutDom($html);
        }

        $previous = libxml_use_internal_errors(true);
        $document = new \DOMDocument('1.0', 'UTF-8');
        $document->loadHTML(
            '<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body><div id="easychat-message-root">' . $html . '</div></body></html>'
        );
        libxml_clear_errors();
        libxml_use_internal_errors($previous);

        $root = $document->getElementById('easychat-message-root');
        if ($root instanceof \DOMNode) {
            $this->sanitizeNode($root, $document);
            return $this->innerHtml($root);
        }

        $this->sanitizeNode($document, $document);
        return $this->innerHtml($document);
    }

    private function sanitizeHtmlWithoutDom(string $html): string {
        $html = strip_tags($html, '<a><strong><em><code><pre><ul><ol><li><p><br>');
        $html = preg_replace('/<(strong|em|code|pre|ul|ol|li|p|br)\b[^>]*>/i', '<$1>', $html);
        $html = preg_replace_callback('/<a\b([^>]*)>(.*?)<\/a>/is', function ($matches) {
            $attributes = (string)$matches[1];
            $label = (string)$matches[2];
            $href = '';

            if (preg_match('/\bhref\s*=\s*"([^"]*)"/i', $attributes, $href_match)) {
                $href = $href_match[1];
            } elseif (preg_match('/\bhref\s*=\s*\'([^\']*)\'/i', $attributes, $href_match)) {
                $href = $href_match[1];
            } elseif (preg_match('/\bhref\s*=\s*([^\s>]+)/i', $attributes, $href_match)) {
                $href = $href_match[1];
            }

            if (!self::isSafeHttpUrl($href)) {
                return $label;
            }

            $safe_href = htmlspecialchars(html_entity_decode(trim($href), ENT_QUOTES | ENT_HTML5, 'UTF-8'), ENT_QUOTES, 'UTF-8');
            return '<a class="easychat-message-link" href="' . $safe_href . '" target="_blank" rel="noopener noreferrer nofollow ugc" referrerpolicy="no-referrer">' . $label . '</a>';
        }, $html);
        return (string)$html;
    }

    private function sanitizeNode(\DOMNode $node, \DOMDocument $document): void {
        for ($child = $node->firstChild; $child !== null;) {
            $next = $child->nextSibling;

            if ($child instanceof \DOMElement) {
                $tag = strtolower($child->tagName);
                if (!isset(self::ALLOWED_TAGS[$tag])) {
                    $this->unwrapElement($child);
                    $child = $next;
                    continue;
                }

                $this->sanitizeAttributes($child);
            } elseif ($child instanceof \DOMComment) {
                $node->removeChild($child);
                $child = $next;
                continue;
            }

            if ($child->parentNode !== null) {
                $this->sanitizeNode($child, $document);
            }

            $child = $next;
        }
    }

    private function sanitizeAttributes(\DOMElement $element): void {
        $tag = strtolower($element->tagName);
        $href = '';

        if ($element->hasAttributes()) {
            for ($i = $element->attributes->length - 1; $i >= 0; $i--) {
                $attribute = $element->attributes->item($i);
                if (!$attribute) {
                    continue;
                }
                $name = strtolower($attribute->name);
                if ($tag === 'a' && $name === 'href') {
                    $href = $attribute->value;
                    continue;
                }
                if ($tag === 'a' && $name === 'title') {
                    continue;
                }
                $element->removeAttributeNode($attribute);
            }
        }

        if ($tag !== 'a') {
            return;
        }

        if (!self::isSafeHttpUrl($href)) {
            $element->removeAttribute('href');
            $element->removeAttribute('title');
            return;
        }

        $element->setAttribute('href', html_entity_decode(trim($href), ENT_QUOTES | ENT_HTML5, 'UTF-8'));
        $element->setAttribute('class', 'easychat-message-link');
        $element->setAttribute('target', '_blank');
        $element->setAttribute('rel', 'noopener noreferrer nofollow ugc');
        $element->setAttribute('referrerpolicy', 'no-referrer');
    }

    private function unwrapElement(\DOMElement $element): void {
        $parent = $element->parentNode;
        if (!$parent) {
            return;
        }

        while ($element->firstChild) {
            $parent->insertBefore($element->firstChild, $element);
        }
        $parent->removeChild($element);
    }

    private function innerHtml(\DOMNode $node): string {
        $html = '';
        foreach ($node->childNodes as $child) {
            $html .= $node->ownerDocument->saveHTML($child);
        }
        return $html;
    }
}
