<?php

declare(strict_types=1);

function sanitizeHtml(string $html): string
{
    // Keep this tight. Add tags only when you truly need them.
    $allowedTags = ['h1','h2','h3','p','ul','ol','li','strong','em','b','i','a','blockquote','br'];

    $dom = new DOMDocument('1.0', 'UTF-8');
    libxml_use_internal_errors(true);
    $dom->loadHTML('<?xml encoding="UTF-8"><div>' . $html . '</div>', LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
    libxml_clear_errors();

    $container = $dom->getElementsByTagName('div')->item(0);
    if ($container === null) {
        return '';
    }

    // Critical change: sanitize the children, not the wrapper itself
    foreach (iterator_to_array($container->childNodes) as $child) {
        sanitizeNode($child, $allowedTags);
    }

    $sanitized = '';
    foreach ($container->childNodes as $child) {
        $sanitized .= $dom->saveHTML($child);
    }

    return $sanitized;
}

function sanitizeNode(DOMNode $node, array $allowedTags): void
{
    if ($node instanceof DOMElement) {
        $tagName = strtolower($node->tagName);

        if (!in_array($tagName, $allowedTags, true)) {
            $parent = $node->parentNode;
            if ($parent !== null) {
                while ($node->firstChild) {
                    $parent->insertBefore($node->firstChild, $node);
                }
                $parent->removeChild($node);
                return;
            }
        }

        cleanAttributes($node);
    }

    foreach (iterator_to_array($node->childNodes) as $child) {
        sanitizeNode($child, $allowedTags);
    }
}

function cleanAttributes(DOMElement $element): void
{
    $allowedAttributes = ['href', 'title'];
    foreach (iterator_to_array($element->attributes) as $attr) {
        $name = strtolower($attr->name);
        $value = trim($attr->value);

        if (strpos($name, 'on') === 0) {
            $element->removeAttribute($attr->name);
            continue;
        }

        if (!in_array($name, $allowedAttributes, true)) {
            $element->removeAttribute($attr->name);
            continue;
        }

        if ($name === 'href' && !isValidLink($value)) {
            $element->removeAttribute($attr->name);
        }
    }
}

function isValidLink(string $url): bool
{
    if ($url === '') {
        return false;
    }

    if (str_starts_with($url, '/') || str_starts_with($url, '#')) {
        return true;
    }

    $parts = parse_url($url);
    if ($parts === false || !isset($parts['scheme'])) {
        return false;
    }

    $scheme = strtolower($parts['scheme']);
    return in_array($scheme, ['http', 'https', 'mailto'], true);
}
