<?php

/**
 * EmailObfuscator
 *
 * Obfuscates email addresses as HTML decimal entities so they render
 * correctly in browsers but are invisible to most bots and scrapers.
 *
 * Example:
 *   obfuscateEmail('foo@example.com')
 *   → &#102;&#111;&#111;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;
 *
 *   obfuscateMailto('foo@example.com')
 *   → &#109;&#97;&#105;&#108;&#116;&#111;&#58;&#102;&#111;&#111;&#64;&#101;&#120;...
 *     (usable inside href="...")
 *
 *   obfuscateEmailText('Contact xamxam@erg.be for help')
 *   → 'Contact &#120;&#97;...@&#101;&#114;... for help'
 *     (replaces email addresses found in plain text)
 *
 * Usage:
 *   require_once APP_ROOT . '/src/EmailObfuscator.php';
 *   echo '<a href="' . EmailObfuscator::mailto('name@domain.com') . '">' . EmailObfuscator::email('name@domain.com') . '</a>';
 */

class EmailObfuscator
{
    /**
     * Obfuscate an email address for display in HTML content.
     */
    public static function email(string $address): string
    {
        return self::encode($address);
    }

    /**
     * Obfuscate a mailto: link for use in an href attribute.
     * Returns 'mailto:' followed by the obfuscated email.
     */
    public static function mailto(string $address): string
    {
        return 'mailto:' . self::encode($address);
    }

    /**
     * Replace plain-text email addresses in a string with obfuscated versions.
     * Only replaces addresses that appear as bare text (not already inside HTML entities).
     */
    public static function emailText(string $text): string
    {
        return preg_replace_callback(
            '/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/',
            fn(array $m) => self::encode($m[0]),
            $text
        );
    }

    /**
     * Obfuscate a mailto: URL found in text (e.g. from Markdown or user input).
     * Matches 'mailto:user@domain.com' pattern.
     */
    public static function mailtoInText(string $text): string
    {
        return preg_replace_callback(
            '/mailto:([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,})/i',
            fn(array $m) => 'mailto:' . self::encode($m[1]),
            $text
        );
    }

    /**
     * Post-process rendered HTML: obfuscate all mailto: links and their
     * visible text (email addresses) so bots can't scrape them.
     *
     * Matches patterns like:
     *   <a href="mailto:foo@bar.com">foo@bar.com</a>
     *   <a href="mailto:foo@bar.com">some text</a>
     *
     * In the second case, only the href is obfuscated (the link text is kept).
     */
    public static function obfuscateHtml(string $html): string
    {
        // Match <a> tags whose href starts with mailto:
        return preg_replace_callback(
            '/<a\s[^>]*href="mailto:([^"]+)"[^>]*>(.*?)<\/a>/is',
            function (array $m): string {
                $email      = $m[1];
                $linkText   = $m[2];
                $obfuscated = self::encode($email);

                // If the link text is the email itself, replace it too
                $text = strip_tags($linkText);
                if ($text === $email) {
                    $linkText = self::encode($linkText);
                }

                // Rebuild the tag with obfuscated values
                return str_replace(
                    ['mailto:' . $email, '>' . $m[2] . '<'],
                    ['mailto:' . $obfuscated, '>' . $linkText . '<'],
                    $m[0]
                );
            },
            $html
        );
    }

    // ── Private ───────────────────────────────────────────────────────────

    private static function encode(string $s): string
    {
        $out = '';
        $len = strlen($s);
        for ($i = 0; $i < $len; $i++) {
            $out .= '&#' . ord($s[$i]) . ';';
        }
        return $out;
    }
}
