<?php
/**
 * ExploreXR License Handler
 * 
 * Handles license activation, validation, and admin interfaces
 */

// Exit if accessed directly
if (!defined('ABSPATH')) {
    exit;
}

/**
 * License Handler Class
 */
class ExploreXR_License_Handler {
    /**
     * The single instance of the class.
     */
    protected static $_instance = null;

    /**
     * The API endpoint for license validation
     */
    private $api_url = 'https://expoxr.com/wp-json/lmfwc/v2/';

    /**
     * The License Manager integration instance
     */
    private $license_integration = null;

    /**
     * The license data option key
     */
    private $option_key = 'explorexr_license_data';

    /**
     * The license key option
     */
    private $license_key_option = 'explorexr_license_key';

    /**
     * The license tier option
     */
    private $license_tier_option = 'explorexr_license_tier';

    /**
     * The included addons option
     */
    private $included_addons_option = 'explorexr_included_addons';

    /**
     * The previous addon selections option (for tier changes)
     */
    private $previous_addons_option = 'explorexr_previous_addon_selections';

    /**
     * License tiers and their details
     */
    private $license_tiers = [
        'free' => [
            'name' => 'Free',
            'description' => 'Basic 3D model viewer with limited features. Add-ons require a valid license.',
            'max_addons' => 0, // No addons allowed without a license
        ],
        'trial' => [
            'name' => 'Trial',
            'description' => '14-day trial with full Plus-tier features — 4 add-ons of your choice. Key prefix: EXTR-',
            'max_addons' => 4, // Same as Plus
        ],
        'pro' => [
            'name' => 'Pro',
            'description' => 'Advanced 3D model viewer with premium features and 2 add-ons of your choice.',
            'max_addons' => 2,
        ],
        'plus' => [
            'name' => 'Plus',
            'description' => 'Enhanced 3D model viewer with premium features and 4 add-ons of your choice.',
            'max_addons' => 4,
        ],
        'ultra' => [
            'name' => 'Ultra',
            'description' => 'Ultimate 3D model viewer with all premium features and all add-ons included.',
            'max_addons' => -1, // -1 means unlimited
        ],
    ];
      /**
     * Available add-ons and their details
     */
    private $addons = [
        'annotations' => [
            'name' => 'Annotations Add-on',
            'description' => 'Add interactive annotations to your 3D models.',
            'path' => 'explorexr-annotations-addon/explorexr-annotations-addon.php',
            'icon' => 'dashicons-admin-comments',
        ],
        'ar' => [
            'name' => 'AR Add-on',
            'description' => 'Enable Augmented Reality features for your 3D models.',
            'path' => 'explorexr-ar-addon/explorexr-ar-addon.php',
            'icon' => 'dashicons-smartphone',
        ],
        'camera' => [
            'name' => 'Camera Controls Add-on',
            'description' => 'Add advanced camera controls to your 3D models.',
            'path' => 'explorexr-camera-addon/explorexr-camera-addon.php',
            'icon' => 'dashicons-camera',
        ],
        'materials' => [
            'name' => 'Materials Variants Add-on',
            'description' => 'Switch between different material variants for your 3D models.',
            'path' => 'explorexr-materials-addon/explorexr-materials-addon.php',
            'icon' => 'dashicons-art',
        ],
        'loading' => [
            'name' => 'Loading Options Add-on',
            'description' => 'Advanced loading customization with progress bars, overlays, and styling options.',
            'path' => 'explorexr-loading-addon/explorexr-loading-addon.php',
            'icon' => 'dashicons-clock',
        ],
        'environment' => [
            'name' => 'Environment Add-on',
            'description' => 'Environment lighting controls (skybox, HDRI, tone mapping, exposure, shadows).',
            'path' => 'explorexr-environment-addon/explorexr-environment-addon.php',
            'icon' => 'dashicons-admin-site-alt3',
        ],
        'woocommerce' => [
            'name' => 'WooCommerce Add-on',
            'description' => 'Integrate 3D models with WooCommerce products.',
            'path' => 'explorexr-woocommerce-addon/explorexr-woocommerce-addon.php',
            'icon' => 'dashicons-cart',
        ],
        'animation' => [
            'name' => 'Animation Add-on',
            'description' => 'Advanced animation controls for 3D models with timeline and triggers.',
            'path' => 'explorexr-animation-addon/explorexr-animation-addon.php',
            'icon' => 'dashicons-controls-play',
        ],
        'morphing' => [
            'name' => 'Morphing Add-on',
            'description' => 'Morph target animations for shape transformations.',
            'path' => 'explorexr-morphing-addon/explorexr-morphing-addon.php',
            'icon' => 'dashicons-image-flip-horizontal',
        ],
        'mouse3d' => [
            'name' => 'Mouse 3D Add-on',
            'description' => 'Enhanced 3D mouse interaction and controls.',
            'path' => 'explorexr-mouse3d-addon/explorexr-mouse3d-addon.php',
            'icon' => 'dashicons-move',
        ],
        'post-processing' => [
            'name' => 'Post Processing Add-on',
            'description' => 'Visual effects and filters for enhanced rendering.',
            'path' => 'explorexr-post-processing-addon/explorexr-post-processing-addon.php',
            'icon' => 'dashicons-admin-appearance',
        ],
        'draggable' => [
            'name' => 'Draggable Add-on',
            'description' => 'Model dragging and position controls.',
            'path' => 'explorexr-draggable-addon/explorexr-draggable-addon.php',
            'icon' => 'dashicons-move',
        ],
        'debug' => [
            'name' => 'Debug Add-on',
            'description' => 'Debug logging console and development tools.',
            'path' => 'explorexr-debug-addon/explorexr-debug-addon.php',
            'icon' => 'dashicons-info',
        ],
    ];

    /**
     * Debug logging helper - only logs when WP_DEBUG is enabled
     */
    private function debug_log($message) {
        if (defined('WP_DEBUG') && WP_DEBUG) {
            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Intentional debug logging behind WP_DEBUG
            error_log($message);
        }
    }

    /**
     * Redact license keys before writing to logs.
     */
    private function redact_license_key($license_key) {
        if (empty($license_key)) {
            return '';
        }

        return substr($license_key, 0, 8) . '...' . substr($license_key, -4);
    }

    /**
     * Main License Handler Instance.
     *
     * Ensures only one instance of the License Handler is loaded or can be loaded.
     *
     * @return ExploreXR_License_Handler
     */
    public static function instance() {
        if (is_null(self::$_instance)) {
            self::$_instance = new self();
        }
        return self::$_instance;
    }

    /**
     * Constructor.
     *
     * Private to enforce singleton pattern. Use ExploreXR_License_Handler::instance().
     */
    private function __construct() {
        // Load license configuration constants
        $this->load_license_config();
        
        // Initialize License API integration if available
        if (class_exists('ExploreXR_License_API_Integration')) {
            $this->license_integration = ExploreXR_License_API_Integration::instance();
        } else {
            // License handler can work without API integration
            $this->license_integration = null;
        }
        
        // Setup hooks
        $this->setup_hooks();
        
        // Initialize license if not already set
        $this->maybe_initialize_license();
    }    /**
     * Setup necessary hooks
     */
    private function setup_hooks() {
        // License activation/deactivation AJAX handlers - supporting both naming conventions
        add_action('wp_ajax_explorexr_activate_license', array($this, 'activate_license_ajax'));
        add_action('wp_ajax_explorexr_premium_activate_license', array($this, 'activate_license_ajax')); // JS compat
        add_action('wp_ajax_explorexr_deactivate_license_improved', array($this, 'deactivate_license_ajax'));
        add_action('wp_ajax_explorexr_premium_deactivate_license_improved', array($this, 'deactivate_license_ajax')); // JS compat
        add_action('wp_ajax_explorexr_select_addon', array($this, 'select_addon_ajax'));
        add_action('wp_ajax_explorexr_premium_select_addon', array($this, 'select_addon_ajax')); // JS compat
        add_action('wp_ajax_explorexr_check_tier_transition', array($this, 'check_tier_transition_ajax'));
        add_action('wp_ajax_explorexr_validate_license', array($this, 'validate_license_ajax'));
        add_action('wp_ajax_explorexr_premium_validate_license_real_time', array($this, 'validate_license_ajax')); // JS compat
        // Note: check_api_config_ajax and get_license_tiers_ajax removed — methods not implemented.
        // Tier data is served via the license page template instead.
        
        // Check license status periodically
        add_action('admin_init', array($this, 'maybe_check_license'));
        
        // License menu is now registered in admin-menu.php to avoid duplicates
        // Removed: add_action('admin_menu', array($this, 'add_license_menu'), 60);
        
        // Check addon activation based on license
        add_action('admin_init', array($this, 'check_addon_activation'));
        
        // Add admin notices for license issues
        add_action('admin_notices', array($this, 'maybe_show_license_expiry_notice'));
        add_action('admin_notices', array($this, 'maybe_show_stuck_license_notice'));
        // Trial upgrade hint — shown daily from day 8 of trial
        add_action('admin_notices', array($this, 'maybe_show_trial_upgrade_hint'));
    }

    /**
     * Initialize default license settings
     */
    private function maybe_initialize_license() {
        $license_data = get_option($this->option_key, false);
        
        if (!$license_data) {
            update_option($this->option_key, array(
                'key' => '',
                'status' => 'inactive',
                'tier' => 'free',
                'expires' => '',
                'error' => '',
                'last_check' => 0,
            ));
            
            update_option($this->included_addons_option, array());
            update_option($this->previous_addons_option, array());
        }
    }

    /**
     * Get the current license data
     */
    public function get_license_data() {
        $license_data = get_option($this->option_key, array());
        
        // Ensure all default keys exist even if the database option is incomplete
        $defaults = array(
            'key' => '',
            'status' => 'inactive',
            'tier' => 'free',
            'expires' => '',
            'error' => '',
            'last_check' => 0,
        );
        
        return wp_parse_args($license_data, $defaults);
    }

    /**
     * Get the Pro license details
     */
    public function get_pro_license() {
        $license_data = $this->get_license_data();
        $included_addons = $this->get_included_addons();
        
        return array(
            'name' => 'ExploreXR Premium',
            'description' => 'Unlock premium features and add-ons for your 3D model viewers.',
            'status' => isset($license_data['status']) ? $license_data['status'] : 'inactive',
            'license_key' => isset($license_data['key']) ? $license_data['key'] : '',
            'license_error' => isset($license_data['error']) ? $license_data['error'] : '',
            'tier' => isset($license_data['tier']) ? $license_data['tier'] : 'free',
            'icon' => 'dashicons-star-filled',
            'included_addons' => $included_addons,
        );
    }

    /**
     * Get license tiers
     */
    public function get_license_tiers() {
        return $this->license_tiers;
    }

    /**
     * Get available add-ons
     */
    public function get_addons() {
        return $this->addons;
    }

    /**
     * Get the selected/included add-ons for current license
     * This returns the addons the user has chosen to activate within their license limits
     * 
     * @return array Array of addon slugs
     */
    public function get_selected_addons() {
        return get_option($this->included_addons_option, array());
    }
    
    /**
     * Get included add-ons for current license (alias for get_selected_addons)
     * @deprecated Use get_selected_addons() instead
     */
    public function get_included_addons() {
        return $this->get_selected_addons();
    }

    /**
     * Get the currently activated addons (replaces get_included_addons for display)
     */
    public function get_activated_addons() {
        $activated_addons = array();
        
        foreach ($this->addons as $addon_id => $addon) {
            if (is_plugin_active($addon['path'])) {
                $activated_addons[] = $addon_id;
            }
        }
        
        return $activated_addons;
    }

    /**
     * Get the maximum number of add-ons allowed for the current license
     */
    public function get_max_addons() {
        $license_data = $this->get_license_data();
        $tier = isset($license_data['tier']) ? $license_data['tier'] : 'free';
        
        if (isset($this->license_tiers[$tier])) {
            return $this->license_tiers[$tier]['max_addons'];
        }
        
        return 0; // Default to 0 for unknown tiers
    }

    /**
     * Check if an add-on is included in the current license
     */
    public function is_addon_included($addon_id) {
        // If not a valid add-on, return false
        if (!isset($this->addons[$addon_id])) {
            return false;
        }
        
        $license_data = $this->get_license_data();
        
        // Ultra includes all add-ons
        if (isset($license_data['tier']) && $license_data['tier'] === 'ultra') {
            return true;
        }
        
        // For other tiers, check if it's in the included add-ons
        $included_addons = $this->get_included_addons();
        return in_array($addon_id, $included_addons);
    }

    /**
     * Check if an add-on is licensed
     * 
     * @param string $addon_id The ID of the add-on
     * @return boolean True if the add-on is licensed, false otherwise
     */
    public function is_addon_licensed($addon_id) {
        // This is an alias for is_addon_included to maintain compatibility
        return $this->is_addon_included($addon_id);
    }

    /**
     * Check if license is active
     */
    public function is_license_active() {
        $license_data = $this->get_license_data();
        return isset($license_data['status']) && $license_data['status'] === 'active';
    }

    /**
     * Get current license tier
     */
    public function get_license_tier() {
        $license_data = $this->get_license_data();
        return $license_data['tier'];
    }

    /**
     * Check if user has a Pro (or higher) license
     * 
     * @return boolean True if user has a Pro, Plus or Ultra license
     */
    public function is_pro_licensed() {
        if (!$this->is_license_active()) {
            return false;
        }
        
        $license_tier = $this->get_license_tier();
        return in_array($license_tier, ['trial', 'pro', 'plus', 'ultra']);
    }    /**
     * Handle license tier changes to preserve addon selections when possible
     * 
     * @param string $old_tier The previous tier
     * @param string $new_tier The new tier
     */
    private function handle_tier_change($old_tier, $new_tier) {
        // Get current addon selections
        $current_addons = get_option($this->included_addons_option, array());
        
        // Always save current selections for possible future transitions
        update_option($this->previous_addons_option, $current_addons);
        
        // Specific transition handling
        switch (true) {
            // Upgrading to Ultra — all addons included automatically
            case ($new_tier === 'ultra'):
                update_option($this->included_addons_option, array_keys($this->addons));
                delete_option('explorexr_trial_started_at');
                delete_option('explorexr_trial_key_used');
                break;

            // Downgrading from Ultra to Plus or Trial (4 addons)
            case ($old_tier === 'ultra' && in_array($new_tier, array('plus', 'trial'), true)):
                $previous_addons = get_option($this->previous_addons_option, array());
                $valid_addons    = array_slice($previous_addons, 0, 4);
                update_option($this->included_addons_option, $valid_addons);
                break;

            // Downgrading from Ultra to Pro (2 addons)
            case ($old_tier === 'ultra' && $new_tier === 'pro'):
                $previous_addons = get_option($this->previous_addons_option, array());
                if (!empty($previous_addons)) {
                    $valid_addons = array_slice($previous_addons, 0, 2);
                } else {
                    $addon_keys   = array_keys($this->addons);
                    $valid_addons = array_slice($addon_keys, 0, 2);
                }
                update_option($this->included_addons_option, $valid_addons);
                break;

            // Upgrading from Pro to Plus or Trial (same 4-slot limit as plus)
            case ($old_tier === 'pro' && in_array($new_tier, array('plus', 'trial'), true)):
                // Current Pro addons remain; user may pick additional slots
                break;

            // Downgrading from Plus or Trial to Pro (keep first 2)
            case (in_array($old_tier, array('plus', 'trial'), true) && $new_tier === 'pro'):
                if (!empty($current_addons)) {
                    $valid_addons = array_slice($current_addons, 0, 2);
                    update_option($this->included_addons_option, $valid_addons);
                }
                if ($old_tier === 'trial') {
                    delete_option('explorexr_trial_started_at');
                    delete_option('explorexr_trial_key_used');
                }
                break;

            // Lateral: Trial ↔ Plus (both allow 4 addons — preserve selections)
            case ($old_tier === 'trial' && $new_tier === 'plus'):
            case ($old_tier === 'plus' && $new_tier === 'trial'):
                // No addon changes needed; count is the same
                if ($old_tier === 'trial') {
                    delete_option('explorexr_trial_started_at');
                    delete_option('explorexr_trial_key_used');
                }
                break;

            // Downgrading to Free — clear all selections
            case ($new_tier === 'free'):
                update_option($this->included_addons_option, array());
                if ($old_tier === 'trial') {
                    delete_option('explorexr_trial_started_at');
                    delete_option('explorexr_trial_key_used');
                }
                break;

            // Any other transition — enforce new tier's addon limit
            default:
                if (isset($this->license_tiers[$new_tier])) {
                    $max_addons = $this->license_tiers[$new_tier]['max_addons'];
                    if ($max_addons >= 0 && count($current_addons) > $max_addons) {
                        $valid_addons = array_slice($current_addons, 0, $max_addons);
                        update_option($this->included_addons_option, $valid_addons);
                    }
                }
                break;
        }
        
        // Log the tier change for debugging purposes
        $this->log_tier_change($old_tier, $new_tier);
    }
    
    /**
     * Log tier change information for debugging
     * 
     * @param string $old_tier The previous tier
     * @param string $new_tier The new tier 
     */
    private function log_tier_change($old_tier, $new_tier) {
        // This could be expanded to log to a file or database for better tracking
        $transition_log = get_option('explorexr_license_transitions', array());
        $transition_log[] = array(
            'timestamp' => current_time('mysql'),
            'from_tier' => $old_tier,
            'to_tier' => $new_tier,
            'previous_addons' => get_option($this->previous_addons_option, array()),
            'current_addons' => get_option($this->included_addons_option, array())
        );
        // Keep only the last 10 transitions
        if (count($transition_log) > 10) {
            $transition_log = array_slice($transition_log, -10);
        }
        update_option('explorexr_license_transitions', $transition_log);
    }    /**
     * Compute a stable, one-way hash of the current site's hostname.
     * Uses only the hostname (not the full URL) so the hash is consistent
     * across HTTP/HTTPS and with or without trailing slashes.
     *
     * @return string Hex SHA-256 of the lowercased hostname.
     */
    private function compute_domain_hash() {
        $host = wp_parse_url( home_url(), PHP_URL_HOST );
        return hash( 'sha256', strtolower( (string) $host ) );
    }

    /**
     * Check whether a trial license has already been used on this domain.
     *
     * @return bool True if the domain is locked (trial already consumed).
     */
    private function is_trial_locked_on_domain() {
        return (bool) get_option( 'explorexr_trial_domain_locked', false );
    }

    /**
     * Attempt to register the trial domain with the expoxr.com server.
     *
     * This is a non-blocking, best-effort call. If the server is unreachable
     * or returns an unexpected response, the method returns null and the
     * caller falls back to the local domain-lock check.
     *
     * @param string $license_key The EXTR- trial key being activated.
     * @return bool|null true = server allowed, false = server denied, null = server unreachable.
     */
    private function maybe_register_trial_with_server( $license_key ) {
        $domain_hash = $this->compute_domain_hash();
        $key_hash    = hash( 'sha256', $license_key );

        $response = wp_remote_post(
            'https://expoxr.com/wp-json/explorexr/v1/trial/register',
            array(
                'timeout'    => 5,
                'body'       => array(
                    'domain_hash' => $domain_hash,
                    'key_hash'    => $key_hash,
                ),
                'user-agent' => 'ExploreXR-Plugin/' . EXPLOREXR_PREMIUM_VERSION,
            )
        );

        if ( is_wp_error( $response ) ) {
            $this->debug_log( 'ExploreXR Trial Registration: server unreachable — ' . $response->get_error_message() );
            return null; // Cannot reach server; fall back to local check.
        }

        $code = wp_remote_retrieve_response_code( $response );
        $body = json_decode( wp_remote_retrieve_body( $response ), true );

        if ( $code !== 200 || ! is_array( $body ) ) {
            $this->debug_log( 'ExploreXR Trial Registration: unexpected response (HTTP ' . $code . ')' );
            return null;
        }

        $allowed = isset( $body['allowed'] ) ? (bool) $body['allowed'] : null;
        if ( $allowed === false ) {
            $reason = isset( $body['reason'] ) ? $body['reason'] : 'domain_already_used';
            $this->debug_log( 'ExploreXR Trial Registration: server denied — ' . $reason );
        }

        return $allowed;
    }

    /**
     * Activate a trial license locally (no remote API required for validation).
     * Trial keys start with EXTR- and grant 14 days / 4 add-ons.
     *
     * Domain registration uses a two-layer check:
     *   Layer 1 – permanent client-side domain lock (explorexr_trial_domain_locked).
     *             Exception: the same key can be re-activated while still within
     *             the valid 14-day window (handles data-loss / re-activation edge case).
     *   Layer 2 – non-blocking POST to expoxr.com to register domain + key hash;
     *             if unreachable, Layer 1 alone applies (graceful degradation).
     *
     * @param string $license_key The EXTR- trial key.
     * @return array {valid: bool, tier: string, status: string, expires: string} or {valid: false, error: string}
     */
    public function activate_trial_license( $license_key ) {
        $pattern = '/^EXTR-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-EXXR$/';
        if ( ! preg_match( $pattern, $license_key ) ) {
            return array( 'valid' => false, 'error' => 'Invalid trial license key format. Trial keys begin with EXTR-.' );
        }

        $existing_key     = get_option( 'explorexr_trial_key_used', '' );
        $trial_started_at = (int) get_option( 'explorexr_trial_started_at', 0 );
        $trial_duration   = 14 * DAY_IN_SECONDS;
        $is_same_key      = ( $existing_key && $existing_key === $license_key );
        $is_within_window = ( $trial_started_at && time() <= $trial_started_at + $trial_duration );

        // --- Layer 1: permanent client-side domain lock ---
        // Exception: allow re-activation of the SAME key within the valid 14-day window.
        // This handles data-loss or plugin re-installation during an active trial.
        if ( $this->is_trial_locked_on_domain() ) {
            if ( $is_same_key && $is_within_window ) {
                // Legitimate re-activation of the same key while still valid — allow through.
            } else {
                return array(
                    'valid' => false,
                    'error' => 'A trial license has already been used on this domain. Please purchase a full license to continue.',
                );
            }
        }

        // Prevent a second distinct trial key from being activated on this install.
        if ( $existing_key && ! $is_same_key ) {
            update_option( 'explorexr_trial_domain_locked', '1' );
            return array(
                'valid' => false,
                'error' => 'A different trial license has already been used on this site. Please purchase a full license to continue.',
            );
        }

        // Prevent re-activation of an expired trial key.
        if ( $trial_started_at && ! $is_within_window ) {
            update_option( 'explorexr_trial_domain_locked', '1' );
            return array(
                'valid' => false,
                'error' => 'This 14-day trial has expired. Please purchase a full license to continue.',
            );
        }

        // --- Layer 2: server-side domain registration (non-blocking) ---
        // Skip server call for re-activations of the same key (already registered).
        if ( ! $is_same_key ) {
            $server_result = $this->maybe_register_trial_with_server( $license_key );
            if ( $server_result === false ) {
                // Server explicitly denied — domain already registered with a different key.
                update_option( 'explorexr_trial_domain_locked', '1' );
                return array(
                    'valid' => false,
                    'error' => 'This domain has already used a trial license. Please purchase a full license to continue.',
                );
            }
            // $server_result === null means server unreachable; Layer 1 already passed, so allow.
        }

        // --- All checks passed: activate trial ---
        $started      = $trial_started_at ?: time();
        $trial_expiry = gmdate( 'Y-m-d', $started + $trial_duration );

        update_option( 'explorexr_trial_started_at', $started );
        update_option( 'explorexr_trial_key_used', $license_key );

        // Store the domain hash permanently (survives deactivation / reinstall).
        // The domain lock is intentionally NOT set here — it is set by deactivate_license()
        // and enforce_license_expiry_policy() so that the trial remains usable until
        // one of those terminal events occurs.
        if ( ! get_option( 'explorexr_trial_domain_hash' ) ) {
            update_option( 'explorexr_trial_domain_hash', $this->compute_domain_hash() );
        }

        return array(
            'valid'                 => true,
            'tier'                  => 'trial',
            'status'                => 'active',
            'expires'               => $trial_expiry,
            'remaining_activations' => 1,
            'max_activations'       => 1,
        );
    }

    /**
     * Save license key and validate it
     */
    public function activate_license($license_key, $selected_addons = array()) {
        if (empty($license_key)) {
            return array(
                'success' => false,
                'message' => 'Please enter a license key.',
            );
        }

        // --- Trial license (EXTR- prefix): local activation, no remote API call ---
        if ( strpos( $license_key, 'EXTR-' ) === 0 ) {
            $validation_result = $this->activate_trial_license( $license_key );
            if ( ! $validation_result['valid'] ) {
                return array(
                    'success' => false,
                    'message' => $validation_result['error'],
                );
            }
        }
        // CRITICAL: Use the proper activation method, not just validation
        elseif ($this->license_integration) {
            // First try to activate the license (this includes validation + activation)
            $activation_result = $this->license_integration->activate_license_remote($license_key);
            
            // Log the activation attempt for debugging
            $this->debug_log('=== ExploreXR LICENSE HANDLER ACTIVATION ===');
            $this->debug_log('License Key: ' . $this->redact_license_key($license_key));
            $this->debug_log('Activation Result: ' . json_encode($activation_result, JSON_PRETTY_PRINT));
            $this->debug_log('=== END HANDLER ACTIVATION ===');
            
            if (!$activation_result['valid']) {
                $this->debug_log('ExploreXR License Handler: License activation FAILED - ' . $activation_result['error']);
                return array(
                    'success' => false,
                    'message' => $activation_result['error'],
                );
            }
            
            $validation_result = $activation_result; // Use activation result
        } else {
            // Fallback to basic validation if WooCommerce integration is not available
            $this->debug_log('ExploreXR License Handler: WooCommerce integration not available, using basic validation');
            $validation_result = $this->validate_license_basic($license_key);
            
            if (!$validation_result['valid']) {
                return array(
                    'success' => false,
                    'message' => $validation_result['error'],
                );
            }
        }
        
        $tier = strtolower($validation_result['tier']);
        $tier_lower = $tier; // For backward compatibility
        
        // Debug logging
        $this->debug_log('ExploreXR License Handler: Setting up license with tier: ' . $tier);
        $this->debug_log('ExploreXR License Handler: Validation result status: ' . (isset($validation_result['status']) ? $validation_result['status'] : 'not set'));
        
        // Get current license data for checking tier changes
        $current_license_data = $this->get_license_data();
        $current_tier = isset($current_license_data['tier']) ? $current_license_data['tier'] : 'free';
        $current_status = isset($current_license_data['status']) ? $current_license_data['status'] : 'inactive';
        
        // Save the license key
        update_option($this->license_key_option, $license_key);
        
        // Handle tier change logic
        if ($current_status === 'active' && $current_tier !== $tier) {
            if (!empty($selected_addons)) {
                // If addons were pre-selected (e.g. from tier transition modal), use those
                update_option($this->included_addons_option, $selected_addons);
                // Still save current selections as previous for future reference
                $current_addons = get_option($this->included_addons_option, array());
                update_option($this->previous_addons_option, $current_addons);
            } else {
                // Use standard tier change logic
                $this->handle_tier_change($current_tier, $tier);
            }
        } else if ($current_status !== 'active') {
            // For new activations, preserve current addon selections
            $current_addons = get_option($this->included_addons_option, array());
            update_option($this->previous_addons_option, $current_addons);
        }
        
        // Set default included addons based on tier from WooCommerce integration
        if ($this->license_integration) {
            $wc_tiers = $this->license_integration->get_license_tiers();
            if (isset($wc_tiers[$tier]['included_addons'])) {
                if (empty($selected_addons)) {
                    $selected_addons = $wc_tiers[$tier]['included_addons'];
                }
            }
        }
        
        // Fallback for backward compatibility and user choice handling
        if (empty($selected_addons)) {
            if ($tier === 'ultra') {
                // ULTRA gets all addons automatically
                $selected_addons = array_keys($this->addons);
            } else {
                // PRO and PLUS start with no addons - user must choose
                $selected_addons = array();
            }
        }
        
        // Update included add-ons
        update_option($this->included_addons_option, $selected_addons);
        
        // Update license tier
        update_option($this->license_tier_option, $tier_lower);
        
        // Update license data with real validation results
        // Ensure status is 'active' if validation passed and we got a valid tier
        $license_status = 'active'; // Force active status for valid licenses
        if (isset($validation_result['status']) && $validation_result['status'] === 'inactive') {
            $license_status = 'inactive';
        }
        
        $expires = isset($validation_result['expires']) && !empty($validation_result['expires'])
            ? $validation_result['expires']
            : null;

        if (is_null($expires)) {
            $this->debug_log('ExploreXR License: Server returned no expiry date for key ' . $this->redact_license_key($license_key));
        }

        update_option($this->option_key, array(
            'key' => $license_key,
            'status' => $license_status,
            'tier' => $tier_lower,
            'expires' => $expires,
            'error' => '',
            'last_check' => time(),
            'activations_left' => isset($validation_result['remaining_activations']) ? $validation_result['remaining_activations'] : 0,
            'max_activations' => isset($validation_result['max_activations']) ? $validation_result['max_activations'] : 1,
            'transition_history' => isset($current_license_data['transition_history']) ? $current_license_data['transition_history'] : array(),
        ));
        
        // Clear any potential caches to ensure fresh data
        wp_cache_delete($this->option_key, 'options');
        wp_cache_delete($this->license_tier_option, 'options');
        wp_cache_delete($this->included_addons_option, 'options');
        
        // Force refresh of cached license data
        delete_transient('explorexr_license_cache');
        
        // Record the tier change in history
        if ($current_tier !== $tier_lower) {
            $this->record_tier_transition($current_tier, $tier_lower);
        }
        
        // Reset the refresh attempt timer since we just successfully activated
        delete_option('explorexr_last_license_refresh_attempt');

        // Notify subscribers (e.g. ExploreXR_License_Expiration_Handler::clear_expiration_warnings)
        // so that stale warning flags from a previously expired license are cleared immediately.
        do_action('explorexr_license_activated');

        // Generate success message with expiration date
        $success_message = 'License activated successfully!';
        if (isset($validation_result['expires']) && !empty($validation_result['expires'])) {
            $expires_date = gmdate('F j, Y', strtotime($validation_result['expires']));
            $success_message = "License activated. License valid until {$expires_date}";
        }
        
        return array(
            'success' => true,
            'message' => $success_message,
        );
    }
    
    /**
     * Records a license tier transition in the history
     */
    private function record_tier_transition($from_tier, $to_tier) {
        $license_data = $this->get_license_data();
        $transition_history = isset($license_data['transition_history']) ? $license_data['transition_history'] : array();
        
        // Add new transition
        $transition_history[] = array(
            'date' => current_time('mysql'),
            'from' => $from_tier,
            'to' => $to_tier,
        );
        
        // Keep only the last 10 transitions
        if (count($transition_history) > 10) {
            $transition_history = array_slice($transition_history, -10);
        }
        
        // Update the license data
        $license_data['transition_history'] = $transition_history;
        update_option($this->option_key, $license_data);
    }
    
    /**
     * Check if a tier transition is allowed between tiers
     * Returns information about what will happen during the transition
     * 
     * @param string $from_tier The current tier
     * @param string $to_tier The target tier
     * @return array Transition info including allowed status and effects
     */
    public function check_tier_transition($from_tier, $to_tier) {
        // If either tier doesn't exist, transition is not allowed
        if (!isset($this->license_tiers[$from_tier]) || !isset($this->license_tiers[$to_tier])) {
            return array(
                'allowed' => false,
                'message' => 'Invalid tier specified',
                'addon_impact' => 'unknown',
            );
        }
        
        // Same tier, no transition needed
        if ($from_tier === $to_tier) {
            return array(
                'allowed' => true,
                'message' => 'No change needed, already on ' . $this->license_tiers[$to_tier]['name'] . ' tier',
                'addon_impact' => 'none',
            );
        }
        
        // Get max add-ons for each tier
        $from_max_addons = $this->license_tiers[$from_tier]['max_addons'];
        $to_max_addons = $this->license_tiers[$to_tier]['max_addons'];
        
        // Get current add-on selections
        $current_addons = $this->get_included_addons();
        $current_addon_count = count($current_addons);
        
        // Determine transition type (upgrade, downgrade, lateral)
        // trial sits between free and pro in value — same slot count as plus but temporary
        $tier_values = ['free' => 0, 'trial' => 1, 'pro' => 2, 'plus' => 3, 'ultra' => 4];
        $from_value   = isset($tier_values[$from_tier]) ? $tier_values[$from_tier] : 0;
        $to_value     = isset($tier_values[$to_tier])   ? $tier_values[$to_tier]   : 0;
        $is_upgrade   = $to_value > $from_value;
        $is_downgrade = $to_value < $from_value;
        
        $result = array(
            'allowed' => true,
            'is_upgrade' => $is_upgrade,
            'is_downgrade' => $is_downgrade,
            'from_tier' => $from_tier,
            'to_tier' => $to_tier,
            'from_max_addons' => $from_max_addons,
            'to_max_addons' => $to_max_addons,
            'current_addons' => $current_addons,
            'current_addon_count' => $current_addon_count,
        );
        
        // Handle specific transitions
        if ($to_tier === 'ultra') {
            // Upgrading to Ultra always works
            $result['message']      = 'Upgrading to Ultra will give you access to all add-ons';
            $result['addon_impact'] = 'gain_all';
            $result['addons_gained'] = array_diff(array_keys($this->addons), $current_addons);
            $result['addons_lost']  = array();
        } elseif ($from_tier === 'ultra') {
            // Downgrading from Ultra requires add-on selection
            $result['message']      = 'Downgrading from Ultra will require selecting specific add-ons';
            $result['addon_impact'] = 'select_specific';
            $result['addons_kept']  = array_slice($current_addons, 0, $to_max_addons);
            $result['addons_lost']  = $to_max_addons >= 0 ? array_slice($current_addons, $to_max_addons) : array();
            $result['requires_selection'] = true;
        } elseif (in_array($from_tier, array('plus', 'trial'), true) && $to_tier === 'pro') {
            // Downgrading from Plus/Trial to Pro
            $result['message']      = 'Switching from ' . $this->license_tiers[$from_tier]['name'] . ' to Pro will limit you to 2 add-ons';
            $result['addon_impact'] = 'lose_some';
            $result['addons_kept']  = array_slice($current_addons, 0, 2);
            $result['addons_lost']  = array_slice($current_addons, 2);
            $result['requires_selection'] = $current_addon_count > 2;
        } elseif ($from_tier === 'pro' && in_array($to_tier, array('plus', 'trial'), true)) {
            // Upgrading from Pro to Plus/Trial (same 4-slot level)
            $result['message']      = 'Upgrading from Pro to ' . $this->license_tiers[$to_tier]['name'] . ' allows 2 additional add-on slots';
            $result['addon_impact'] = 'gain_slots';
            $result['additional_slots'] = $to_max_addons - $from_max_addons;
        } elseif ($from_tier === 'trial' && $to_tier === 'plus') {
            // Trial → Plus: same slot count, seamless
            $result['message']      = 'Upgrading from Trial to Plus — your current add-ons are preserved';
            $result['addon_impact'] = 'none';
        } elseif ($from_tier === 'plus' && $to_tier === 'trial') {
            // Plus → Trial: same slot count
            $result['message']      = 'Switching to Trial — your current add-ons are preserved for the 14-day period';
            $result['addon_impact'] = 'none';
        } else {
            // Generic upgrade / downgrade
            if ($is_upgrade) {
                $result['message']      = 'Upgrading to ' . $this->license_tiers[$to_tier]['name'] . ' tier';
                $result['addon_impact'] = $to_max_addons > $from_max_addons ? 'gain_slots' : 'none';
                $result['additional_slots'] = max(0, $to_max_addons - $from_max_addons);
            } else {
                $result['message']      = 'Downgrading to ' . $this->license_tiers[$to_tier]['name'] . ' tier';
                $result['addon_impact'] = ($to_max_addons >= 0 && $to_max_addons < $from_max_addons) ? 'lose_slots' : 'none';
                $result['slots_lost']   = max(0, $from_max_addons - ($to_max_addons >= 0 ? $to_max_addons : $from_max_addons));
            }
        }
        
        return $result;
    }

    /**
     * Deactivate license
     */
    public function deactivate_license() {
        $current_license_data = $this->get_license_data();
        $license_key = isset($current_license_data['key']) ? $current_license_data['key'] : '';
        $deactivated_tier = isset($current_license_data['tier']) ? $current_license_data['tier'] : 'free';

        // Use WooCommerce integration for remote deactivation (not for trial keys)
        if (!empty($license_key) && $this->license_integration && strpos($license_key, 'EXTR-') !== 0) {
            $deactivation_result = $this->license_integration->deactivate_license_remote($license_key);

            if (!$deactivation_result['success']) {
                $this->debug_log('ExploreXR: Remote license deactivation failed: ' . $deactivation_result['error']);
                // Continue with local deactivation even if remote fails
            }
        }

        // If deactivating a trial, permanently lock the domain so no further trial can run.
        if ($deactivated_tier === 'trial') {
            update_option('explorexr_trial_domain_locked', '1');
        }

        // Get current addon selections and save them for future reference
        $current_addons = get_option($this->included_addons_option, array());
        update_option($this->previous_addons_option, $current_addons);

        // Clear license key
        update_option($this->license_key_option, '');

        // Update license data
        update_option($this->option_key, array(
            'key' => '',
            'status' => 'inactive',
            'tier' => 'free',
            'expires' => '',
            'error' => '',
            'last_check' => time(),
        ));

        // Reset included add-ons
        update_option($this->included_addons_option, array());

        // Clear grace-enforcement flag so future activations can expire cleanly.
        delete_option('explorexr_license_grace_enforced_at');

        return array(
            'success' => true,
            'message' => 'License deactivated successfully.',
        );
    }

    /**
     * Select which add-ons to enable with Pro or Plus license
     * 
     * @param array $addon_ids Array of addon slugs to select
     * @return array Result with success status and message
     */
    public function select_addons($addon_ids) {
        $license_data = $this->get_license_data();
        $tier = $license_data['tier'];
        
        // Ultra already includes all add-ons - auto-populate
        if ($tier === 'ultra') {
            update_option($this->included_addons_option, array_keys($this->addons));
            update_option($this->previous_addons_option, array_keys($this->addons));
            
            return array(
                'success' => true,
                'message' => 'All add-ons are included with your Ultra license.',
            );
        }
        
        // Free tier allows no addons
        if ($tier === 'free') {
            update_option($this->included_addons_option, array());
            
            return array(
                'success' => false,
                'message' => 'A valid license is required to use add-ons.',
            );
        }
        
        // Check if license is active for other tiers
        if ($license_data['status'] !== 'active') {
            return array(
                'success' => false,
                'message' => 'Please activate your license first.',
            );
        }
        
        // Get max add-ons limit for this tier
        $max_addons = $this->license_tiers[$tier]['max_addons'];
        
        // Validate addon count
        if (count($addon_ids) > $max_addons) {
            return array(
                'success' => false,
                'message' => sprintf(
                    'Your %s license allows a maximum of %d add-on(s). You selected %d.',
                    $this->license_tiers[$tier]['name'],
                    $max_addons,
                    count($addon_ids)
                ),
            );
        }
        
        // Validate addon IDs
        $valid_addon_ids = array();
        foreach ($addon_ids as $addon_id) {
            if (isset($this->addons[$addon_id])) {
                $valid_addon_ids[] = $addon_id;
            }
        }
        
        // Save the selected add-ons
        update_option($this->included_addons_option, $valid_addon_ids);
        update_option($this->previous_addons_option, $valid_addon_ids);
        
        return array(
            'success' => true,
            'message' => 'Add-on selection saved successfully.',
            'selected_addons' => $valid_addon_ids,
        );
    }

    /**
     * Maybe check license status periodically
     * AUTO-FIX: Automatically corrects stuck inactive licenses
     */
    public function maybe_check_license() {
        $license_data = $this->get_license_data();
        $this->enforce_license_expiry_policy($license_data);
        // Refresh in case enforcement updated the stored data
        $license_data = $this->get_license_data();
        
        // AUTO-FIX: Check if we have a license key but status is inactive - this might be a stuck license
        if (!empty($license_data['key']) && $license_data['status'] === 'inactive' && 
            $license_data['tier'] !== 'free') {
            
            // Try to auto-refresh once per day (previously was hourly, now daily to reduce API calls)
            $last_auto_refresh = get_option('explorexr_last_auto_refresh', 0);
            if (time() - $last_auto_refresh > DAY_IN_SECONDS) {
                $this->debug_log('ExploreXR License Handler: AUTO-FIX - Detected stuck inactive license, attempting automatic refresh');
                update_option('explorexr_last_auto_refresh', time());
                
                $refresh_success = $this->force_refresh_license_status();
                
                if ($refresh_success) {
                    // Set transient for success notice
                    set_transient('explorexr_license_auto_fixed', true, HOUR_IN_SECONDS);
                    $this->debug_log('ExploreXR License Handler: AUTO-FIX - License successfully corrected to active');
                }
                return;
            }
        }
        
        // Check once a week for active licenses
        if ($license_data['status'] === 'active' && ($license_data['last_check'] + (7 * DAY_IN_SECONDS) < time())) {
            $this->check_license($license_data['key']);
        }
    }

    /**
     * Periodically verify an active license with the remote API.
     *
     * Delegates to force_refresh_license_status() when the integration
     * class is available, or falls back to updating the last_check
     * timestamp when it is not (e.g. API credentials not yet configured).
     */
    private function check_license($license_key) {
        if ($this->license_integration) {
            // Trial keys are validated locally — no remote call needed.
            if (strpos($license_key, 'EXTR-') === 0) {
                $license_data = $this->get_license_data();
                $license_data['last_check'] = time();
                update_option($this->option_key, $license_data);
                return;
            }

            // Attempt remote refresh; logs internally on failure.
            $this->force_refresh_license_status();
        } else {
            // Integration not available — just record the check time.
            $license_data = $this->get_license_data();
            $license_data['last_check'] = time();
            update_option($this->option_key, $license_data);
        }
    }

    /**
     * Force refresh license status - fixes stuck inactive licenses
     * This method should be called when we detect a license might be stuck in inactive state
     */
    public function force_refresh_license_status() {
        $license_data = $this->get_license_data();
        $license_key = $license_data['key'];
        
        if (empty($license_key)) {
            $this->debug_log('ExploreXR License Handler: Cannot refresh license status - no license key found');
            return false;
        }
        
        $this->debug_log('ExploreXR License Handler: Force refreshing license status for key: ' . $this->redact_license_key($license_key));
        
        // Try to validate the license again using the integration
        if ($this->license_integration) {
            $validation_result = $this->license_integration->validate_license_remote($license_key);
            
            $this->debug_log('ExploreXR License Handler: Force refresh validation result: ' . json_encode($validation_result, JSON_PRETTY_PRINT));
            
            if ($validation_result['valid']) {
                $tier = strtolower($validation_result['tier']);

                $refresh_expires = isset($validation_result['expires']) && !empty($validation_result['expires'])
                    ? $validation_result['expires']
                    : null;

                if (is_null($refresh_expires)) {
                    $this->debug_log('ExploreXR License: Server returned no expiry date during refresh for key ' . $this->redact_license_key($license_key));
                }

                // Update license data with corrected status
                $updated_license_data = array(
                    'key' => $license_key,
                    'status' => 'active', // Force active status for valid licenses
                    'tier' => $tier,
                    'expires' => $refresh_expires,
                    'error' => '',
                    'last_check' => time(),
                    'activations_left' => isset($validation_result['remaining_activations']) ? $validation_result['remaining_activations'] : 0,
                    'max_activations' => isset($validation_result['max_activations']) ? $validation_result['max_activations'] : 1,
                    'transition_history' => isset($license_data['transition_history']) ? $license_data['transition_history'] : array(),
                );
                
                update_option($this->option_key, $updated_license_data);
                update_option($this->license_tier_option, $tier);
                
                // Clear caches
                wp_cache_delete($this->option_key, 'options');
                wp_cache_delete($this->license_tier_option, 'options');
                delete_transient('explorexr_license_cache');
                
                $this->debug_log('ExploreXR License Handler: License status successfully refreshed to active');
                return true;
            } else {
                $this->debug_log('ExploreXR License Handler: License validation failed during refresh: ' . $validation_result['error']);
                return false;
            }
        } else {
            $this->debug_log('ExploreXR License Handler: Cannot refresh license status - WooCommerce integration not available');
            return false;
        }
    }

    /**
     * Show admin notice for potentially stuck licenses or auto-fix success
     */
    public function maybe_show_stuck_license_notice() {
        // Only show on ExploreXR admin pages
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Used for conditional display only
        if (!isset($_GET['page']) || strpos(sanitize_text_field(wp_unslash($_GET['page'])), 'explorexr-premium') !== 0) {
            return;
        }
        
        // Check if license was auto-fixed (show success notice)
        if (get_transient('explorexr_license_auto_fixed')) {
            delete_transient('explorexr_license_auto_fixed');
            ?>
            <div class="notice notice-success is-dismissible">
                <p>
                    <strong>✓ ExploreXR License Verified:</strong> 
                    Your license status has been automatically refreshed and verified as active.
                </p>
            </div>
            <?php
            return; // Don't show warning if we just fixed it
        }
        
        $license_data = $this->get_license_data();
        
        // Check if we have a license key but status is inactive for non-free, non-trial tier
        // Trial licenses are validated locally and never go through remote revalidation.
        if (!empty($license_data['key']) &&
            $license_data['status'] === 'inactive' &&
            $license_data['tier'] !== 'free' &&
            $license_data['tier'] !== 'trial') {
            
            // Only show notice once per day to avoid annoying users
            $notice_shown = get_option('explorexr_stuck_license_notice_shown', 0);
            if (time() - $notice_shown < DAY_IN_SECONDS) {
                return;
            }
            
            // Check if automatic refresh recently attempted
            $last_auto_refresh = get_option('explorexr_last_auto_refresh', 0);
            $is_recent_refresh = (time() - $last_auto_refresh) < DAY_IN_SECONDS;
            
            if (!$is_recent_refresh) {
                ?>
                <div class="notice notice-warning is-dismissible">
                    <p>
                        <strong>ExploreXR License Issue:</strong> 
                        Your <?php echo esc_html(ucfirst($license_data['tier'])); ?> license appears inactive. This will be automatically checked daily.
                        <a href="<?php echo esc_url(admin_url('admin.php?page=explorexr-license')); ?>" class="button button-secondary">
                            Re-validate Now
                        </a>
                    </p>
                </div>
                <?php
                
                // Mark notice as shown
                update_option('explorexr_stuck_license_notice_shown', time());
            }
        }
    }    /**
     * AJAX handler for license activation
     */
    public function activate_license_ajax() {
        try {
            // Check nonce for security
            // CRITICAL FIX: The nonce ACTION is always 'explorexr_premium_license_nonce' (from wp_nonce_field)
            // The nonce FIELD can be either 'explorexr_license_nonce' or 'explorexr_premium_license_nonce'
            // We must verify against the ACTION name, not the field name
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is verified on the next line
            $nonce_value = isset($_POST['explorexr_license_nonce'])
                ? sanitize_text_field(wp_unslash($_POST['explorexr_license_nonce']))
                : (isset($_POST['explorexr_premium_license_nonce'])
                    ? sanitize_text_field(wp_unslash($_POST['explorexr_premium_license_nonce']))
                    : (isset($_POST['nonce'])
                        ? sanitize_text_field(wp_unslash($_POST['nonce']))
                        : ''));
            
            // The nonce action must match what was used in wp_nonce_field()
            // In license-page.php line 99: wp_nonce_field('explorexr_premium_license_nonce', 'explorexr_premium_license_nonce')
            // First parameter is the ACTION (what we verify against), second is the FIELD name
            if (empty($nonce_value) || !wp_verify_nonce($nonce_value, 'explorexr_premium_license_nonce')) {
                wp_send_json_error(array('message' => 'Security token is missing or invalid. For more help, please contact support.'));
                return;
            }
            
            // Check user permissions
            if (!current_user_can('manage_options')) {
                wp_send_json_error(array('message' => 'Insufficient permissions. For more help, please contact support.'));
                return;
            }

            // Check rate limiting (max 5 activation attempts per minute)
            if (function_exists('ExploreXR_check_rate_limit')) {
                if (!ExploreXR_check_rate_limit('activate_license', 5, 60)) {
                    if (function_exists('explorexr_log_security_event')) {
                        explorexr_log_security_event(
                            'rate_limit_exceeded',
                            'License activation rate limit exceeded',
                            array('action' => 'activate_license', 'limit' => 5)
                        );
                    }
                    wp_send_json_error(array('message' => 'Too many activation requests. Please wait before trying again.'));
                    return;
                }
            }

            $license_key = isset($_POST['license_key']) ? sanitize_text_field(wp_unslash($_POST['license_key'])) : '';

            if (empty($license_key)) {
                wp_send_json_error(array('message' => 'License key is required. For more help, please contact support.'));
                return;
            }

            // Check for pre-selected add-ons from tier transition
            $selected_addons = array();
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified above
            if (isset($_POST['selected_addons']) && is_array($_POST['selected_addons'])) {
                $selected_addons = array_map('sanitize_text_field', wp_unslash((array) $_POST['selected_addons']));
            }
            
            $result = $this->activate_license($license_key, $selected_addons);
            
            if ($result['success']) {
                wp_send_json_success(array('message' => $result['message']));
            } else {
                // Add support contact message to error
                $error_message = $result['message'];
                if (strpos($error_message, 'contact support') === false) {
                    $error_message .= ' For more help, please contact support.';
                }
                wp_send_json_error(array('message' => $error_message));
            }
        } catch (Exception $e) {
            // Catch any unexpected errors and return user-friendly message
            $this->debug_log('ExploreXR License Activation Error: ' . $e->getMessage());
            wp_send_json_error(array(
                'message' => 'An error occurred while activating your license. For more help, please contact support.'
            ));
        } catch (Error $e) {
            // Catch PHP 7+ errors
            $this->debug_log('ExploreXR License Activation Fatal Error: ' . $e->getMessage());
            wp_send_json_error(array(
                'message' => 'A system error occurred. For more help, please contact support.'
            ));
        }
    }    /**
     * AJAX handler for license deactivation
     */
    public function deactivate_license_ajax() {
        try {
            // Check nonce for security
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is verified on the next line
            $nonce_value = isset($_POST['explorexr_license_nonce'])
                ? sanitize_text_field(wp_unslash($_POST['explorexr_license_nonce']))
                : (isset($_POST['explorexr_premium_license_nonce'])
                    ? sanitize_text_field(wp_unslash($_POST['explorexr_premium_license_nonce']))
                    : (isset($_POST['nonce'])
                        ? sanitize_text_field(wp_unslash($_POST['nonce']))
                        : ''));
            
            $nonce_valid = $nonce_value && (wp_verify_nonce($nonce_value, 'explorexr_premium_license_nonce') || wp_verify_nonce($nonce_value, 'explorexr_license_nonce'));
            
            if (!$nonce_valid) {
                $this->debug_log('ExploreXR: License deactivation blocked - invalid nonce');
                wp_send_json_error(array('message' => 'Security token is missing or invalid. For more help, please contact support.'));
                return;
            }
            
            // Check user permissions
            if (!current_user_can('manage_options')) {
                $this->debug_log('ExploreXR: License deactivation blocked - insufficient permissions');
                wp_send_json_error(array('message' => 'Insufficient permissions. For more help, please contact support.'));
                return;
            }
            
            // Check rate limiting if function exists (max 3 deactivations per minute)
            if (function_exists('ExploreXR_check_rate_limit')) {
                if (!ExploreXR_check_rate_limit('deactivate_license', 3, 60)) {
                    if (function_exists('explorexr_log_security_event')) {
                        explorexr_log_security_event(
                            'rate_limit_exceeded',
                            'License deactivation rate limit exceeded',
                            array('action' => 'deactivate_license', 'limit' => 3)
                        );
                    }
                    wp_send_json_error(array('message' => 'Too many deactivation requests. Please wait before trying again.'));
                    return;
                }
            }
            
            $result = $this->deactivate_license();
            
            if ($result['success']) {
                wp_send_json_success(array('message' => $result['message']));
            } else {
                // Add support contact message to error
                $error_message = $result['message'];
                if (strpos($error_message, 'contact support') === false) {
                    $error_message .= ' For more help, please contact support.';
                }
                wp_send_json_error(array('message' => $error_message));
            }
        } catch (Exception $e) {
            $this->debug_log('ExploreXR License Deactivation Error: ' . $e->getMessage());
            wp_send_json_error(array(
                'message' => 'An error occurred while deactivating your license. For more help, please contact support.'
            ));
        } catch (Error $e) {
            $this->debug_log('ExploreXR License Deactivation Fatal Error: ' . $e->getMessage());
            wp_send_json_error(array(
                'message' => 'A system error occurred. For more help, please contact support.'
            ));
        }
    }    /**
     * AJAX handler for selecting add-ons
     */
    public function select_addon_ajax() {
        try {
            // Check nonce for security
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is verified on the next line
            $nonce_value = isset($_POST['explorexr_license_nonce'])
                ? sanitize_text_field(wp_unslash($_POST['explorexr_license_nonce']))
                : (isset($_POST['explorexr_premium_license_nonce'])
                    ? sanitize_text_field(wp_unslash($_POST['explorexr_premium_license_nonce']))
                    : (isset($_POST['nonce'])
                        ? sanitize_text_field(wp_unslash($_POST['nonce']))
                        : ''));
            
            $nonce_valid = $nonce_value && (wp_verify_nonce($nonce_value, 'explorexr_premium_license_nonce') || wp_verify_nonce($nonce_value, 'explorexr_license_nonce'));
            
            if (!$nonce_valid) {
                $this->debug_log('ExploreXR: Addon selection blocked - invalid nonce');
                wp_send_json_error(array('message' => 'Security token is missing or invalid. For more help, please contact support.'));
                return;
            }
            
            // Check user permissions
            if (!current_user_can('manage_options')) {
                $this->debug_log('ExploreXR: Addon selection blocked - insufficient permissions');
                wp_send_json_error(array('message' => 'Insufficient permissions. For more help, please contact support.'));
                return;
            }
            
            // Check rate limiting if function exists (max 5 selections per minute)
            if (function_exists('ExploreXR_check_rate_limit')) {
                if (!ExploreXR_check_rate_limit('select_addon', 5, 60)) {
                    if (function_exists('explorexr_log_security_event')) {
                        explorexr_log_security_event(
                            'rate_limit_exceeded',
                            'Addon selection rate limit exceeded',
                            array('action' => 'select_addon', 'limit' => 5)
                        );
                    }
                    wp_send_json_error(array('message' => 'Too many selection requests. Please wait before trying again.'));
                    return;
                }
            }
            
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified above
            $addon_ids = isset($_POST['addon_ids']) ? array_map('sanitize_text_field', wp_unslash((array) $_POST['addon_ids'])) : array();
            
            $result = $this->select_addons($addon_ids);
            
            if ($result['success']) {
                wp_send_json_success(array('message' => $result['message']));
            } else {
                // Add support contact message to error
                $error_message = $result['message'];
                if (strpos($error_message, 'contact support') === false) {
                    $error_message .= ' For more help, please contact support.';
                }
                wp_send_json_error(array('message' => $error_message));
            }
        } catch (Exception $e) {
            $this->debug_log('ExploreXR Addon Selection Error: ' . $e->getMessage());
            wp_send_json_error(array(
                'message' => 'An error occurred while selecting addons. For more help, please contact support.'
            ));
        } catch (Error $e) {
            $this->debug_log('ExploreXR Addon Selection Fatal Error: ' . $e->getMessage());
            wp_send_json_error(array(
                'message' => 'A system error occurred. For more help, please contact support.'
            ));
        }
    }

    /**
     * Show a global admin notice when the license is expired (or in grace)
     */
    public function maybe_show_license_expiry_notice() {
        if (!current_user_can('manage_options')) {
            return;
        }

        $license_data = $this->get_license_data();

        if (empty($license_data['key']) || empty($license_data['expires'])) {
            return;
        }

        $expiry_timestamp = strtotime($license_data['expires']);
        if (!$expiry_timestamp) {
            return;
        }

        $now = time();
        if ($now <= $expiry_timestamp) {
            return; // Not expired yet.
        }

        $grace_seconds = 90 * DAY_IN_SECONDS;
        $grace_end     = $expiry_timestamp + $grace_seconds;
        $days_into_grace = max(0, ceil(($now - $expiry_timestamp) / DAY_IN_SECONDS));
        $days_left_in_grace = max(0, ceil(($grace_end - $now) / DAY_IN_SECONDS));
        $renew_url = 'https://expoxr.com';

        $is_grace_active = $now <= $grace_end;
        $message = $is_grace_active
            ? sprintf(
                /* translators: 1: days left in grace period */
                'Your ExploreXR license expired and is in a grace period. You have %d day(s) left before premium features are disabled. Please renew to stay active.',
                $days_left_in_grace
            )
            : 'Your ExploreXR license expired more than 90 days ago. Premium features and add-ons have been disabled. Renew to reactivate.';

        ?>
        <div class="notice notice-<?php echo $is_grace_active ? 'warning' : 'error'; ?> is-dismissible">
            <p><strong>ExploreXR License Notice:</strong> <?php echo esc_html($message); ?></p>
            <p>
                <a class="button button-primary" href="<?php echo esc_url($renew_url); ?>" target="_blank" rel="noopener noreferrer">Renew License</a>
                <a class="button" href="<?php echo esc_url(admin_url('admin.php?page=explorexr-license')); ?>">Open License Manager</a>
            </p>
        </div>
        <?php
    }

    /**
     * Enforce expiration grace policy.
     *
     * - Trial licenses: no grace period — expire immediately after 14 days, revert to free.
     * - All other licenses: 90-day grace period, then deactivate.
     *
     * @param array|null $license_data Optional license data to reuse.
     */
    private function enforce_license_expiry_policy($license_data = null) {
        if (is_null($license_data)) {
            $license_data = $this->get_license_data();
        }

        if (empty($license_data['key']) || empty($license_data['expires'])) {
            return;
        }

        $expiry_timestamp = strtotime($license_data['expires']);
        if (!$expiry_timestamp) {
            return;
        }

        $now = time();
        if ($now <= $expiry_timestamp) {
            return; // Not expired yet.
        }

        // --- Trial licenses: zero grace period ---
        if (isset($license_data['tier']) && $license_data['tier'] === 'trial') {
            $enforced_at = (int) get_option('explorexr_license_grace_enforced_at', 0);
            if ($enforced_at) {
                return; // Already enforced.
            }

            $license_data['status'] = 'inactive';
            $license_data['tier']   = 'free';
            $license_data['error']  = 'Your 14-day trial has expired. Please purchase a license to continue using add-ons.';
            update_option($this->option_key, $license_data);
            update_option($this->license_tier_option, 'free');
            update_option('explorexr_license_grace_enforced_at', $now);

            // Permanently lock the domain so no second trial can be started.
            update_option('explorexr_trial_domain_locked', '1');

            $this->deactivate_all_addons_after_expiry();
            return;
        }

        // --- Paid licenses: 90-day grace period ---
        $grace_seconds = 90 * DAY_IN_SECONDS;
        $grace_end     = $expiry_timestamp + $grace_seconds;

        if ($now > $grace_end) {
            $enforced_at = (int) get_option('explorexr_license_grace_enforced_at', 0);
            if ($enforced_at && $enforced_at >= $grace_end) {
                return; // Already enforced after grace.
            }

            $license_data['status'] = 'inactive';
            $license_data['error']  = 'License expired beyond the 3-month grace period. Premium features are disabled until renewal.';
            update_option($this->option_key, $license_data);
            update_option('explorexr_license_grace_enforced_at', $now);

            $this->deactivate_all_addons_after_expiry();
            return;
        }

        // Within grace period: keep working but flag in stored data.
        if (empty($license_data['error'])) {
            $license_data['error'] = 'License expired and is in a grace period.';
            update_option($this->option_key, $license_data);
        }
    }

    /**
     * Deactivate all add-ons when the grace period has ended.
     */
    private function deactivate_all_addons_after_expiry() {
        foreach ($this->addons as $addon) {
            if (is_plugin_active($addon['path'])) {
                deactivate_plugins($addon['path']);
            }
        }

        update_option($this->included_addons_option, array());
    }

    /**
     * Show a daily upgrade hint to admins once the trial reaches day 8.
     * The notice is shown once per calendar day to avoid being intrusive.
     */
    public function maybe_show_trial_upgrade_hint() {
        if (!current_user_can('manage_options')) {
            return;
        }

        $license_data = $this->get_license_data();

        if (empty($license_data['key']) || $license_data['status'] !== 'active' || $license_data['tier'] !== 'trial') {
            return;
        }

        $trial_started_at = (int) get_option('explorexr_trial_started_at', 0);
        if (!$trial_started_at) {
            return;
        }

        $days_elapsed = (int) ceil((time() - $trial_started_at) / DAY_IN_SECONDS);

        // Only show from day 8 onward.
        if ($days_elapsed < 8) {
            return;
        }

        // Show once per calendar day (UTC).
        $last_shown = (int) get_option('explorexr_trial_hint_last_shown', 0);
        if ($last_shown && gmdate('Y-m-d', $last_shown) === gmdate('Y-m-d') ) {
            return;
        }

        $days_remaining = max(0, 14 - $days_elapsed);
        update_option('explorexr_trial_hint_last_shown', time());

        ?>
        <div class="notice notice-info is-dismissible">
            <p>
                <strong>⏳ ExploreXR Trial — <?php echo (int) $days_remaining; ?> day<?php echo $days_remaining !== 1 ? 's' : ''; ?> remaining</strong><br>
                Enjoying ExploreXR? Upgrade to a full license before your trial ends to keep your add-ons active and unlock all features.
            </p>
            <p>
                <a href="https://expoxr.com/explorexr/pricing/" target="_blank" rel="noopener noreferrer" class="button button-primary">Get a License</a>
                &nbsp;
                <a href="<?php echo esc_url(admin_url('admin.php?page=explorexr-license')); ?>" class="button button-secondary">View License Plans</a>
            </p>
        </div>
        <?php
    }

    /**
     * Add license menu to admin
     * NOTE: This method is deprecated. License menu is now registered in admin-menu.php
     * Kept for backwards compatibility but no longer hooked.
     */
    public function add_license_menu() {
        // Menu registration moved to admin-menu.php to avoid duplicates
        // This method is kept for backwards compatibility only
        /*
        add_submenu_page(
            'explorexr-premium',
            'License',
            'License',
            'manage_options',
            'explorexr-license',
            'explorexr_license_page'
        );
        */
    }

    /**
     * Check if add-ons should be active based on license
     * Enforces addon limits and automatically updates selected addons list
     */
    public function check_addon_activation() {
        if (!is_admin()) {
            return;
        }

        // Get maximum addons allowed by current license tier
        $max_addons = $this->get_max_addons();
        $license_tier = $this->get_license_tier();
        $tier_name = isset($this->license_tiers[$license_tier]) ?
                    $this->license_tiers[$license_tier]['name'] :
                    ucfirst($license_tier);

        // Get currently active addons — exclude debug, which is license-free.
        $active_addons = array();
        foreach ($this->addons as $addon_id => $addon) {
            if ($addon_id === 'debug') {
                continue; // Debug add-on never requires a license; skip enforcement.
            }
            if (is_plugin_active($addon['path'])) {
                $active_addons[] = $addon_id;
            }
        }
        
        // If license is Ultra, allow unlimited addons and sync selected list
        if ($license_tier === 'ultra') {
            // Update selected addons to match active addons (or all available)
            update_option($this->included_addons_option, array_keys($this->addons));
            return;
        }
        
        $addon_count = count($active_addons);
        
        // If addon count is within limit, save them as selected
        if ($addon_count <= $max_addons) {
            // Update the selected addons list to match currently active addons
            update_option($this->included_addons_option, $active_addons);
            return;
        }
        
        // If active addon count exceeds maximum, deactivate the most recently activated ones
        $excess_count = $addon_count - $max_addons;
        
        // Get addon activation order from WordPress (most recent first)
        $all_plugins = get_option('active_plugins', array());
        $addon_paths = array();
        foreach ($this->addons as $addon_id => $addon) {
            $addon_paths[$addon['path']] = $addon_id;
        }
        
        $recently_activated_addons = array();
        foreach (array_reverse($all_plugins) as $plugin_path) {
            if (isset($addon_paths[$plugin_path]) && in_array($addon_paths[$plugin_path], $active_addons)) {
                $recently_activated_addons[] = array(
                    'id' => $addon_paths[$plugin_path],
                    'path' => $plugin_path,
                    'name' => $this->addons[$addon_paths[$plugin_path]]['name']
                );
            }
        }
        
        // Deactivate the most recently activated addons that exceed the limit
        $deactivated_count = 0;
        $deactivated_ids = array();
        foreach ($recently_activated_addons as $addon) {
            if ($deactivated_count >= $excess_count) {
                break;
            }

            deactivate_plugins($addon['path']);
            $deactivated_ids[] = $addon['id'];

            // Add admin notice
            add_action('admin_notices', function() use ($addon, $tier_name, $max_addons) {
                ?>
                <div class="notice notice-warning is-dismissible">
                    <p>
                        <?php echo esc_html($addon['name']); ?> has been deactivated because your
                        <?php echo esc_html($tier_name); ?> license allows a maximum of <?php echo (int)$max_addons; ?> addon(s).
                        <a href="<?php echo esc_url(admin_url('admin.php?page=explorexr-license')); ?>">Upgrade your license</a> to activate more addons.
                    </p>
                </div>
                <?php
            });

            $deactivated_count++;
        }

        // Update selected addons list to match remaining active addons
        $remaining_active = array_diff($active_addons, $deactivated_ids);
        update_option($this->included_addons_option, array_values($remaining_active));

        // BUG-04 fix: publish the slugs this method deactivated so the secondary enforcement
        // layer (ExploreXR_Addon_License_Validator::validate_addon_licenses, priority 20) can
        // skip them and avoid firing a duplicate admin notice for the same addon.
        if (!empty($deactivated_ids)) {
            set_transient('explorexr_handler_deactivated_slugs', $deactivated_ids, 30);
        }
    }
      /**
     * AJAX handler for checking tier transition compatibility
     */
    public function check_tier_transition_ajax() {
        try {
            // Check nonce for security
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is verified on the next line
            $nonce_value = isset($_POST['explorexr_license_nonce'])
                ? sanitize_text_field(wp_unslash($_POST['explorexr_license_nonce']))
                : (isset($_POST['explorexr_premium_license_nonce'])
                    ? sanitize_text_field(wp_unslash($_POST['explorexr_premium_license_nonce']))
                    : (isset($_POST['nonce'])
                        ? sanitize_text_field(wp_unslash($_POST['nonce']))
                        : ''));
            
            $nonce_valid = $nonce_value && (wp_verify_nonce($nonce_value, 'explorexr_premium_license_nonce') || wp_verify_nonce($nonce_value, 'explorexr_license_nonce'));
            
            if (!$nonce_valid) {
                $this->debug_log('ExploreXR: Tier transition check blocked - invalid nonce');
                wp_send_json_error(array('message' => 'Security token is missing or invalid. For more help, please contact support.'));
                return;
            }
            
            // Check user permissions
            if (!current_user_can('manage_options')) {
                $this->debug_log('ExploreXR: Tier transition check blocked - insufficient permissions');
                wp_send_json_error(array('message' => 'Insufficient permissions. For more help, please contact support.'));
                return;
            }
            
            // Check rate limiting if function exists (max 10 checks per minute)
            if (function_exists('ExploreXR_check_rate_limit')) {
                if (!ExploreXR_check_rate_limit('check_tier_transition', 10, 60)) {
                    if (function_exists('explorexr_log_security_event')) {
                        explorexr_log_security_event(
                            'rate_limit_exceeded',
                            'Tier transition check rate limit exceeded',
                            array('action' => 'check_tier_transition', 'limit' => 10)
                        );
                    }
                    wp_send_json_error(array('message' => 'Too many transition checks. Please wait before trying again.'));
                    return;
                }
            }
            
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified above
            $from_tier = isset($_POST['from_tier']) ? sanitize_text_field(wp_unslash($_POST['from_tier'])) : '';
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified above
            $to_tier = isset($_POST['to_tier']) ? sanitize_text_field(wp_unslash($_POST['to_tier'])) : '';
            
            if (empty($from_tier) || empty($to_tier)) {
                wp_send_json_error(array('message' => 'Missing required parameters. For more help, please contact support.'));
                return;
            }
            
            $result = $this->check_tier_transition($from_tier, $to_tier);
            
            if ($result['allowed']) {
                wp_send_json_success($result);
            } else {
                wp_send_json_error($result);
            }
        } catch (Exception $e) {
            $this->debug_log('ExploreXR Tier Transition Check Error: ' . $e->getMessage());
            wp_send_json_error(array(
                'message' => 'An error occurred while checking tier transition. For more help, please contact support.'
            ));
        } catch (Error $e) {
            $this->debug_log('ExploreXR Tier Transition Check Fatal Error: ' . $e->getMessage());
            wp_send_json_error(array(
                'message' => 'A system error occurred. For more help, please contact support.'
            ));
        }
    }
    
    /**
     * AJAX handler for real-time license validation
     * Delegates to WooCommerce integration for secure validation
     */
    public function validate_license_ajax() {
        try {
            // If WooCommerce integration is available, delegate to it
            if ($this->license_integration && method_exists($this->license_integration, 'ajax_validate_license')) {
                return $this->license_integration->ajax_validate_license();
            }
            
            // Fallback validation if integration is not available
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is verified on this line
            $nonce_value = isset($_POST['nonce'])
                ? sanitize_text_field(wp_unslash($_POST['nonce']))
                : (isset($_POST['explorexr_license_nonce'])
                    ? sanitize_text_field(wp_unslash($_POST['explorexr_license_nonce']))
                    : (isset($_POST['explorexr_premium_license_nonce'])
                        ? sanitize_text_field(wp_unslash($_POST['explorexr_premium_license_nonce']))
                        : ''));

            $nonce_valid = $nonce_value && (wp_verify_nonce($nonce_value, 'explorexr_premium_license_nonce') || wp_verify_nonce($nonce_value, 'explorexr_license_nonce'));

            if (!$nonce_valid) {
                wp_send_json_error(array('message' => 'Security check failed. For more help, please contact support.'));
                return;
            }
            
            if (!current_user_can('manage_options')) {
                wp_send_json_error(array('message' => 'Insufficient permissions. For more help, please contact support.'));
                return;
            }
            
            $license_key = isset($_POST['license_key']) ? sanitize_text_field(wp_unslash($_POST['license_key'])) : '';
            
            // If no license key provided, try to use the current license key for re-validation
            if (empty($license_key)) {
                $current_license_data = $this->get_license_data();
                $license_key = isset($current_license_data['key']) ? $current_license_data['key'] : '';
            }
            
            if (empty($license_key)) {
                wp_send_json_error(array('message' => 'No license key found to validate. For more help, please contact support.'));
                return;
            }
            
            // First try force refresh to fix any stuck inactive licenses
            $this->debug_log('ExploreXR License Handler: Re-validation requested, attempting force refresh');
            $refresh_result = $this->force_refresh_license_status();
            
            if ($refresh_result) {
                // Get updated license data after refresh
                $updated_license_data = $this->get_license_data();
                wp_send_json_success(array(
                    'message' => 'License successfully refreshed and validated',
                    'tier' => $updated_license_data['tier'],
                    'status' => $updated_license_data['status'],
                    'expires' => isset($updated_license_data['expires']) ? $updated_license_data['expires'] : '',
                    'max_addons' => $this->get_max_addons()
                ));
                return;
            }
            
            // Fallback to basic validation if refresh failed
            $validation_result = $this->validate_license_basic($license_key);
            
            if ($validation_result['valid']) {
                wp_send_json_success(array(
                    'message' => 'License key format is valid (basic validation)',
                    'tier' => strtolower($validation_result['tier']),
                    'status' => $validation_result['status'],
                    'note' => 'Remote validation not configured'
                ));
            } else {
                // Add support contact message to error
                $error_message = $validation_result['error'];
                if (strpos($error_message, 'contact support') === false) {
                    $error_message .= ' For more help, please contact support.';
                }
                wp_send_json_error(array('message' => $error_message));
            }
        } catch (Exception $e) {
            $this->debug_log('ExploreXR License Validation Error: ' . $e->getMessage());
            wp_send_json_error(array(
                'message' => 'An error occurred while validating your license. For more help, please contact support.'
            ));
        } catch (Error $e) {
            $this->debug_log('ExploreXR License Validation Fatal Error: ' . $e->getMessage());
            wp_send_json_error(array(
                'message' => 'A system error occurred. For more help, please contact support.'
            ));
        }
    }
    
    /**
     * Basic license validation fallback (when WooCommerce integration is not available).
     * Also handles EXTR- trial keys locally so they never hit the remote API.
     */
    private function validate_license_basic($license_key) {
        // Trial keys: always handled locally with full persistence — no remote API needed.
        if (strpos($license_key, 'EXTR-') === 0) {
            $result = $this->activate_trial_license($license_key);
            if (!$result['valid']) {
                return array('valid' => false, 'error' => $result['error']);
            }
            return $result; // Contains tier, status, expires.
        }

        // Paid keys require server-side verification via ExploreXR_License_API_Integration.
        // If that class is missing, we must NOT accept the key — accepting based on format
        // alone would allow any correctly-formatted fake key to activate a full license.
        $this->debug_log('ExploreXR License: validate_license_basic() reached for paid key — integration class unavailable. Rejecting key: ' . $this->redact_license_key($license_key));
        return array(
            'valid' => false,
            'error' => 'License verification service is unavailable. Please ensure the plugin is fully installed and try again. If the problem persists, please contact support.',
        );
    }
    
    /**
     * Load license configuration constants
     */
    private function load_license_config() {
        $config_file = plugin_dir_path(__FILE__) . 'license-config.php';
        if (file_exists($config_file)) {
            require_once $config_file;
        }
    }
}

/**
 * Convenience wrapper to retrieve the license handler singleton.
 *
 * Usage: ExploreXR_License_Handler()
 * The singleton is explicitly initialized by ExploreXR-Premium.php after
 * this file is loaded, so no auto-init call is needed here.
 */
if (!function_exists('ExploreXR_License_Handler')) {
    function ExploreXR_License_Handler() {
        return ExploreXR_License_Handler::instance();
    }
}
