<?php
/**
 * Module: SEO Fix API (production-ready)
 * Route: POST /wp-json/myaio/v1/fix-issues
 *
 * Notes:
 *  - Keeps SAME issue cases and behavior you shared (working logic).
 *  - Adds function prefix: myaio_sac_
 *  - Uses shared permission callback from includes/sac-utils.php:
 *      myaio_sac_rest_permission_check
 */

if (!defined('ABSPATH')) exit;

// If utils are not loaded globally in your plugin bootstrap, include them here:
if ( ! function_exists( 'myaio_sac_rest_permission_check' ) ) {
	$path = plugin_dir_path( __FILE__ ) . '../includes/sac-utils.php';

	if ( file_exists( $path ) ) {
		require_once $path;
	}
}

// ---------------- API ROUTE ----------------
add_action('rest_api_init', function () {
    register_rest_route('myaio/v1', '/fix-issues', [
        'methods'  => 'POST',
        'callback' => 'myaio_sac_fix_issues',

        // ✅ Use shared API key validation
        'permission_callback' => function ($request) {
            if (function_exists('myaio_sac_rest_permission_check')) {
                return myaio_sac_rest_permission_check($request);
            }
            // If utils not loaded for some reason, fallback to deny (secure default)
            return new WP_Error('unauthorized', 'Invalid or unverified API key', ['status' => 401]);
        },
    ]);
});
add_action('wp_head', function () {
    if (!is_singular()) return;

    $post_id = get_queried_object_id();
    if (!$post_id) return;

    $robots = get_post_meta($post_id, '_myaio_meta_robots', true);
    if (!$robots) $robots = get_post_meta($post_id, '_myaio_seo_robots', true);

    $robots = trim((string) $robots);
    if ($robots === '') return;

    echo '<meta name="robots" content="' . esc_attr($robots) . '">' . "\n";
}, 1);
add_action('wp_head', function () {
    if (!is_singular()) return;

    $post_id = get_queried_object_id();
    if (!$post_id) return;

    $keywords = get_post_meta($post_id, '_myaio_meta_keywords', true);
    if (!$keywords) $keywords = get_post_meta($post_id, '_myaio_seo_keywords', true);

    $keywords = trim((string) $keywords);
    if ($keywords === '') return;

    echo '<meta name="keywords" content="' . esc_attr($keywords) . '">' . "\n";
}, 1);
// ---------------- META OUTPUT ----------------
add_filter('document_title_parts', function ($title) {
    if (is_singular()) {
        $seo_title = get_post_meta(get_the_ID(), '_myaio_seo_title', true);
        if (!empty($seo_title)) {
            $title['title'] = $seo_title;
        }
    }
    return $title;
});

add_filter('rank_math/frontend/description', function ($desc) {
    $custom = get_post_meta(get_the_ID(), '_myaio_meta_description', true);
    return !empty($custom) ? $custom : $desc;
});

// ---------------- MAIN CALLBACK ----------------
function myaio_sac_fix_issues(WP_REST_Request $request) {
    nocache_headers();

    // --- INPUT (same params) ---
    $url         = esc_url_raw($request->get_param('url'));
    $issues      = (array) ($request->get_param('issues') ?? []);
    $values      = (array) ($request->get_param('values') ?? []);
    $image_fixes = (array) ($request->get_param('image_fixes') ?? []);
    $mode        = strtolower(sanitize_text_field($request->get_param('mode') ?? 'apply'));

    if (empty($url)) {
        return new WP_REST_Response(['error' => 'Missing url'], 400);
    }

    $post_id = url_to_postid($url);
    if (!$post_id) {
        return new WP_REST_Response(['error' => 'Invalid URL / no post found'], 404);
    }

    $post = get_post($post_id);
    if (!$post) return new WP_REST_Response(['error' => 'Post not found'], 404);

    $content_original = (string) $post->post_content;
    $content          = $content_original;
    $title_original   = (string) $post->post_title;
    $fixed            = [];
    $skipped          = [];

    // --- PERMANENT CORE FIELD WRITES (keep same behavior) ---
    if ($mode !== 'dry-run') {
        if (!empty($values['title'])) {
            $perma_title = sanitize_text_field((string) $values['title']);
            update_post_meta($post_id, '_myaio_seo_title', $perma_title);
        }

        if (!empty($values['meta_description'])) {
            $perma_desc = sanitize_textarea_field((string) $values['meta_description']);
            update_post_meta($post_id, '_myaio_meta_description', $perma_desc);
            update_post_meta($post_id, '_myaio_seo_description', $perma_desc);
        }

        if (!empty($values['meta_keywords'])) {
            $perma_kw = sanitize_text_field((string) $values['meta_keywords']);
            update_post_meta($post_id, '_myaio_meta_keywords', $perma_kw);
            update_post_meta($post_id, '_myaio_seo_keywords', $perma_kw);
        }

        if (!empty($values['robots'])) {
            $perma_robots = strtolower(sanitize_text_field((string) $values['robots']));
            $perma_robots = preg_replace('/\s+/', '', $perma_robots); // index, follow -> index,follow

            update_post_meta($post_id, '_myaio_meta_robots', $perma_robots);
            update_post_meta($post_id, '_myaio_seo_robots', $perma_robots);

            // Optional compatibility with common SEO plugins
            update_post_meta($post_id, 'rank_math_robots', $perma_robots);
            update_post_meta($post_id, '_yoast_wpseo_meta-robots-noindex', strpos($perma_robots, 'noindex') !== false ? '1' : '2');
            update_post_meta($post_id, '_yoast_wpseo_meta-robots-nofollow', strpos($perma_robots, 'nofollow') !== false ? '1' : '2');
        }
    }

    // --- HELPERS ---
    $truncate_chars = function (string $text, int $limit) {
        $text = trim(preg_replace('/\s+/', ' ', $text));
        if (mb_strlen($text) <= $limit) return $text;
        $cut = mb_substr($text, 0, $limit + 1);
        $space = mb_strrpos($cut, ' ');
        if ($space !== false) $cut = mb_substr($cut, 0, $space);
        return rtrim($cut, " \t\n\r\0\x0B.,;:-_—") . '…';
    };

    $myaio_norm_text = function ($s) {
        $s = (string) $s;
        $s = html_entity_decode($s, ENT_QUOTES, 'UTF-8');
        $s = wp_strip_all_tags($s);
        $s = preg_replace('/\s+/u', ' ', $s);
        return trim($s);
    };

    $myaio_norm_html = function ($s) {
        $s = (string) $s;
        $s = html_entity_decode($s, ENT_QUOTES, 'UTF-8');
        $s = preg_replace('~<br\s*/?>~i', '<br>', $s);
        $s = preg_replace('/>\s+</', '><', $s);
        $s = preg_replace('/\s+/u', ' ', $s);
        return trim($s);
    };

    /**
     * Replace <hN>...</hN> inner HTML in a given HTML string
     * Supports:
     *  - h{N}_index (Nth occurrence of that tag in the string)
     *  - exact old_html match (innerHTML)
     *  - old_text match (whitespace tolerant)
     */
    $myaio_replace_heading_in_html = function (
        string $html_string,
        string $tag,
        string $replacement_inner,
        string $old_html = '',
        string $old_text = '',
        int $target_index = 0
    ) {
        if ($html_string === '') return [$html_string, false];

        $changed = false;
        $i = 0;

        // 1) Index-based (most reliable)
        if ($target_index > 0) {
            $new = preg_replace_callback(
                '/<' . $tag . '\b[^>]*>.*?<\/' . $tag . '>/is',
                function ($m) use (&$i, $target_index, $tag, $replacement_inner, &$changed) {
                    $i++;
                    if ($i !== $target_index) return $m[0];
                    $changed = true;
                    return preg_replace(
                        '/(<' . $tag . '\b[^>]*>).*?(<\/' . $tag . '>)/is',
                        '$1' . $replacement_inner . '$2',
                        $m[0]
                    );
                },
                $html_string
            );

            return [$new ?? $html_string, $changed];
        }

        // 2) Exact innerHTML match
        if ($old_html !== '') {
            $pattern = '/(<' . $tag . '\b[^>]*>)\s*' . preg_quote($old_html, '/') . '\s*(<\/' . $tag . '>)/is';
            $new = preg_replace($pattern, '$1' . $replacement_inner . '$2', $html_string, 1, $count);
            if (!empty($count)) $changed = true;
            return [$new ?? $html_string, $changed];
        }

        // 3) Text match (whitespace tolerant)
        if ($old_text !== '') {
            $old_text_re = preg_quote($old_text, '/');
            $old_text_re = str_replace('\ ', '\s+', $old_text_re);

            $pattern = '/(<' . $tag . '\b[^>]*>)\s*' . $old_text_re . '\s*(<\/' . $tag . '>)/is';
            $new = preg_replace($pattern, '$1' . $replacement_inner . '$2', $html_string, 1, $count);
            if (!empty($count)) $changed = true;
            return [$new ?? $html_string, $changed];
        }

        return [$html_string, false];
    };

    // --- APPLY FIXES ONLY FOR LISTED ISSUES (same switch/cases) ---
    foreach ($issues as $issue) {
        switch ($issue) {

            case 'Missing title':
                if (empty($title_original) || !empty($values['title'])) {
                    $seo_title = !empty($values['title'])
                        ? sanitize_text_field($values['title'])
                        : 'Untitled Page';

                    if ($mode !== 'dry-run') {
                        update_post_meta($post_id, '_myaio_seo_title', $seo_title);
                        update_post_meta($post_id, 'rank_math_title', $seo_title);
                        update_post_meta($post_id, '_yoast_wpseo_title', $seo_title);
                    }

                    $fixed[] = [
                        'issue' => $issue,
                        'fix'   => 'Set SEO title (stored in postmeta)',
                        'value' => $seo_title
                    ];
                }
                break;

            case 'Title exceeds 60 characters':
                if ((!empty($title_original) && mb_strlen($title_original) > 60) || !empty($values['title'])) {
                    $seo_title = !empty($values['title'])
                        ? sanitize_text_field($values['title'])
                        : $truncate_chars($title_original, 60);

                    if ($mode !== 'dry-run') {
                        update_post_meta($post_id, '_myaio_seo_title', $seo_title);
                        update_post_meta($post_id, 'rank_math_title', $seo_title);
                        update_post_meta($post_id, '_yoast_wpseo_title', $seo_title);
                    }

                    $fixed[] = [
                        'issue' => $issue,
                        'fix'   => !empty($values['title']) ? 'Updated SEO title' : 'Truncated SEO title',
                        'value' => $seo_title
                    ];
                }
                break;

            case 'Missing meta description':
                $desc = !empty($values['meta_description'])
                    ? sanitize_text_field($values['meta_description'])
                    : $truncate_chars(wp_strip_all_tags($content), 155);

                if ($mode !== 'dry-run') {
                    update_post_meta($post_id, '_myaio_meta_description', $desc);
                    update_post_meta($post_id, '_myaio_seo_description', $desc);
                    update_post_meta($post_id, 'rank_math_description', $desc);
                    update_post_meta($post_id, '_yoast_wpseo_metadesc', $desc);
                }

                $fixed[] = ['issue' => $issue, 'fix' => 'Added meta description', 'value' => $desc];
                break;

            case 'Meta description exceeds 160 characters':
                $existing = get_post_meta($post_id, '_myaio_meta_description', true);
                if (!empty($existing) && mb_strlen($existing) > 160) {
                    $desc = $truncate_chars($existing, 160);

                    if ($mode !== 'dry-run') {
                        update_post_meta($post_id, '_myaio_meta_description', $desc);
                        update_post_meta($post_id, '_myaio_seo_description', $desc);
                        update_post_meta($post_id, 'rank_math_description', $desc);
                        update_post_meta($post_id, '_yoast_wpseo_metadesc', $desc);
                    }

                    $fixed[] = ['issue' => $issue, 'fix' => 'Truncated meta description', 'value' => $desc];
                }
                break;

            case 'Missing meta keywords':
                $keywords = !empty($values['meta_keywords'])
                    ? sanitize_text_field($values['meta_keywords'])
                    : '';

                if ($mode !== 'dry-run' && !empty($keywords)) {
                    update_post_meta($post_id, '_myaio_meta_keywords', $keywords);
                    update_post_meta($post_id, '_myaio_seo_keywords', $keywords);
                }

                $fixed[] = ['issue' => $issue, 'fix' => 'Added meta keywords', 'value' => $keywords];
                break;

            case 'Meta keywords exceeds 255 characters':
                $existing = get_post_meta($post_id, '_myaio_meta_keywords', true);
                if (!empty($existing) && mb_strlen($existing) > 255) {
                    $keywords = $truncate_chars($existing, 255);

                    if ($mode !== 'dry-run') {
                        update_post_meta($post_id, '_myaio_meta_keywords', $keywords);
                        update_post_meta($post_id, '_myaio_seo_keywords', $keywords);
                    }

                    $fixed[] = ['issue' => $issue, 'fix' => 'Truncated meta keywords', 'value' => $keywords];
                }
                break;
            case 'Robots meta update':
            case 'Missing robots meta tag':

                $robots = !empty($values['robots'])
                    ? strtolower(sanitize_text_field((string) $values['robots']))
                    : '';

                $robots = preg_replace('/\s+/', '', $robots); // normalize: index, follow => index,follow

                if (empty($robots)) {
                    $skipped[] = [
                        'issue'  => $issue,
                        'reason' => 'No robots value provided in values[robots]'
                    ];
                    break;
                }

                if ($mode !== 'dry-run') {
                    update_post_meta($post_id, '_myaio_meta_robots', $robots);
                    update_post_meta($post_id, '_myaio_seo_robots', $robots);

                    // Optional compatibility with common SEO plugins
                    update_post_meta($post_id, 'rank_math_robots', $robots);
                    update_post_meta($post_id, '_yoast_wpseo_meta-robots-noindex', strpos($robots, 'noindex') !== false ? '1' : '2');
                    update_post_meta($post_id, '_yoast_wpseo_meta-robots-nofollow', strpos($robots, 'nofollow') !== false ? '1' : '2');

                    clean_post_cache($post_id);
                }

                $fixed[] = [
                    'issue' => $issue,
                    'fix'   => 'Set robots meta tag',
                    'value' => $robots,
                    'output'=> '<meta name="robots" content="' . $robots . '" />'
                ];
                break;

            // ----- Generic heading update (H1–H6) -----
            case 'Missing h1 tag':
            case 'Missing h2 tag':
            case 'Missing h3 tag':
            case 'Missing h4 tag':
            case 'Missing h5 tag':
            case 'Missing h6 tag':

                if (preg_match('/Missing h([1-6]) tag/i', $issue, $m)) {
                    $heading_level = (int) $m[1];
                } else {
                    break;
                }

                $new_text = !empty($values['h' . $heading_level]) ? sanitize_text_field($values['h' . $heading_level]) : '';
                $old_text = !empty($values['h' . $heading_level . '_old']) ? (string) $values['h' . $heading_level . '_old'] : '';

                $new_html = !empty($values['h' . $heading_level . '_html']) ? wp_kses_post((string) $values['h' . $heading_level . '_html']) : '';
                $old_html = !empty($values['h' . $heading_level . '_html_old']) ? (string) $values['h' . $heading_level . '_html_old'] : '';

                if ($old_text === '' && $old_html !== '') {
                    $old_text = $myaio_norm_text($old_html);
                }
                if ($new_text === '' && $new_html !== '') {
                    $new_text = $myaio_norm_text($new_html);
                }

                $target_index = !empty($values['h' . $heading_level . '_index']) ? (int) $values['h' . $heading_level . '_index'] : 0;

                $apply_inner_is_html = !empty($new_html);
                if (empty($new_text) && empty($new_html)) {
                    $skipped[] = ['issue' => $issue, 'reason' => 'No new heading value provided'];
                    break;
                }

                $pattern_tag = 'h' . $heading_level;
                $replacement_inner = $apply_inner_is_html ? $new_html : esc_html($new_text);

                // Check if this post uses Elementor
                $elementor_flag = get_post_meta($post_id, '_elementor_edit_mode', true);

                if ($elementor_flag === 'builder') {

                    [$data, $source] = myaio_sac_get_elementor_elements_data($post_id);

                    if (empty($data)) {
                        return new WP_REST_Response([
                            'error' => 'Elementor elements data not readable',
                            'decode_source' => $source,
                            'json_decode' => json_last_error_msg(),
                        ], 500);
                    }

                    $changed = false;

                    $replace_elementor = function (&$elements) use (
                        &$replace_elementor,
                        $myaio_replace_heading_in_html,
                        $myaio_norm_text,
                        $myaio_norm_html,
                        $old_text,
                        $old_html,
                        $new_text,
                        $new_html,
                        $apply_inner_is_html,
                        $heading_level,
                        $pattern_tag,
                        $replacement_inner,
                        $target_index,
                        &$changed
                    ) {
                        foreach ($elements as &$el) {
                            $wt = $el['widgetType'] ?? '';

                            // 1) Elementor Heading widget
                            if ($wt === 'heading' && isset($el['settings']['title'])) {
                                $tag = strtolower((string)($el['settings']['header_size'] ?? ''));
                                if ($tag === '' || $tag === 'h' . $heading_level) {

                                    $current_title = $myaio_norm_text($el['settings']['title']);
                                    $old_text_n    = $myaio_norm_text($old_text);
                                    $old_html_n    = $myaio_norm_text($old_html);

                                    $match = false;

                                    if ($old_text_n !== '' && $current_title === $old_text_n) {
                                        $match = true;
                                    } elseif ($old_html_n !== '' && $current_title === $old_html_n) {
                                        $match = true;
                                    }

                                    if ($match) {
                                        $el['settings']['title'] = ($new_text !== '' ? $new_text : wp_strip_all_tags($new_html));
                                        $el['settings']['header_size'] = 'h' . $heading_level;
                                        $changed = true;
                                    }
                                }
                            }

                            // 2) Elementor Text Editor widget
                            if ($wt === 'text-editor' && isset($el['settings']['editor']) && is_string($el['settings']['editor'])) {
                                [$new_editor, $did] = $myaio_replace_heading_in_html(
                                    (string)$el['settings']['editor'],
                                    $pattern_tag,
                                    $replacement_inner,
                                    (string)$old_html,
                                    (string)$old_text,
                                    $target_index
                                );
                                if ($did) {
                                    $el['settings']['editor'] = $new_editor;
                                    $changed = true;
                                }
                            }

                            // 3) Elementor HTML widget
                            if ($wt === 'html' && isset($el['settings']['html']) && is_string($el['settings']['html'])) {
                                [$new_html_block, $did] = $myaio_replace_heading_in_html(
                                    (string)$el['settings']['html'],
                                    $pattern_tag,
                                    $replacement_inner,
                                    (string)$old_html,
                                    (string)$old_text,
                                    $target_index
                                );
                                if ($did) {
                                    $el['settings']['html'] = $new_html_block;
                                    $changed = true;
                                }
                            }

                            // 4) Elementor Icon Box widget
                            if ($wt === 'icon-box') {
                                $keys = ['title_text', 'title', 'heading_title'];
                                foreach ($keys as $k) {
                                    if (isset($el['settings'][$k]) && is_string($el['settings'][$k])) {
                                        $current = (string) $el['settings'][$k];

                                        $match = false;
                                        if (!empty($old_text) && trim($current) === trim($old_text)) {
                                            $match = true;
                                        } elseif (!empty($old_html)) {
                                            $rendered_text = trim(wp_strip_all_tags($old_html));
                                            if ($rendered_text !== '' && trim($current) === $rendered_text) {
                                                $match = true;
                                            }
                                        }

                                        if ($match) {
                                            $el['settings'][$k] = $new_text !== '' ? $new_text : wp_strip_all_tags($new_html);
                                            $changed = true;
                                            break;
                                        }
                                    }
                                }
                            }

                            // 5) ElementsKit Heading
                            if ($wt === 'elementskit-heading' || $wt === 'elementskit-heading.default') {
                                $keys = ['title', 'heading_title', 'ekit_heading_title'];

                                foreach ($keys as $k) {
                                    if (!isset($el['settings'][$k]) || !is_string($el['settings'][$k])) {
                                        continue;
                                    }

                                    $current = (string)$el['settings'][$k];

                                    $cur_norm = $myaio_norm_html($current);
                                    $old_norm = $myaio_norm_html($old_html);
                                    $old_txtn = $myaio_norm_text($old_text);

                                    $match = false;

                                    if ($old_html !== '' && $cur_norm === $old_norm) {
                                        $match = true;
                                    }

                                    if (!$match && $old_html !== '') {
                                        $cur_txt = $myaio_norm_text(wp_strip_all_tags($current));
                                        $old_txt = $myaio_norm_text(wp_strip_all_tags($old_html));
                                        if ($cur_txt !== '' && $cur_txt === $old_txt) {
                                            $match = true;
                                        }
                                    }

                                    if (!$match && $old_txtn !== '') {
                                        $cur_txt = $myaio_norm_text(wp_strip_all_tags($current));
                                        if ($cur_txt === $old_txtn) {
                                            $match = true;
                                        }
                                    }

                                    if ($match) {
                                        $el['settings'][$k] = $apply_inner_is_html ? $new_html : esc_html($new_text);
                                        $changed = true;
                                        break;
                                    }
                                }
                            }

                            // 6) ElementsKit Icon Box
                            if ($wt === 'elementskit-icon-box' || $wt === 'elementskit-icon-box.default') {
                                if (isset($el['settings']['ekit_icon_box_title_text']) && is_string($el['settings']['ekit_icon_box_title_text'])) {

                                    $current = (string) $el['settings']['ekit_icon_box_title_text'];

                                    $cur_norm = $myaio_norm_text($current);
                                    $old_norm = $myaio_norm_text($old_text);

                                    if ($old_norm !== '' && $cur_norm === $old_norm) {
                                        $el['settings']['ekit_icon_box_title_text'] = ($new_text !== '' ? $new_text : wp_strip_all_tags((string)$new_html));
                                        $changed = true;

                                        if (isset($el['settings']['ekit_icon_box_title_size']) && is_string($el['settings']['ekit_icon_box_title_size'])) {
                                            $el['settings']['ekit_icon_box_title_size'] = 'h' . $heading_level;
                                        }
                                    }
                                }
                            }

                            // Elementor Call To Action widget
                            if ($wt === 'cta' || $wt === 'call-to-action' || $wt === 'elementor-cta') {
                                $title_keys = ['title', 'cta_title', 'heading', 'heading_text'];

                                foreach ($title_keys as $k) {
                                    if (!isset($el['settings'][$k]) || !is_string($el['settings'][$k])) continue;

                                    $current = (string) $el['settings'][$k];

                                    $cur_norm = $myaio_norm_text($current);
                                    $old_norm = $myaio_norm_text($old_text);

                                    if ($old_norm !== '' && $cur_norm === $old_norm) {

                                        $el['settings'][$k] = ($new_text !== '' ? $new_text : wp_strip_all_tags((string)$new_html));
                                        $changed = true;

                                        foreach (['title_tag', 'heading_tag', 'html_tag'] as $tk) {
                                            if (isset($el['settings'][$tk]) && is_string($el['settings'][$tk])) {
                                                $el['settings'][$tk] = 'h' . $heading_level;
                                            }
                                        }

                                        break;
                                    }
                                }
                            }

                            // Elementor Image Box widget
                            if ($wt === 'image-box') {
                                if (isset($el['settings']['title_text']) && is_string($el['settings']['title_text'])) {
                                    $current = (string) $el['settings']['title_text'];

                                    $cur_norm = $myaio_norm_text($current);
                                    $old_norm = $myaio_norm_text($old_text);

                                    if ($old_norm !== '' && $cur_norm === $old_norm) {
                                        $el['settings']['title_text'] = ($new_text !== '' ? $new_text : wp_strip_all_tags((string)$new_html));
                                        $changed = true;

                                        foreach (['title_size', 'title_tag', 'html_tag'] as $tk) {
                                            if (isset($el['settings'][$tk]) && is_string($el['settings'][$tk])) {
                                                $el['settings'][$tk] = 'h' . $heading_level;
                                            }
                                        }
                                    }
                                }
                            }

                            if (!empty($el['elements']) && is_array($el['elements'])) {
                                $replace_elementor($el['elements']);
                            }
                        }
                    };

                    $replace_elementor($data);

                    if ($changed && $mode !== 'dry-run') {
                        update_post_meta($post_id, '_elementor_data', wp_slash(wp_json_encode($data)));
                        if (class_exists('\Elementor\Plugin')) {
                            \Elementor\Plugin::$instance->files_manager->clear_cache();
                        }
                        clean_post_cache($post_id);
                    }

                    if ($changed) {
                        $fixed[] = [
                            'issue' => $issue,
                            'fix'   => "Updated Elementor H{$heading_level} (heading/text-editor/html)",
                            'old'   => ($old_html !== '' ? $old_html : $old_text),
                            'new'   => ($apply_inner_is_html ? $new_html : $new_text),
                            'index' => $target_index ?: null
                        ];
                    } else {
                        // fallback to post_content (same as your old logic)
                        $has_heading_any = (bool) preg_match('/<' . $pattern_tag . '\b[^>]*>.*?<\/' . $pattern_tag . '>/is', $content);

                        if ($target_index > 0) {
                            $i = 0;
                            $new_content = preg_replace_callback(
                                '/<' . $pattern_tag . '\b[^>]*>.*?<\/' . $pattern_tag . '>/is',
                                function ($m) use (&$i, $target_index, $pattern_tag, $replacement_inner) {
                                    $i++;
                                    if ($i !== $target_index) return $m[0];
                                    return preg_replace(
                                        '/(<' . $pattern_tag . '\b[^>]*>).*?(<\/' . $pattern_tag . '>)/is',
                                        '$1' . $replacement_inner . '$2',
                                        $m[0]
                                    );
                                },
                                $content
                            );
                        } elseif (!empty($old_html)) {
                            $pattern = '/(<' . $pattern_tag . '\b[^>]*>)\s*' . preg_quote($old_html, '/') . '\s*(<\/' . $pattern_tag . '>)/is';
                            $new_content = preg_replace($pattern, '$1' . $replacement_inner . '$2', $content, 1);
                        } elseif (!empty($old_text)) {
                            $old_text_re = preg_quote($old_text, '/');
                            $old_text_re = str_replace('\ ', '\s+', $old_text_re);
                            $pattern = '/(<' . $pattern_tag . '\b[^>]*>)\s*' . $old_text_re . '\s*(<\/' . $pattern_tag . '>)/is';
                            $new_content = preg_replace($pattern, '$1' . $replacement_inner . '$2', $content, 1);
                        } elseif (!$has_heading_any) {
                            $new_content = "<{$pattern_tag}>" . $replacement_inner . "</{$pattern_tag}>\n" . $content;
                        } else {
                            $new_content = $content;
                        }

                        if (($new_content ?? $content) !== $content && $mode !== 'dry-run') {
                            $result = wp_update_post([
                                'ID'           => $post_id,
                                'post_content' => $new_content
                            ], true);

                            if (is_wp_error($result)) {
                                return new WP_REST_Response([
                                    'error' => 'Update failed: ' . $result->get_error_message()
                                ], 500);
                            }

                            clean_post_cache($post_id);
                            $content = $new_content;

                            $fixed[] = [
                                'issue' => $issue,
                                'fix'   => "Fallback updated post_content H{$heading_level}",
                                'old'   => ($old_html !== '' ? $old_html : $old_text),
                                'new'   => ($apply_inner_is_html ? $new_html : $new_text),
                                'index' => $target_index ?: null
                            ];
                        } else {
                            $skipped[] = [
                                'issue'  => $issue,
                                'reason' => 'Elementor builder detected but no matching heading found in widget'
                            ];
                        }
                    }

                } else {
                    // raw post_content update (same old logic)
                    $has_heading_any = (bool) preg_match('/<' . $pattern_tag . '\b[^>]*>.*?<\/' . $pattern_tag . '>/is', $content);

                    if ($target_index > 0) {
                        $i = 0;
                        $new_content = preg_replace_callback(
                            '/<' . $pattern_tag . '\b[^>]*>.*?<\/' . $pattern_tag . '>/is',
                            function ($m) use (&$i, $target_index, $pattern_tag, $replacement_inner) {
                                $i++;
                                if ($i !== $target_index) return $m[0];
                                return preg_replace(
                                    '/(<' . $pattern_tag . '\b[^>]*>).*?(<\/' . $pattern_tag . '>)/is',
                                    '$1' . $replacement_inner . '$2',
                                    $m[0]
                                );
                            },
                            $content
                        );
                    } elseif (!empty($old_html)) {
                        $pattern = '/(<' . $pattern_tag . '\b[^>]*>)\s*' . preg_quote($old_html, '/') . '\s*(<\/' . $pattern_tag . '>)/is';
                        $new_content = preg_replace($pattern, '$1' . $replacement_inner . '$2', $content, 1);
                    } elseif (!empty($old_text)) {
                        $old_text_re = preg_quote($old_text, '/');
                        $old_text_re = str_replace('\ ', '\s+', $old_text_re);
                        $pattern = '/(<' . $pattern_tag . '\b[^>]*>)\s*' . $old_text_re . '\s*(<\/' . $pattern_tag . '>)/is';
                        $new_content = preg_replace($pattern, '$1' . $replacement_inner . '$2', $content, 1);
                    } elseif (!$has_heading_any) {
                        $new_content = "<{$pattern_tag}>" . $replacement_inner . "</{$pattern_tag}>\n" . $content;
                    } else {
                        $new_content = $content;
                    }

                    if (($new_content ?? $content) !== $content && $mode !== 'dry-run') {
                        $result = wp_update_post([
                            'ID'           => $post_id,
                            'post_content' => $new_content
                        ], true);

                        if (is_wp_error($result)) {
                            return new WP_REST_Response([
                                'error' => 'Update failed: ' . $result->get_error_message()
                            ], 500);
                        }

                        clean_post_cache($post_id);
                        $content = $new_content;

                        $fixed[] = [
                            'issue' => $issue,
                            'fix'   => $has_heading_any ? "Replaced H{$heading_level}" : "Inserted H{$heading_level}",
                            'value' => ($apply_inner_is_html ? $new_html : $new_text),
                            'index' => $target_index ?: null
                        ];
                    } else {
                        $skipped[] = [
                            'issue'  => $issue,
                            'reason' => 'No match found in post_content for provided old value / index'
                        ];
                    }
                }

                break;

            case 'Image missing alt':

                // --- Step 1: Store explicit fixes ---
                if (!empty($image_fixes)) {
                    $map = [];
                    foreach ($image_fixes as $fix) {
                        if (!empty($fix['target']) && !empty($fix['alt'])) {
                            $map[$fix['target']] = $fix['alt'];

                            $attachment_id = attachment_url_to_postid($fix['target']);
                            if ($attachment_id) {
                                update_post_meta($attachment_id, '_wp_attachment_image_alt', sanitize_text_field($fix['alt']));
                            }
                        }
                    }
                    update_option('myaio_image_alt_map', $map);

                    $fixed[] = ['issue' => $issue, 'fix' => 'Applied explicit alt fixes', 'count' => count($image_fixes)];
                }

                // --- Step 2: Force alt on all WordPress-generated images ---
                add_filter('wp_get_attachment_image_attributes', function ($attr, $attachment) {
                    $map = get_option('myaio_image_alt_map', []);
                    $url = wp_get_attachment_url($attachment->ID);

                    if (isset($map[$url])) {
                        $attr['alt'] = $map[$url];
                    } elseif (empty($attr['alt'])) {
                        $alt_text = pathinfo(basename($url), PATHINFO_FILENAME);
                        $alt_text = preg_replace('/[_-]+/', ' ', $alt_text);
                        $attr['alt'] = $alt_text;
                        update_post_meta($attachment->ID, '_wp_attachment_image_alt', sanitize_text_field($attr['alt']));
                    }
                    return $attr;
                }, 10, 2);

                // --- Step 3: Fix all <img> in post content dynamically ---
                $content = preg_replace_callback(
                    '/<img\b([^>]*)\bsrc=["\']([^"\']+)["\']([^>]*)>/i',
                    function ($matches) {
                        $before = $matches[1];
                        $src = $matches[2];
                        $after = $matches[3];
                        $map = get_option('myaio_image_alt_map', []);

                        if (preg_match('/\balt=["\'](.*?)["\']/', $matches[0], $altMatch)) {
                            $alt_text = trim($altMatch[1]);
                        } else {
                            $alt_text = '';
                        }

                        if (isset($map[$src])) {
                            $alt_text = $map[$src];
                        }

                        if (empty($alt_text)) {
                            $parsed_url = wp_parse_url($src);
                            $path       = isset($parsed_url['path']) ? $parsed_url['path'] : '';
                            $alt_text   = pathinfo(basename($path), PATHINFO_FILENAME);
                            $alt_text   = preg_replace('/[_-]+/', ' ', $alt_text);
                        }

                        if (preg_match('/\balt=["\'].*?["\']/', $matches[0])) {
                            $img_tag = preg_replace('/\balt=["\'].*?["\']/', 'alt="' . esc_attr($alt_text) . '"', $matches[0]);
                        } else {
                            $img_tag = '<img alt="' . esc_attr($alt_text) . '" ' . $before . ' src="' . $src . '"' . $after . '>';
                        }

                        $attachment_id = attachment_url_to_postid($src);
                        if ($attachment_id) {
                            update_post_meta($attachment_id, '_wp_attachment_image_alt', sanitize_text_field($alt_text));
                        }

                        return $img_tag;
                    },
                    $content
                );

                // --- Step 4: Save content and purge cache ---
                if ($mode !== 'dry-run') {
                    wp_update_post([
                        'ID' => $post_id,
                        'post_content' => $content,
                    ]);

                    clean_post_cache($post_id);
                    if (function_exists('wp_cache_flush')) wp_cache_flush();
                }

                $fixed[] = ['issue' => $issue, 'fix' => 'Added/Updated alt attributes for all images with immediate front-end effect'];
                break;
        }
    }

    // --- SAVE CHANGES (content only) ---
    if ($mode !== 'dry-run' && $content !== $content_original) {
        $result = wp_update_post(['ID' => $post_id, 'post_content' => $content], true);
        if (is_wp_error($result)) {
            return new WP_REST_Response(['error' => 'Update failed: ' . $result->get_error_message()], 500);
        }

        // Core cache
        clean_post_cache($post_id);
        if (function_exists('wp_cache_flush')) wp_cache_flush();

        // WP Rocket
        if (function_exists('rocket_clean_post')) {
            rocket_clean_post($post_id);
        }

        // W3 Total Cache
        if (function_exists('w3tc_pgcache_flush_post')) {
            w3tc_pgcache_flush_post($post_id);
        }

        // LiteSpeed
        if (class_exists('LiteSpeed_Cache_API')) {
            LiteSpeed_Cache_API::purge_post($post_id);
        }

        // WP-Optimize
        if (function_exists('wp_optimize_cache_purge')) {
            wp_optimize_cache_purge();
        }
    }

    // --- RESPONSE (same keys) ---
    return rest_ensure_response([
        'post_id'        => (int) $post_id,
        'url'            => get_permalink($post_id),
        'fixed_issues'   => $fixed,
        'skipped_issues' => $skipped,
        'mode'           => $mode
		    ]);
}

function myaio_sac_get_elementor_elements_data($post_id) {
    $raw  = get_post_meta($post_id, '_elementor_data', true);
    $json = is_string($raw) ? wp_unslash($raw) : '';

    // First try normal decode
    $data = $json ? json_decode($json, true) : null;

    if (is_array($data)) {
        return [$data, 'meta_json'];
    }

    // Fallback: Elementor Document API (most reliable)
    if (class_exists('\Elementor\Plugin')) {
        $doc = \Elementor\Plugin::$instance->documents->get($post_id);
        if ($doc) {
            $elements = $doc->get_elements_data();
            if (is_array($elements) && !empty($elements)) {
                return [$elements, 'elementor_document'];
            }
        }
    }

    return [[], 'failed'];
}
