<?php

/**
 * Controller class for admin pages.
 *
 * @package SpeedInChina
 */
if ( !defined( 'ABSPATH' ) ) {
    exit;
}
/**
 * Free-build stub for the PRO REST parent class.
 *
 * In the premium build, GPSIC_Features_API (admin/RestAPI/class-features.php)
 * is loaded inside the `is__premium_only` block in speed-in-china.php and the
 * controller inherits get_features() / subscribe() / get_latest_notification()
 * from it. In the free build, the Freemius deployer strips the require_once
 * for that file — so we declare a no-op stub here to keep the `extends`
 * relationship valid. Class-existence check ensures the stub is harmless in
 * the premium build (the real class loads first).
 */
if ( !class_exists( 'GPSIC_Features_API' ) ) {
    class GPSIC_Features_API {
        public function get_features( $slug = 'speed-test' ) {
            return array();
        }

        public function subscribe( $post = array() ) {
            return array(
                'message' => '',
            );
        }

        public function get_latest_notification() {
            return null;
        }

    }

}
/**
 * Admin controller for Speed in China.
 *
 * Constructor wires up the free-tier hooks unconditionally and the premium
 * hooks inside an `is__premium_only` block (stripped from the free wp.org
 * build by the Freemius CodeArtisan deployer).
 */
class SPEEINCH_AdminController extends GPSIC_Features_API {
    public function __construct() {
        // ---- Free-tier hooks (always registered) -------------------------------
        add_action( 'admin_menu', array($this, 'speed_in_china_page_callback') );
        add_action( 'admin_enqueue_scripts', array($this, 'speed_in_china_scripts_enqueue') );
        // Speed test (free + PRO use the same handler; plan-aware cooldown inside).
        add_action( 'wp_ajax_speeinch_run_api_manual', array($this, 'speed_in_china_run_api_manual') );
        // Resource analysis & Google Font localization.
        add_action( 'wp_ajax_speeinch_run_analysis', array($this, 'speeinch_run_analysis_callback') );
        add_action( 'wp_ajax_speeinch_toggle_google_fonts', array($this, 'speeinch_toggle_google_fonts_callback') );
        add_action( 'wp_ajax_speeinch_clear_font_cache', array($this, 'speeinch_clear_font_cache_callback') );
        // is__premium_only
    }

    /**
     * Register Speed in China menu.
     */
    public function speed_in_china_page_callback() {
        add_menu_page(
            __( 'Speed in China', 'speed-in-china' ),
            __( 'Speed in China', 'speed-in-china' ),
            'manage_options',
            'speed-in-china',
            array($this, 'speed_in_china_menu_callback'),
            'dashicons-performance',
            100
        );
    }

    /**
     * Render the admin page based on the current tab.
     */
    public function speed_in_china_menu_callback() {
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- page load context, no data mutation.
        $tab = ( isset( $_GET['tab'] ) ? sanitize_text_field( wp_unslash( $_GET['tab'] ) ) : '' );
        switch ( $tab ) {
            case 'optimizations':
                require_once SPEEINCH_PLUGIN_PATH . 'admin/templates/optimizations.php';
                break;
            case 'log':
                // is__premium_only
                break;
            case 'schedule':
                // is__premium_only
                break;
            case 'bundle':
                // is__premium_only
                break;
            default:
                require_once SPEEINCH_PLUGIN_PATH . 'admin/templates/form.php';
                break;
        }
    }

    /**
     * Enqueue admin scripts and styles.
     *
     * @param string $hook The current admin page hook.
     */
    public function speed_in_china_scripts_enqueue( $hook ) {
        if ( 'toplevel_page_speed-in-china' !== $hook ) {
            return;
        }
        wp_enqueue_style(
            'speeinch-admin-style',
            SPEEINCH_PLUGIN_URL . 'assets/css/speeinch-admin.css',
            array(),
            SPEEINCH_PLUGIN_VERSION
        );
        /*
         * Lottie player web component — bundled production build of LottieFiles'
         * `@lottiefiles/dotlottie-web` (MIT-licensed, GPL-compatible). The bundle
         * ships with full attribution at:
         *
         *   assets/js/vendor/dotlottie/LICENSE      (upstream MIT)
         *   assets/js/vendor/dotlottie/README.md    (package, version, source URL,
         *                                            why bundled, and the wp.org
         *                                            reviewer notice on the single
         *                                            `eval()` site inside the player
         *                                            — Lottie expression engine,
         *                                            unreachable with our static
         *                                            Speedometer.json input)
         *
         * Bumping the version: bump both `'2.0.8'` here AND the version line in
         * the bundled README so the two never drift.
         */
        wp_enqueue_script(
            'lottie-player',
            SPEEINCH_PLUGIN_URL . 'assets/js/vendor/dotlottie/lottie-player.js',
            array(),
            '2.0.8',
            true
        );
        wp_enqueue_script(
            'speeinch-admin-script',
            SPEEINCH_PLUGIN_URL . 'assets/js/speeinch-admin.js',
            array('jquery', 'wp-api-request', 'lottie-player'),
            SPEEINCH_PLUGIN_VERSION,
            true
        );
        wp_localize_script( 'speeinch-admin-script', 'speeinch', array(
            'ajaxurl'      => admin_url( 'admin-ajax.php' ),
            'current_page' => ( isset( $_GET['tab'] ) && !empty( $_GET['tab'] ) ? sanitize_text_field( wp_unslash( $_GET['tab'] ) ) : 'speed-test' ),
            'siteurl'      => site_url(),
            'nonce'        => wp_create_nonce( 'wp_rest' ),
            'ajax_nonce'   => wp_create_nonce( 'speeinch_nonce' ),
            'debug'        => defined( 'SPEEINCH_DEBUG' ) && SPEEINCH_DEBUG,
        ) );
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- page load context, no data mutation.
        $current_tab = ( isset( $_GET['tab'] ) ? sanitize_text_field( wp_unslash( $_GET['tab'] ) ) : '' );
        // Optimizations tab assets (free + PRO).
        if ( 'optimizations' === $current_tab ) {
            wp_enqueue_style(
                'speeinch-optimizations',
                SPEEINCH_PLUGIN_URL . 'admin/css/speeinch-optimizations.css',
                array(),
                SPEEINCH_PLUGIN_VERSION
            );
            wp_enqueue_script(
                'speeinch-optimizations',
                SPEEINCH_PLUGIN_URL . 'admin/js/speeinch-optimizations.js',
                array('jquery'),
                SPEEINCH_PLUGIN_VERSION,
                true
            );
            $saved_analysis = get_option( 'speeinch_analysis_results', null );
            $js_data = array();
            if ( $saved_analysis && !empty( $saved_analysis['summary'] ) ) {
                $js_data['savedAnalysis'] = $saved_analysis;
            }
            wp_localize_script( 'speeinch-optimizations', 'speeinchOptimizations', $js_data );
        }
        // is__premium_only
    }

    /**
     * AJAX: Run Speed Test via Globalping API.
     *
     * Plan-aware cooldown: free=30d, personal=7d, pro=1d, agency=1h. In the
     * free build the plan checks all return false and the cooldown falls
     * through to 30d, so this handler is shared between free and PRO builds.
     */
    public function speed_in_china_run_api_manual() {
        check_ajax_referer( 'speeinch_nonce', 'security' );
        if ( !current_user_can( 'manage_options' ) ) {
            wp_send_json_error( __( 'Unauthorized.', 'speed-in-china' ) );
        }
        // Cloud-bound: must have explicit opt-in to call our backend (R3).
        if ( function_exists( 'speeinch_can_collect_data' ) && !speeinch_can_collect_data() ) {
            wp_send_json_error( array(
                'message'       => __( 'Connect to run China-side speed tests.', 'speed-in-china' ),
                'reconnect_url' => ( function_exists( 'speeinch_reconnect_url' ) ? speeinch_reconnect_url() : admin_url( 'plugins.php' ) ),
            ) );
        }
        // Cooldown check — bypass only in plugin-dev mode.
        $next_run_time = get_option( 'speeinch_manual_api_run' );
        $current_time = time();
        $bypass_for_dev = defined( 'SPEEINCH_DEBUG' ) && SPEEINCH_DEBUG;
        if ( !$bypass_for_dev && $current_time < $next_run_time ) {
            $next_date = wp_date( 'F j, Y', $next_run_time );
            /* translators: %s: next available test date */
            wp_send_json_error( sprintf( __( '⏳ Next free test available: %s. Upgrade for more frequent tests — Personal (weekly), Pro (daily), Agency (hourly).', 'speed-in-china' ), $next_date ) );
        }
        // Build the API request to our Globalping proxy.
        $api_url = SPEEINCH_API_BASE_URL . '/wp-json/speed-in-china-api/v1/speed-test';
        // In plugin-dev mode (.local hosts that Globalping cannot reach), override
        // the probed URL to a public host. Defaults to https://example.com so
        // no real domain is silently probed if a reviewer enables SPEEINCH_DEBUG;
        // developers can override via wp-config.php:
        // define( 'SPEEINCH_DEBUG_TEST_URL', 'https://your-public-staging-url' );
        $test_url = ( $bypass_for_dev ? ( defined( 'SPEEINCH_DEBUG_TEST_URL' ) ? SPEEINCH_DEBUG_TEST_URL : 'https://example.com' ) : site_url() );
        // Determine current plan for API-side rate limiting.
        $current_plan = 'free';
        if ( function_exists( 'speeinch_fs' ) ) {
            $fs = speeinch_fs();
            if ( is_object( $fs ) && method_exists( $fs, 'is_plan' ) ) {
                if ( $fs->is_plan( 'agency', true ) ) {
                    $current_plan = 'agency';
                } elseif ( $fs->is_plan( 'pro', true ) ) {
                    $current_plan = 'pro';
                } elseif ( $fs->is_plan( 'personal', true ) ) {
                    $current_plan = 'personal';
                }
            }
        }
        // Outbound request metadata. The `X-SIC-*` header names are part of an
        // HTTP contract with our backend at api.speedinchinaplugin.com.
        $timestamp = time();
        $headers = array(
            'Content-Type'    => 'application/json',
            'X-SIC-Timestamp' => $timestamp,
            'X-SIC-Site'      => site_url(),
        );
        $response = wp_remote_post( $api_url, array(
            'headers' => $headers,
            'body'    => wp_json_encode( array(
                'url'  => $test_url,
                'plan' => $current_plan,
            ) ),
            'timeout' => 120,
        ) );
        if ( is_wp_error( $response ) ) {
            speeinch_log( 'API request failed: ' . $response->get_error_message() );
            wp_send_json_error( __( 'Speed test request failed: ', 'speed-in-china' ) . $response->get_error_message() );
        }
        $code = wp_remote_retrieve_response_code( $response );
        $raw_body = wp_remote_retrieve_body( $response );
        $body = json_decode( $raw_body, true );
        if ( 200 !== $code || empty( $body['data'] ) ) {
            $error = ( isset( $body['message'] ) ? $body['message'] : (( isset( $body['data'] ) ? $body['data'] : 'HTTP ' . $code )) );
            speeinch_log( 'API error (HTTP ' . $code . '): ' . $error . ' | Raw: ' . substr( $raw_body, 0, 500 ) );
            wp_send_json_error( __( 'Speed test failed: ', 'speed-in-china' ) . $error );
        }
        $data = $body['data'];
        // Plan-aware cooldown.
        $next_time = strtotime( '+30 day', time() );
        if ( 'personal' === $current_plan ) {
            $next_time = strtotime( '+7 day', time() );
        } elseif ( 'pro' === $current_plan ) {
            $next_time = strtotime( '+1 day', time() );
        } elseif ( 'agency' === $current_plan ) {
            $next_time = strtotime( '+1 hour', time() );
        }
        update_option( 'speeinch_manual_api_run', $next_time );
        update_option( 'speeinch_manual_api_run_last', time() );
        // Store result in history (keep last 10 results).
        $history = get_option( 'speeinch_speed_test_history', array() );
        array_unshift( $history, array(
            'timestamp'      => current_time( 'mysql' ),
            'timestamp_gmt'  => gmdate( 'Y-m-d H:i:s' ),
            'domain'         => ( isset( $data['domain'] ) ? $data['domain'] : '' ),
            'measurement_id' => ( isset( $data['measurement_id'] ) ? $data['measurement_id'] : '' ),
            'locations'      => ( isset( $data['locations'] ) ? $data['locations'] : array() ),
            'suggestions'    => ( isset( $data['suggestions'] ) ? $data['suggestions'] : array() ),
        ) );
        $history = array_slice( $history, 0, 10 );
        update_option( 'speeinch_speed_test_history', $history );
        wp_send_json_success( $data );
    }

    /**
     * AJAX: Run China resource analysis.
     */
    public function speeinch_run_analysis_callback() {
        check_ajax_referer( 'speeinch_nonce', 'security' );
        if ( !current_user_can( 'manage_options' ) ) {
            wp_send_json_error( __( 'Unauthorized.', 'speed-in-china' ) );
        }
        $results = SPEEINCH_Resource_Analysis::analyze();
        update_option( 'speeinch_analysis_results', $results );
        wp_send_json_success( $results );
    }

    /**
     * AJAX: Toggle Google Font localization.
     */
    public function speeinch_toggle_google_fonts_callback() {
        check_ajax_referer( 'speeinch_nonce', 'security' );
        if ( !current_user_can( 'manage_options' ) ) {
            wp_send_json_error( __( 'Unauthorized.', 'speed-in-china' ) );
        }
        $enabled = ( isset( $_POST['enabled'] ) ? sanitize_text_field( wp_unslash( $_POST['enabled'] ) ) : '0' );
        update_option( SPEEINCH_Google_Fonts::OPTION_ENABLED, '1' === $enabled );
        if ( '1' === $enabled ) {
            // Fetch the site's homepage to find Google Font URLs.
            $site_url = home_url( '/' );
            $response = wp_remote_get( $site_url, array(
                'timeout'    => 30,
                'user-agent' => 'SpeedInChina/1.0 Font Scanner',
            ) );
            $cached = 0;
            $families = array();
            if ( !is_wp_error( $response ) ) {
                $html = wp_remote_retrieve_body( $response );
                if ( preg_match_all( '/href=["\']([^"\']*fonts\\.googleapis\\.com[^"\']*)["\']/', $html, $matches ) ) {
                    foreach ( $matches[1] as $font_url ) {
                        // Strict host validation — the regex match alone is bypassable
                        // by URLs like https://attacker.test/?x=fonts.googleapis.com.
                        if ( !SPEEINCH_Google_Fonts::is_allowed_google_host( $font_url, SPEEINCH_Google_Fonts::GOOGLE_FONTS_API_HOST ) ) {
                            continue;
                        }
                        $result = SPEEINCH_Google_Fonts::get_local_font_url( $font_url );
                        if ( $result ) {
                            ++$cached;
                            $parsed_families = SPEEINCH_Google_Fonts::parse_font_families( $font_url );
                            $families = array_merge( $families, $parsed_families );
                        }
                    }
                }
            }
            $families = array_unique( $families );
            $family_list = ( !empty( $families ) ? implode( ', ', $families ) : 'none found' );
            wp_send_json_success( array(
                'enabled'  => true,
                'cached'   => $cached,
                'families' => $families,
                'message'  => ( $cached > 0 ? sprintf( __( 'Enabled! Cached %1$d stylesheet(s): %2$s. Fonts will be served locally on the next page load.', 'speed-in-china' ), $cached, $family_list ) : __( 'Enabled. No Google Font stylesheets found on your homepage — fonts will be localized automatically when detected.', 'speed-in-china' ) ),
            ) );
        } else {
            wp_send_json_success( array(
                'enabled' => false,
                'message' => __( 'Google Font localization disabled.', 'speed-in-china' ),
            ) );
        }
    }

    /**
     * AJAX: Clear Google Font local cache.
     */
    public function speeinch_clear_font_cache_callback() {
        check_ajax_referer( 'speeinch_nonce', 'security' );
        if ( !current_user_can( 'manage_options' ) ) {
            wp_send_json_error( __( 'Unauthorized.', 'speed-in-china' ) );
        }
        SPEEINCH_Google_Fonts::clear_cache();
        wp_send_json_success( array(
            'message' => __( 'Font cache cleared successfully.', 'speed-in-china' ),
        ) );
    }

    /*
    	================================================================
    * PREMIUM-ONLY METHODS
    * ================================================================
    *
    * Action registrations for the methods below live inside the
    * `is__premium_only` block in __construct(). The method bodies themselves
    * stay in the class — they're only invoked when an action is registered.
    *
    * Wrapping the methods in `// __premium_only` line markers is brittle for
    * multi-line definitions; the cleaner approach is gating the hook
    * registration so the method never runs in the free build (and never
    * appears in the wp.org plugin's surface area).
    */
    public function speed_in_china_cron_add_monthly( $schedules ) {
        $schedules['monthly'] = array(
            'interval' => MONTH_IN_SECONDS,
            'display'  => __( 'Once Monthly', 'speed-in-china' ),
        );
        return $schedules;
    }

    public function speed_in_china_filter_log() {
        check_ajax_referer( 'speeinch_nonce', 'security' );
        if ( !current_user_can( 'manage_options' ) ) {
            wp_send_json_error( __( 'Unauthorized.', 'speed-in-china' ) );
        }
        if ( empty( $_POST['option'] ) ) {
            wp_send_json_error( __( 'Please select an option to filter', 'speed-in-china' ) );
        }
        $option = absint( $_POST['option'] );
        global $wpdb;
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- targeted lookup by option_id PK.
        $record = $wpdb->get_var( $wpdb->prepare( "SELECT `option_value` FROM {$wpdb->options} WHERE `option_id` = %d", $option ) );
        $record = maybe_unserialize( $record );
        if ( $record ) {
            if ( isset( $record['data']['summary']['result'] ) && 'visible' === $record['data']['summary']['result'] ) {
                wp_send_json_success( $record['data'] );
            } else {
                wp_send_json_error( ( isset( $record['data']['summary']['description'] ) ? $record['data']['summary']['description'] : __( 'Test result unavailable.', 'speed-in-china' ) ) );
            }
        } else {
            wp_send_json_error( __( 'Unable to fetch the log. Reload the page and try again...', 'speed-in-china' ) );
        }
    }

    public function speed_in_china_run_api() {
        check_ajax_referer( 'speeinch_nonce', 'security' );
        if ( !current_user_can( 'manage_options' ) ) {
            wp_send_json_error( __( 'Unauthorized.', 'speed-in-china' ) );
        }
        // Cloud-bound: must have explicit opt-in to schedule cron-driven tests (R3).
        if ( function_exists( 'speeinch_can_collect_data' ) && !speeinch_can_collect_data() ) {
            wp_send_json_error( array(
                'message'       => __( 'Connect to enable scheduled tests.', 'speed-in-china' ),
                'reconnect_url' => ( function_exists( 'speeinch_reconnect_url' ) ? speeinch_reconnect_url() : admin_url( 'plugins.php' ) ),
            ) );
        }
        global $speeinch_fs;
        if ( $speeinch_fs->is_plan( 'free', true ) ) {
            wp_send_json_error( sprintf( 
                /* translators: %1$s: current plan name (bolded), %2$s: upgrade link */
                __( 'Scheduling status updates is not allowed on %1$s plan, %2$s', 'speed-in-china' ),
                '<b>' . ucfirst( __( 'Free', 'speed-in-china' ) ) . '</b>',
                '<a href="' . esc_url( $speeinch_fs->get_upgrade_url() ) . '">' . __( 'Upgrade here', 'speed-in-china' ) . '</a>'
             ) );
        }
        if ( empty( $_POST['speeinch_cron_on'] ) ) {
            if ( wp_next_scheduled( 'speeinch_cron_hook' ) ) {
                $timestamp = wp_next_scheduled( 'speeinch_cron_hook' );
                wp_unschedule_event( $timestamp, 'speeinch_cron_hook' );
            }
            delete_option( 'speeinch_cron_schedule' );
            $cron_status = false;
        } else {
            $cron_status = true;
            if ( empty( $_POST['frequency'] ) ) {
                wp_send_json_error( __( 'Please select a frequency', 'speed-in-china' ) );
            }
            $frequency = sanitize_text_field( wp_unslash( $_POST['frequency'] ) );
            $show = false;
            if ( $speeinch_fs->is_plan( 'personal', true ) ) {
                $show = array('monthly', 'weekly');
            } elseif ( $speeinch_fs->is_plan( 'pro', true ) ) {
                $show = array('monthly', 'weekly', 'daily');
            } elseif ( $speeinch_fs->is_plan( 'agency', true ) ) {
                $show = array(
                    'monthly',
                    'weekly',
                    'daily',
                    'hourly'
                );
            }
            if ( !$show || !in_array( $frequency, $show, true ) ) {
                wp_send_json_error( sprintf( 
                    /* translators: %1$s: frequency name, %2$s: upgrade link */
                    __( 'You are not allowed to schedule %1$s updates, %2$s', 'speed-in-china' ),
                    $frequency,
                    '<a href="' . esc_url( $speeinch_fs->get_upgrade_url() ) . '">' . __( 'Upgrade here', 'speed-in-china' ) . '</a>'
                 ) );
            }
            if ( wp_next_scheduled( 'speeinch_cron_hook' ) ) {
                $timestamp = wp_next_scheduled( 'speeinch_cron_hook' );
                wp_unschedule_event( $timestamp, 'speeinch_cron_hook' );
            }
            if ( get_option( 'speeinch_cron_schedule' ) === $frequency ) {
                wp_send_json_error( sprintf( 
                    /* translators: %1$s: frequency name */
                    __( 'Cron is already scheduled to run %1$s.', 'speed-in-china' ),
                    $frequency
                 ) );
            }
            $response = wp_schedule_event( time(), $frequency, 'speeinch_cron_hook' );
            if ( $response ) {
                update_option( 'speeinch_cron_schedule', $frequency );
            }
        }
        update_option( 'speeinch_cron_on', $cron_status );
        wp_send_json_success( __( 'Settings saved successfully!', 'speed-in-china' ) );
    }

    public function speed_in_china_admin_status_bar() {
        check_ajax_referer( 'speeinch_nonce', 'security' );
        if ( !current_user_can( 'manage_options' ) ) {
            wp_send_json_error( __( 'Unauthorized.', 'speed-in-china' ) );
        }
        if ( !isset( $_POST['option'] ) ) {
            wp_send_json_error( __( 'Please select an option to filter', 'speed-in-china' ) );
        }
        $option = sanitize_text_field( wp_unslash( $_POST['option'] ) );
        update_option( 'speeinch_admin_bar_status', ( 'true' === $option ? 1 : 0 ) );
        wp_send_json_success();
    }

    public function admin_bar_item( WP_Admin_Bar $admin_bar ) {
        $admin_item = array(
            'id'     => 'speeinch-status',
            'parent' => 'top-secondary',
            'group'  => null,
            'href'   => admin_url( 'admin.php?page=speed-in-china' ),
        );
        $speeinch_manual_api_run = get_option( 'speeinch_manual_api_run' );
        $speeinch_manual_api_run_last = get_option( 'speeinch_manual_api_run_last' );
        $manual_log = get_option( 'speeinch_cron_log-' . gmdate( 'Y-m-d H:i', $speeinch_manual_api_run_last ) );
        if ( $speeinch_manual_api_run && !empty( $manual_log['data']['servers'] ) ) {
            /* translators: %1$s: date of last check */
            $title = sprintf( __( 'Speed in China Status: Not Blocked as of %1$s', 'speed-in-china' ), gmdate( 'F d, Y', $speeinch_manual_api_run_last ) );
            $meta = array(
                'title' => __( 'Speed in China Status: Not Blocked', 'speed-in-china' ),
                'class' => 'speeinch-green',
            );
        } elseif ( $speeinch_manual_api_run && empty( $manual_log['data']['servers'] ) ) {
            /* translators: %1$s: date of last check */
            $title = sprintf( __( 'Speed in China Status: Blocked as of %1$s', 'speed-in-china' ), gmdate( 'F d, Y', $speeinch_manual_api_run_last ) );
            $meta = array(
                'title' => __( 'Speed in China Status: Blocked', 'speed-in-china' ),
                'class' => 'speeinch-red',
            );
        } else {
            $title = __( 'Speed in China Status: Check', 'speed-in-china' );
            $meta = array(
                'title' => __( 'Speed in China Status: Check', 'speed-in-china' ),
                'class' => 'speeinch-gray',
            );
        }
        $admin_item['title'] = $title;
        $admin_item['meta'] = $meta;
        $admin_bar->add_menu( $admin_item );
    }

    public function speeinch_custom_style() {
        wp_add_inline_style( 'admin-bar', '.speeinch-gray{ background: #434343 !important; }
			 .speeinch-green{ background: #6aa84f !important; }
			 .speeinch-red{ background: #cc0000 !important; }
			 .speeinch-yellow{ color: #ffc800 !important; }' );
    }

    public function speeinch_plugin_routes() {
        register_rest_route( 'speed-in-china/v1', '/administration/dismiss-notification', array(
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => array($this, 'dismiss_notification'),
            'permission_callback' => function () {
                return current_user_can( 'manage_options' );
            },
        ) );
    }

    public function dismiss_notification( WP_REST_Request $request ) {
        $latest_notification = $this->get_latest_notification();
        if ( isset( $latest_notification['id'] ) ) {
            update_user_meta( get_current_user_id(), 'china_speed_last_notification_id', $latest_notification['id'] );
        }
        return rest_ensure_response( array(
            'status' => 'ok',
        ) );
    }

    public function speeinch_get_all_features_callback() {
        check_ajax_referer( 'speeinch_nonce', 'security' );
        if ( !current_user_can( 'manage_options' ) ) {
            wp_send_json_error( __( 'Unauthorized.', 'speed-in-china' ) );
        }
        $valid_slugs = array(
            'speed-test',
            'optimizations',
            'log',
            'schedule'
        );
        $slug = ( empty( $_POST['current_page'] ) ? 'speed-test' : sanitize_text_field( wp_unslash( $_POST['current_page'] ) ) );
        if ( !in_array( $slug, $valid_slugs, true ) ) {
            $slug = 'speed-test';
        }
        $force = !empty( $_POST['force_refresh'] );
        if ( $force ) {
            delete_transient( 'speeinch_features_' . $slug );
        }
        $transient = get_transient( 'speeinch_features_' . $slug );
        if ( !empty( $transient ) ) {
            wp_send_json_success( $transient );
        }
        $response = $this->get_features( $slug );
        if ( !empty( $response ) && isset( $response['data'] ) ) {
            set_transient( 'speeinch_features_' . $slug, $response['data'], HOUR_IN_SECONDS );
        }
        wp_send_json_success( ( isset( $response['data'] ) ? $response['data'] : $response ) );
    }

    public function speeinch_features_contact_callback() {
        check_ajax_referer( 'speeinch_nonce', 'security' );
        if ( !current_user_can( 'manage_options' ) ) {
            wp_send_json_error( __( 'Unauthorized.', 'speed-in-china' ) );
        }
        if ( empty( $_POST['features'] ) ) {
            wp_send_json_error( __( 'Please select at least one feature.', 'speed-in-china' ) );
        }
        if ( empty( $_POST['email'] ) ) {
            wp_send_json_error( __( 'Please enter email address.', 'speed-in-china' ) );
        }
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified above.
        $body = $this->subscribe( wp_unslash( $_POST ) );
        wp_send_json_success( ( isset( $body['message'] ) ? $body['message'] : '' ) );
    }

    public function speeinch_admin_notice() {
        if ( !current_user_can( 'manage_options' ) ) {
            return;
        }
        // Cloud-bound: skip fetching notifications until the user has opted in.
        if ( function_exists( 'speeinch_can_collect_data' ) && !speeinch_can_collect_data() ) {
            return;
        }
        $latest_notification = $this->get_latest_notification();
        if ( empty( $latest_notification ) || !is_array( $latest_notification ) || empty( $latest_notification['id'] ) ) {
            return;
        }
        if ( intval( get_user_meta( get_current_user_id(), 'china_speed_last_notification_id', true ) ) === intval( $latest_notification['id'] ) ) {
            return;
        }
        $protocols = wp_allowed_protocols();
        if ( !in_array( 'data', $protocols, true ) ) {
            $protocols[] = 'data';
        }
        $content = wp_kses( $latest_notification['content'], $this->china_speed_content_allowed_html_tags(), $protocols );
        echo '<div id="china-speed-notification-container" class="notice notice-info is-dismissible">
				<h2>' . esc_html( $latest_notification['title'] ) . '</h2>
				' . wp_kses_post( $content ) . '
			  </div>';
    }

    public function china_speed_content_allowed_html_tags() {
        return array(
            'a'      => array(
                'href'   => array(),
                'target' => array(),
                'alt'    => array(),
            ),
            'br'     => array(),
            'video'  => array(
                'width'  => array(),
                'height' => array(),
            ),
            'source' => array(
                'src'  => array(),
                'type' => array(),
            ),
            'strong' => array(
                'style' => array(),
            ),
            'sub'    => array(
                'style' => array(),
            ),
            'sup'    => array(
                'style' => array(),
            ),
            's'      => array(
                'style' => array(),
            ),
            'i'      => array(
                'style' => array(),
            ),
            'u'      => array(
                'style' => array(),
            ),
            'span'   => array(
                'align' => array(),
                'class' => array(),
                'type'  => array(),
                'id'    => array(),
                'style' => array(),
                'data'  => array(),
            ),
            'h1'     => array(
                'align' => array(),
                'class' => array(),
                'type'  => array(),
                'id'    => array(),
                'style' => array(),
                'data'  => array(),
            ),
            'h2'     => array(
                'align' => array(),
                'class' => array(),
                'type'  => array(),
                'id'    => array(),
                'style' => array(),
                'data'  => array(),
            ),
            'h3'     => array(
                'align' => array(),
                'class' => array(),
                'type'  => array(),
                'id'    => array(),
                'style' => array(),
                'data'  => array(),
            ),
            'ol'     => array(
                'align' => array(),
                'class' => array(),
                'type'  => array(),
                'id'    => array(),
                'style' => array(),
                'data'  => array(),
            ),
            'ul'     => array(
                'align' => array(),
                'class' => array(),
                'type'  => array(),
                'id'    => array(),
                'style' => array(),
                'data'  => array(),
            ),
            'li'     => array(
                'align' => array(),
                'class' => array(),
                'type'  => array(),
                'id'    => array(),
                'style' => array(),
                'data'  => array(),
            ),
            'em'     => array(
                'align' => array(),
                'class' => array(),
                'type'  => array(),
                'id'    => array(),
                'style' => array(),
                'data'  => array(),
            ),
            'hr'     => array(),
            'p'      => array(
                'align' => array(),
                'class' => array(),
                'type'  => array(),
                'id'    => array(),
                'style' => array(),
                'data'  => array(),
            ),
            'img'    => array(
                'align'  => array(),
                'class'  => array(),
                'type'   => array(),
                'id'     => array(),
                'style'  => array(),
                'src'    => array(),
                'alt'    => array(),
                'href'   => array(),
                'rel'    => array(),
                'target' => array(),
                'value'  => array(),
                'name'   => array(),
                'width'  => array(),
                'height' => array(),
                'data'   => array(),
                'title'  => array(),
            ),
        );
    }

    /**
     * AJAX: Save schedule settings (Pro/Agency).
     */
    public function speeinch_save_schedule_callback() {
        check_ajax_referer( 'speeinch_schedule_nonce', 'security' );
        if ( !current_user_can( 'manage_options' ) ) {
            wp_send_json_error( __( 'Unauthorized.', 'speed-in-china' ) );
        }
        // Cloud-bound: scheduled tests require opt-in (R3).
        if ( function_exists( 'speeinch_can_collect_data' ) && !speeinch_can_collect_data() ) {
            wp_send_json_error( array(
                'message'       => __( 'Connect to enable scheduled speed tests.', 'speed-in-china' ),
                'reconnect_url' => ( function_exists( 'speeinch_reconnect_url' ) ? speeinch_reconnect_url() : admin_url( 'plugins.php' ) ),
            ) );
        }
        global $speeinch_fs;
        $speeinch_is_pro = $speeinch_fs->is_plan( 'pro', true );
        $speeinch_is_agency = $speeinch_fs->is_plan( 'agency', true );
        if ( !($speeinch_is_pro || $speeinch_is_agency) ) {
            wp_send_json_error( __( 'Scheduling requires a Pro plan or higher.', 'speed-in-china' ) );
        }
        $enabled = !empty( $_POST['schedule_enabled'] );
        $frequency = ( isset( $_POST['schedule_frequency'] ) ? sanitize_text_field( wp_unslash( $_POST['schedule_frequency'] ) ) : 'weekly' );
        $allowed = array('monthly', 'weekly', 'daily');
        if ( $speeinch_is_agency ) {
            $allowed[] = 'hourly';
        }
        if ( !in_array( $frequency, $allowed, true ) ) {
            wp_send_json_error( __( 'Your plan does not include this frequency. Please upgrade.', 'speed-in-china' ) );
        }
        update_option( 'speeinch_schedule_enabled', $enabled );
        update_option( 'speeinch_schedule_frequency', $frequency );
        $hook = 'speeinch_scheduled_speed_test';
        wp_clear_scheduled_hook( $hook );
        $next_run = '';
        if ( $enabled ) {
            $recurrence_seconds = array(
                'hourly'  => HOUR_IN_SECONDS,
                'daily'   => DAY_IN_SECONDS,
                'weekly'  => WEEK_IN_SECONDS,
                'monthly' => 30 * DAY_IN_SECONDS,
            );
            $delay = ( isset( $recurrence_seconds[$frequency] ) ? $recurrence_seconds[$frequency] : WEEK_IN_SECONDS );
            $timestamp = time() + $delay;
            wp_schedule_event( $timestamp, $frequency, $hook );
            $next_run = wp_date( 'Y-m-d H:i:s', $timestamp );
            update_option( 'speeinch_schedule_next_run', $next_run );
        } else {
            delete_option( 'speeinch_schedule_next_run' );
        }
        wp_send_json_success( array(
            'message'  => ( $enabled ? __( 'Schedule saved successfully.', 'speed-in-china' ) : __( 'Scheduled tests disabled.', 'speed-in-china' ) ),
            'next_run' => $next_run,
        ) );
    }

    /**
     * WP-Cron callback: Run a scheduled speed test (Pro/Agency).
     */
    public static function speeinch_run_scheduled_test() {
        // Cloud-bound: hard early-return when user is not opted in.
        if ( function_exists( 'speeinch_can_collect_data' ) && !speeinch_can_collect_data() ) {
            if ( wp_next_scheduled( 'speeinch_scheduled_speed_test' ) ) {
                wp_clear_scheduled_hook( 'speeinch_scheduled_speed_test' );
            }
            return;
        }
        $api_url = SPEEINCH_API_BASE_URL . '/wp-json/speed-in-china-api/v1/speed-test';
        $test_url = site_url();
        global $speeinch_fs;
        $plan = 'free';
        if ( $speeinch_fs->is_plan( 'agency', true ) ) {
            $plan = 'agency';
        } elseif ( $speeinch_fs->is_plan( 'pro', true ) ) {
            $plan = 'pro';
        } elseif ( $speeinch_fs->is_plan( 'personal', true ) ) {
            $plan = 'personal';
        }
        $timestamp = time();
        $response = wp_remote_post( $api_url, array(
            'headers' => array(
                'Content-Type'    => 'application/json',
                'X-SIC-Timestamp' => $timestamp,
                'X-SIC-Site'      => site_url(),
            ),
            'body'    => wp_json_encode( array(
                'url'   => $test_url,
                'token' => md5( gmdate( 'm' ) ),
                'plan'  => $plan,
            ) ),
            'timeout' => 120,
        ) );
        if ( is_wp_error( $response ) ) {
            speeinch_log( 'Schedule speed test failed: ' . $response->get_error_message() );
            return;
        }
        $body = json_decode( wp_remote_retrieve_body( $response ), true );
        if ( empty( $body['data'] ) || !isset( $body['data']['locations'] ) ) {
            speeinch_log( 'Schedule: Invalid API response' );
            return;
        }
        $data = $body['data'];
        $history = get_option( 'speeinch_speed_test_history', array() );
        $history[] = array(
            'timestamp'   => wp_date( 'Y-m-d H:i:s' ),
            'domain'      => $data['domain'],
            'locations'   => $data['locations'],
            'suggestions' => ( isset( $data['suggestions'] ) ? $data['suggestions'] : array() ),
            'source'      => 'scheduled',
        );
        if ( count( $history ) > 50 ) {
            $history = array_slice( $history, -50 );
        }
        update_option( 'speeinch_speed_test_history', $history );
        $next = wp_next_scheduled( 'speeinch_scheduled_speed_test' );
        if ( $next ) {
            update_option( 'speeinch_schedule_next_run', wp_date( 'Y-m-d H:i:s', $next ) );
        }
    }

    /**
     * AJAX: Install a partner plugin from wordpress.org (Bundle tab).
     */
    public function speeinch_install_plugin_callback() {
        check_ajax_referer( 'speeinch_install_plugin', 'security' );
        if ( !current_user_can( 'install_plugins' ) ) {
            wp_send_json_error( __( 'You do not have permission to install plugins.', 'speed-in-china' ) );
        }
        $slug = ( isset( $_POST['slug'] ) ? sanitize_text_field( wp_unslash( $_POST['slug'] ) ) : '' );
        $allowed = array('china-payments-plugin', 'blocked-in-china');
        if ( !in_array( $slug, $allowed, true ) ) {
            wp_send_json_error( __( 'Plugin not allowed.', 'speed-in-china' ) );
        }
        include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
        include_once ABSPATH . 'wp-admin/includes/plugin-install.php';
        $api = plugins_api( 'plugin_information', array(
            'slug'   => $slug,
            'fields' => array(
                'sections' => false,
            ),
        ) );
        if ( is_wp_error( $api ) ) {
            wp_send_json_error( $api->get_error_message() );
        }
        $upgrader = new \Plugin_Upgrader(new \Automatic_Upgrader_Skin());
        $result = $upgrader->install( $api->download_link );
        if ( is_wp_error( $result ) ) {
            wp_send_json_error( $result->get_error_message() );
        }
        if ( false === $result ) {
            wp_send_json_error( __( 'Plugin installation failed.', 'speed-in-china' ) );
        }
        wp_send_json_success( array(
            'message' => __( 'Plugin installed successfully.', 'speed-in-china' ),
        ) );
    }

    /**
     * AJAX: Activate an installed partner plugin (Bundle tab).
     */
    public function speeinch_activate_plugin_callback() {
        check_ajax_referer( 'speeinch_activate_plugin', 'security' );
        if ( !current_user_can( 'activate_plugins' ) ) {
            wp_send_json_error( __( 'You do not have permission to activate plugins.', 'speed-in-china' ) );
        }
        $plugin_file = ( isset( $_POST['plugin_file'] ) ? sanitize_text_field( wp_unslash( $_POST['plugin_file'] ) ) : '' );
        $allowed_files = array('china-payments-plugin/wpstripeglobal.php', 'blocked-in-china/blocked-in-china.php');
        if ( !in_array( $plugin_file, $allowed_files, true ) ) {
            wp_send_json_error( __( 'Plugin not allowed.', 'speed-in-china' ) );
        }
        if ( !file_exists( WP_PLUGIN_DIR . '/' . $plugin_file ) ) {
            wp_send_json_error( __( 'Plugin file not found. Please install first.', 'speed-in-china' ) );
        }
        $result = activate_plugin( $plugin_file );
        if ( is_wp_error( $result ) ) {
            wp_send_json_error( $result->get_error_message() );
        }
        wp_send_json_success( array(
            'message' => __( 'Plugin activated successfully.', 'speed-in-china' ),
        ) );
    }

}

new SPEEINCH_AdminController();