<?php


function sanitize_post_html(
    string $html
): string {

    $html =
        trim($html);


    if ($html === '') {
        return '';
    }


    /*
    |--------------------------------------------------------------------------
    | Nếu DOM không khả dụng thì fallback an toàn hơn
    |--------------------------------------------------------------------------
    */

    if (!class_exists('DOMDocument')) {

        return strip_tags(
            $html,
            '<p><br><strong><b><em><i><u><s>'
            . '<h2><h3><h4>'
            . '<ul><ol><li>'
            . '<blockquote>'
            . '<a><span><div><font>'
        );
    }


    $dom =
        new DOMDocument(
            '1.0',
            'UTF-8'
        );


    libxml_use_internal_errors(
        true
    );


    $dom->loadHTML(

        '<?xml encoding="utf-8" ?>'
        . '<div id="post-root">'
        . $html
        . '</div>',

        LIBXML_HTML_NOIMPLIED
        | LIBXML_HTML_NODEFDTD

    );


    libxml_clear_errors();


    $allowedTags = [

        'div',
        'p',
        'br',

        'strong',
        'b',

        'em',
        'i',

        'u',
        's',

        'h2',
        'h3',
        'h4',

        'ul',
        'ol',
        'li',

        'blockquote',

        'a',

        'span',

        'font'

    ];


    $cleanNode =
        function (
            DOMNode $node
        ) use (
            &$cleanNode,
            $allowedTags
        ) {

            /*
            |--------------------------------------------------------------------------
            | Duyệt con trước
            |--------------------------------------------------------------------------
            */

            for (
                $child =
                    $node->firstChild;

                $child !== null;

            ) {

                $next =
                    $child->nextSibling;


                if (
                    $child
                    instanceof DOMElement
                ) {

                    $tag =
                        strtolower(
                            $child->tagName
                        );


                    /*
                     * Loại script/style hoàn toàn.
                     */

                    if (
                        in_array(
                            $tag,
                            [
                                'script',
                                'style',
                                'iframe',
                                'object',
                                'embed'
                            ],
                            true
                        )
                    ) {

                        $node->removeChild(
                            $child
                        );

                        $child = $next;

                        continue;
                    }


                    /*
                     * Tag không cho phép:
                     * bỏ tag nhưng giữ text/con bên trong.
                     */

                    if (
                        !in_array(
                            $tag,
                            $allowedTags,
                            true
                        )
                    ) {

                        while (
                            $child->firstChild
                        ) {

                            $node->insertBefore(
                                $child->firstChild,
                                $child
                            );

                        }


                        $node->removeChild(
                            $child
                        );


                        $child = $next;

                        continue;
                    }


                    /*
                    |--------------------------------------------------------------------------
                    | Lọc attribute
                    |--------------------------------------------------------------------------
                    */

                    $attributesToRemove = [];


                    foreach (
                        iterator_to_array(
                            $child->attributes
                        )
                        as $attribute
                    ) {

                        $name =
                            strtolower(
                                $attribute->name
                            );


                        if (
                            !in_array(
                                $name,
                                [
                                    'href',
                                    'target',
                                    'rel',
                                    'style',
                                    'align',
                                    'size'
                                ],
                                true
                            )
                        ) {

                            $attributesToRemove[] =
                                $name;

                        }

                    }


                    foreach (
                        $attributesToRemove
                        as $attribute
                    ) {

                        $child->removeAttribute(
                            $attribute
                        );

                    }


                    /*
                    |--------------------------------------------------------------------------
                    | LINK
                    |--------------------------------------------------------------------------
                    */

                    if ($tag === 'a') {

                        $href =
                            trim(
                                $child->getAttribute(
                                    'href'
                                )
                            );


                        if (
                            $href !== ''
                            &&
                            !preg_match(
                                '#^(https?://|mailto:|tel:|/|\\?|\\#)#i',
                                $href
                            )
                        ) {

                            $child->removeAttribute(
                                'href'
                            );

                        }


                        if (
                            $child->getAttribute(
                                'target'
                            )
                            === '_blank'
                        ) {

                            $child->setAttribute(
                                'rel',
                                'noopener noreferrer'
                            );

                        }

                    }


                    /*
                    |--------------------------------------------------------------------------
                    | STYLE
                    |
                    | Chỉ giữ text-align và font-size.
                    |--------------------------------------------------------------------------
                    */

                    if (
                        $child->hasAttribute(
                            'style'
                        )
                    ) {

                        $style =
                            $child->getAttribute(
                                'style'
                            );


                        $safeStyles = [];


                        foreach (
                            explode(
                                ';',
                                $style
                            )
                            as $rule
                        ) {

                            if (
                                !str_contains(
                                    $rule,
                                    ':'
                                )
                            ) {
                                continue;
                            }


                            [
                                $property,
                                $value
                            ] =
                                array_map(
                                    'trim',
                                    explode(
                                        ':',
                                        $rule,
                                        2
                                    )
                                );


                            $property =
                                strtolower(
                                    $property
                                );


                            if (
                                $property
                                === 'text-align'
                                &&
                                in_array(
                                    strtolower($value),
                                    [
                                        'left',
                                        'center',
                                        'right',
                                        'justify'
                                    ],
                                    true
                                )
                            ) {

                                $safeStyles[] =
                                    'text-align:'
                                    . strtolower(
                                        $value
                                    );

                            }


                            if (
                                $property
                                === 'font-size'
                                &&
                                preg_match(
                                    '/^\d+(px|pt|em|rem|%)$/i',
                                    $value
                                )
                            ) {

                                $safeStyles[] =
                                    'font-size:'
                                    . $value;

                            }

                        }


                        if ($safeStyles) {

                            $child->setAttribute(
                                'style',
                                implode(
                                    ';',
                                    $safeStyles
                                )
                            );

                        } else {

                            $child->removeAttribute(
                                'style'
                            );

                        }

                    }


                    /*
                     * align
                     */

                    if (
                        $child->hasAttribute(
                            'align'
                        )
                        &&
                        !in_array(
                            strtolower(
                                $child->getAttribute(
                                    'align'
                                )
                            ),
                            [
                                'left',
                                'center',
                                'right',
                                'justify'
                            ],
                            true
                        )
                    ) {

                        $child->removeAttribute(
                            'align'
                        );

                    }


                    /*
                     * font size 1–7
                     */

                    if (
                        $child->hasAttribute(
                            'size'
                        )
                        &&
                        !preg_match(
                            '/^[1-7]$/',
                            $child->getAttribute(
                                'size'
                            )
                        )
                    ) {

                        $child->removeAttribute(
                            'size'
                        );

                    }

                }


                $cleanNode(
                    $child
                );


                $child =
                    $next;

            }

        };


    $root =
        $dom->getElementById(
            'post-root'
        );


    if (!$root) {
        return '';
    }


    $cleanNode(
        $root
    );


    /*
    |--------------------------------------------------------------------------
    | Lấy HTML bên trong root
    |--------------------------------------------------------------------------
    */

    $result = '';


    foreach (
        $root->childNodes
        as $child
    ) {

        $result .=
            $dom->saveHTML(
                $child
            );

    }


    return trim(
        $result
    );
}
