<?php
/**
 * Plugin Name: __BRAND_NAME__ SEO Render
 * Description: Universal SEO rewrites — empty alt fix, og:image / twitter:image injection, viewport scalability fix, empty <h1> placeholder neutralization, optional H1/H2 injection from Rank Math title, optional rendering of buried SEO content blocks.
 * Version: 1.0
 * Author: Claude SEO playbook
 *
 * INSTALLATION (per-site, ~30s):
 * 1. Copy this file to /home/<domain>/public_html/wp-content/mu-plugins/<brand>-seo-render.php
 * 2. Replace the 3 placeholder tokens:
 *    __BRAND_NAME__     — used in alt-text fallbacks and plugin name
 *                         Example: "Trados", "Alta Janitorial", "demelos"
 *    __THEME_COLOR__    — hex color for any rendered SEO sections
 *                         Example: "#1e3a5f" (navy), "#0a2540" (dark blue), "#1f4e79" (alta navy)
 *    __TABLE_PREFIX__   — DB table prefix from wp-config.php
 *                         Example: "wp_" (most sites), "abh_" (altajan.com)
 * 3. chown to the site's user (e.g., demel3276, altaj4686, trado8506)
 * 4. chmod 644
 * 5. sudo /usr/local/lsws/bin/lswsctrl restart
 * 6. sudo rm -rf /usr/local/lsws/cachedata/<domain>/*
 *
 * ENABLE OPTIONAL FEATURES (uncomment if needed):
 * - "Render buried SEO blocks" — only useful if post_content contains DEMELOS_SEO_*-style markers
 *   that are hidden by Elementor canvas. See bottom of file.
 * - "H1 injection" — only if some pages truly lack an H1 (most well-built sites already have one)
 *
 * SAFE TO LEAVE ENABLED EVERYWHERE:
 * - Universal alt rewriter
 * - og:image / twitter:image injector
 * - Empty <h1> neutralizer
 * - viewport user-scalable=no stripper
 */
if (!defined('ABSPATH')) exit;

const __BRAND_NAME__SEO_RENDER_MARKER = '<!-- __BRAND_NAME__-seo-render -->';
const __BRAND_NAME__SEO_RENDER_END    = '<!-- /__BRAND_NAME__-seo-render -->';
const __BRAND_NAME__SEO_RENDER_H1     = 'data-__BRAND_NAME__-h1="1"';

/* ---------- HELPERS ---------- */

function __BRAND_NAME___alt_for_src(string $src): string {
    if ($src === '' || str_starts_with($src, 'data:')) return '';
    static $cache = [];
    if (isset($cache[$src])) return $cache[$src];

    // Strip thumbnail size suffix (e.g., -300x200)
    $base_url = preg_replace('/-\d+x\d+(\.\w+)$/', '$1', $src);

    // Look up by URL in media library
    global $wpdb;
    $like = '%' . $wpdb->esc_like(parse_url($base_url, PHP_URL_PATH) ?: $base_url) . '%';
    $row  = $wpdb->get_row($wpdb->prepare(
        "SELECT ID FROM {$wpdb->posts} WHERE post_type='attachment' AND guid LIKE %s LIMIT 1",
        $like
    ));
    if ($row) {
        $alt = get_post_meta($row->ID, '_wp_attachment_image_alt', true);
        if (is_string($alt) && trim($alt) !== '') return $cache[$src] = $alt;
        $att = get_post($row->ID);
        if ($att && trim($att->post_title) !== '') {
            return $cache[$src] = ucwords(trim(preg_replace('/[-_]+/', ' ', $att->post_title)));
        }
    }

    // Fallback: humanize filename
    $base = pathinfo($base_url, PATHINFO_FILENAME);
    $base = preg_replace('/-\d+x\d+$/', '', $base);
    $alt  = ucwords(preg_replace('/[-_]+/', ' ', $base));
    return $cache[$src] = ($alt !== '' ? "__BRAND_NAME__ — $alt" : '');
}

/* ---------- OPTIONAL: Render buried SEO blocks ---------- *
 * Uncomment if post_content contains markers like <!-- *_SEO_BLOCK_START --> that
 * Elementor canvas hides. Reads markers, renders below page.
 */
/*
add_action('wp_footer', function () {
    if (!is_singular()) return;
    global $post;
    if (!$post) return;

    $content = $post->post_content;
    $matches = [];
    if (preg_match('/<!--\s*([A-Z_]+)_INTRO_START\s*-->(.*?)<!--\s*\1_INTRO_END\s*-->/is', $content, $m)) $matches['intro'] = $m[2];
    if (preg_match('/<!--\s*([A-Z_]+)_TOC_START\s*-->(.*?)<!--\s*\1_TOC_END\s*-->/is', $content, $m))     $matches['toc']   = $m[2];
    if (preg_match('/<!--\s*([A-Z_]+)_SEO_BLOCK_START\s*-->(.*?)<!--\s*\1_SEO_BLOCK_END\s*-->/is', $content, $m)) $matches['block'] = $m[2];
    if (preg_match('/<!--\s*([A-Z_]+)_REFS_START\s*-->(.*?)<!--\s*\1_REFS_END\s*-->/is', $content, $m))   $matches['refs']  = $m[2];
    if (empty($matches)) return;

    $rendered = implode("\n", $matches);
    $rendered = preg_replace('/<!--\s*\/?wp:[a-z\-\/]+\s*-->/', '', $rendered);
    $rendered = do_shortcode($rendered);

    $rm_title = get_post_meta($post->ID, 'rank_math_title', true);
    $h1 = is_string($rm_title) && $rm_title !== ''
        ? (preg_split('/\s*[\|\—\-]\s*/', $rm_title)[0] ?? '')
        : (get_the_title($post) ?: '__BRAND_NAME__');
    $h1 = esc_html(trim($h1));

    echo "\n" . __BRAND_NAME__SEO_RENDER_MARKER . "\n";
    echo '<section class="__BRAND_NAME__-seo-rendered" style="background:#fff;border-top:4px solid __THEME_COLOR__;margin:0;padding:48px 24px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;color:#1a1a1a;line-height:1.65">';
    echo '<div style="max-width:1100px;margin:0 auto">';
    echo '<h1 ' . __BRAND_NAME__SEO_RENDER_H1 . ' style="font-family:Times New Roman,serif;color:__THEME_COLOR__;font-size:2.4rem;line-height:1.15;margin:0 0 18px;font-weight:700">' . $h1 . '</h1>';
    echo $rendered;
    echo '</div></section>';
    echo "\n" . __BRAND_NAME__SEO_RENDER_END . "\n";
}, 5);
*/

/* ---------- OPTIONAL: Inject H2 content section ---------- *
 * Uncomment if pages routinely have <2 H2s on the homepage and Rank Math flags
 * "No H2 found". Outputs 2 H2-led short content blurbs in a footer section.
 */
/*
add_action('wp_footer', function () {
    if (!is_singular()) return;
    global $post;
    if (!$post) return;
    $kw = (string)get_post_meta($post->ID, 'rank_math_focus_keyword', true);
    $kw_first = trim(strtok($kw, ',')) ?: 'our service';

    echo "\n<!-- __BRAND_NAME__-h2-injection -->\n";
    echo '<section class="__BRAND_NAME__-seo-context" style="background:#fafafa;border-top:3px solid __THEME_COLOR__;margin:0;padding:48px 24px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;color:#1a1a1a;line-height:1.7">';
    echo '<div style="max-width:1100px;margin:0 auto">';
    echo '<h2>About __BRAND_NAME__</h2><p>Brief blurb about the brand.</p>';
    echo '<h2>Why ' . esc_html(ucwords($kw_first)) . ' matters</h2><p>Brief blurb tying focus keyword to value proposition.</p>';
    echo '</div></section>';
}, 7);
*/

/* ---------- ALWAYS-ON: Output buffer (alt fix, viewport, og:image, empty H1) ---------- */

add_action('template_redirect', function () {
    ob_start(function ($html) {
        if (!is_string($html) || strlen($html) < 500) return $html;

        // 1. Empty <h1>...</h1> → <div>...</div>  (catches Bridge "icon_title" placeholders too)
        $html = preg_replace('/<h1([^>]*)>(\s*)<\/h1>/i', '<div$1>$2</div>', $html);

        // 2. Strip user-scalable=no from viewport (WCAG 1.4.4 compliance)
        $html = preg_replace(
            '/(<meta\s+name=["\']viewport["\']\s+content=["\'][^"\']*?)(?:\s*,\s*)?user-scalable\s*=\s*no([^"\']*?["\'])/i',
            '$1$2',
            $html
        );

        // 3. Demote injected H1 to H2 if a real H1 exists outside our render section
        $without_ours = $html;
        if (strpos($html, __BRAND_NAME__SEO_RENDER_MARKER) !== false) {
            $without_ours = preg_replace(
                '/' . preg_quote(__BRAND_NAME__SEO_RENDER_MARKER, '/') . '.*?' . preg_quote(__BRAND_NAME__SEO_RENDER_END, '/') . '/s',
                '',
                $html
            );
        }
        $other_h1_count = 0;
        if (preg_match_all('/<h1[^>]*>(.*?)<\/h1>/is', $without_ours, $mm)) {
            foreach ($mm[1] as $body) {
                if (trim(strip_tags($body)) !== '') $other_h1_count++;
            }
        }
        if ($other_h1_count > 0) {
            $html = preg_replace(
                '/<h1(\s+' . preg_quote(__BRAND_NAME__SEO_RENDER_H1, '/') . '[^>]*)>(.*?)<\/h1>/is',
                '<h2$1>$2</h2>',
                $html
            );
        }

        // 4. Universal alt rewriter — every <img> with empty/missing alt gets one
        $html = preg_replace_callback('/<img\b([^>]*)\/?>/i', function ($m) {
            $attrs = $m[1];
            $alt_match = preg_match('/\balt\s*=\s*(["\'])([^"\']*)\1/i', $attrs, $am);
            if ($alt_match && trim($am[2]) !== '') return $m[0];

            $src = '';
            if (preg_match('/\bdata-src\s*=\s*["\']([^"\']+)["\']/i', $attrs, $sm)) $src = $sm[1];
            elseif (preg_match('/\bsrc\s*=\s*["\']([^"\']+)["\']/i', $attrs, $sm)) $src = $sm[1];
            if ($src === '' || str_starts_with($src, 'data:')) {
                if (preg_match('/\bdata-src\s*=\s*["\']([^"\']+)["\']/i', $attrs, $sm)) $src = $sm[1];
            }

            $alt = __BRAND_NAME___alt_for_src($src);
            if ($alt === '') $alt = ' ';
            $alt_safe = esc_attr($alt);

            if ($alt_match) {
                $new_attrs = preg_replace('/\balt\s*=\s*(["\'])[^"\']*\1/i', 'alt="' . $alt_safe . '"', $attrs);
            } else {
                $new_attrs = rtrim($attrs) . ' alt="' . $alt_safe . '"';
            }
            return '<img' . $new_attrs . '>';
        }, $html);

        // 5. Inject og:image and twitter:image before </head> if absent
        if (strpos($html, '</head>') !== false) {
            $head_section = substr($html, 0, strpos($html, '</head>'));
            if (!preg_match('/property=["\']og:image["\']/', $head_section)) {
                $img = '';
                $qid = get_queried_object_id();
                if ($qid) {
                    $img = (string)get_post_meta($qid, 'rank_math_facebook_image', true);
                    if ($img === '') $img = (string)get_post_meta($qid, 'rank_math_twitter_image', true);
                    if ($img === '' && has_post_thumbnail($qid)) {
                        $thumb = get_the_post_thumbnail_url($qid, 'large');
                        if ($thumb) $img = $thumb;
                    }
                }
                if ($img === '') {
                    $opts = get_option('rank-math-options-titles', []);
                    if (is_front_page() || is_home() || $qid == get_option('page_on_front')) {
                        $img = (string)($opts['homepage_facebook_image'] ?? '');
                    }
                    if ($img === '') $img = (string)($opts['open_graph_image'] ?? '');
                }
                // Last-resort: extract from og:image:secure_url already in the head
                if ($img === '' && preg_match('/property=["\']og:image:secure_url["\']\s+content=["\']([^"\']+)["\']/', $head_section, $sm)) {
                    $img = $sm[1];
                }
                if ($img !== '') {
                    $esc = esc_url($img);
                    $tags = "<meta property=\"og:image\" content=\"$esc\" />\n<meta name=\"twitter:image\" content=\"$esc\" />\n";
                    $html = preg_replace('#</head>#i', $tags . '</head>', $html, 1);
                }
            }
        }

        return $html;
    }, 0, false);
}, 11);
