<?php
if (!defined('ABSPATH')) die('Restricted Access');

/**
 * AI Flash Tune license, addon install, and update client.
 *
 * Temporary API base is intentionally pointed at the local license server endpoint
 * supplied for development/testing. For production, define AIFLT_LICENSE_API_BASE
 * in wp-config.php or filter `aiflt_license_api_base`.
 */
class AIFLT_License_Manager {

    const DEFAULT_API_BASE = 'https://aiflashtune.com/wp-json/aift-license/v1';
    const CACHE_KEY_STATUS = 'aiflt_license_status_cache';
    const CACHE_KEY_DATA   = 'aiflt_license_data_cache';
    const ACTIVATION_TABLE_VERSION = '1.0';
    const ACTIVATION_CHECK_INTERVAL = 604800; // 7 days.
    const ACTIVATION_GRACE_PERIOD = 2592000; // 30 days.

    private static $locked_addon_notices = [];

    /**
     * Register native WordPress update hooks.
     */
    public static function init() {
        self::maybe_create_activation_table();
        self::maybe_schedule_license_cron();
        add_filter('pre_set_site_transient_update_plugins', [__CLASS__, 'inject_plugin_updates']);
        add_filter('plugins_api', [__CLASS__, 'plugins_api_details'], 20, 3);
        add_action('aiflt_weekly_license_check_event', [__CLASS__, 'cron_check_all_activations']);
        add_action('admin_notices', [__CLASS__, 'render_locked_addon_notices']);
    }

    /**
     * Returns the saved PRO license key without exposing validation state.
     * Used by admin screens so a saved key is never mistaken for a missing key.
     *
     * @return string
     */
    public static function saved_license_key() {
        return self::get_license_key();
    }

    /**
     * True when a PRO license key is saved locally.
     *
     * @return bool
     */
    public static function has_saved_license_key() {
        return self::get_license_key() !== '';
    }

    /**
     * Checks if the user has a valid PRO license based on the server check.
     *
     * @return bool
     */
    public static function is_pro_user() {
        $cached_status = get_transient(self::CACHE_KEY_STATUS);
        if ($cached_status === 'valid') {
            return true;
        }

        $status_data = self::get_addon_statuses(true);
        if (isset($status_data['status']) && $status_data['status'] === 'success') {
            set_transient(self::CACHE_KEY_STATUS, 'valid', HOUR_IN_SECONDS);
            return true;
        }

        if (isset($status_data['status'])) {
            set_transient(self::CACHE_KEY_STATUS, 'invalid', HOUR_IN_SECONDS);
        }

        return false;
    }

    /**
     * Gets addon entitlements by validating the license key with the server.
     *
     * @param bool $allow_cache Whether cached license data can be returned.
     * @return array ['status', 'message', 'data', 'token']
     */
    public static function get_addon_statuses($allow_cache = false) {
        $license_key = self::get_license_key();

        if (empty($license_key)) {
            delete_transient(self::CACHE_KEY_STATUS);
            delete_transient(self::CACHE_KEY_DATA);
            return [
                'status'  => 'no_key',
                'message' => __('No license key has been saved. Please enter your key on the Settings page.', 'ai-flash-tune'),
                'data'    => [],
                'token'   => '',
            ];
        }

        if ($allow_cache) {
            $cached = get_transient(self::CACHE_KEY_DATA);
            if (is_array($cached) && !empty($cached['status'])) {
                return $cached;
            }
        }

        $response = self::remote_post('check', self::base_payload());

        if (!$response['success']) {
            $result = [
                'status'  => 'error',
                'message' => $response['message'],
                'data'    => [],
                'token'   => '',
            ];
            set_transient(self::CACHE_KEY_STATUS, 'invalid', HOUR_IN_SECONDS);
            return $result;
        }

        $body = $response['body'];
        if (self::response_is_success($body)) {
            $entitlements = self::normalize_entitlements($body);
            $grants_all_modules = empty($entitlements) || self::response_value($body, [
                'all_addons',
                'all_modules',
                'full_access',
                'pro',
                'is_pro',
                'data.all_addons',
                'data.all_modules',
                'data.full_access',
                'data.pro',
                'data.is_pro',
                'data.license.all_addons',
                'data.license.all_modules',
                'data.license.full_access',
            ], null);

            $result = [
                'status'      => 'success',
                'message'     => self::response_message($body, __('License key verified.', 'ai-flash-tune')),
                'data'        => $entitlements,
                'all_modules' => (bool) $grants_all_modules,
                'token'       => self::response_value($body, ['token', 'license_token', 'download_token', 'data.token', 'data.license.token'], ''),
                'raw'         => $body,
            ];
            set_transient(self::CACHE_KEY_STATUS, 'valid', DAY_IN_SECONDS);
            set_transient(self::CACHE_KEY_DATA, $result, 15 * MINUTE_IN_SECONDS);
            return $result;
        }

        $result = [
            'status'  => 'invalid_key',
            'message' => self::response_message($body, __('Invalid license key or communication error.', 'ai-flash-tune')),
            'data'    => [],
            'token'   => '',
            'raw'     => $body,
        ];
        set_transient(self::CACHE_KEY_STATUS, 'invalid', HOUR_IN_SECONDS);
        set_transient(self::CACHE_KEY_DATA, $result, 5 * MINUTE_IN_SECONDS);
        return $result;
    }

    /**
     * Handles AJAX requests for installing, activating, deactivating, deleting,
     * or checking updates for addons.
     */
    public static function install_and_activate_addon_ajax() {
        check_ajax_referer('aiflt-addon-management-nonce', '_ajax_nonce');

        if (!function_exists('get_plugins')) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }

        $addon_slug = isset($_POST['addon_slug']) ? self::normalize_addon_slug(wp_unslash($_POST['addon_slug'])) : '';
        $token = isset($_POST['token']) ? sanitize_text_field(wp_unslash($_POST['token'])) : '';
        $action = isset($_POST['addon_action']) ? sanitize_key(wp_unslash($_POST['addon_action'])) : '';

        if (empty($addon_slug) || empty($action)) {
            wp_send_json_error(['message' => __('Invalid action or addon specified.', 'ai-flash-tune')]);
        }

        $all_plugins = get_plugins();
        $plugin_file = self::find_plugin_file($addon_slug, $all_plugins);
        $installed_version = ($plugin_file && isset($all_plugins[$plugin_file]['Version'])) ? $all_plugins[$plugin_file]['Version'] : '';

        if ($action === 'check_update' || $action === 'update_check') {
            if (!current_user_can('update_plugins')) {
                wp_send_json_error(['message' => __('You do not have permission to check plugin updates.', 'ai-flash-tune')]);
            }

            $update = self::check_update($addon_slug, $installed_version);
            if (is_wp_error($update)) {
                wp_send_json_error(['message' => $update->get_error_message()]);
            }

            if (!empty($update['update_available'])) {
                wp_send_json_success([
                    'message' => sprintf(
                        /* translators: %s: version number */
                        __('Update available: version %s.', 'ai-flash-tune'),
                        $update['version'] ?? $update['new_version'] ?? ''
                    ),
                    'update' => $update,
                    'reload' => false,
                ]);
            }

            wp_send_json_success([
                'message' => __('This module is already up to date.', 'ai-flash-tune'),
                'update'  => $update,
                'reload'  => false,
            ]);
        }

        if ($action === 'activate') {
            if (!current_user_can('activate_plugins')) {
                wp_send_json_error(['message' => __('You do not have permission to activate plugins.', 'ai-flash-tune')]);
            }

            if (!$plugin_file) {
                wp_send_json_error(['message' => __('Plugin file not found. Please install the module first.', 'ai-flash-tune')]);
            }

            if (is_plugin_active($plugin_file)) {
                wp_send_json_success(['message' => __('Feature is already active.', 'ai-flash-tune'), 'reload' => true]);
            }

            $license_result = self::activate_addon_license($addon_slug);
            if (!$license_result['success']) {
                wp_send_json_error(['message' => $license_result['message']]);
            }

            $result = activate_plugin($plugin_file);
            if (is_wp_error($result)) {
                wp_send_json_error(['message' => $result->get_error_message()]);
            }

            wp_send_json_success(['message' => __('Feature activated successfully!', 'ai-flash-tune'), 'reload' => true]);
        }

        if ($action === 'deactivate') {
            if (!current_user_can('activate_plugins')) {
                wp_send_json_error(['message' => __('You do not have permission to deactivate plugins.', 'ai-flash-tune')]);
            }

            if (!$plugin_file) {
                wp_send_json_error(['message' => __('Plugin file not found.', 'ai-flash-tune')]);
            }

            if (is_plugin_active($plugin_file)) {
                deactivate_plugins($plugin_file);
                self::deactivate_addon_license($addon_slug, true);
                wp_send_json_success(['message' => __('Feature deactivated successfully!', 'ai-flash-tune'), 'reload' => true]);
            }

            wp_send_json_success(['message' => __('Feature is already inactive.', 'ai-flash-tune'), 'reload' => true]);
        }

        if ($action === 'delete') {
            if (!current_user_can('delete_plugins')) {
                wp_send_json_error(['message' => __('You do not have permission to delete plugins.', 'ai-flash-tune')]);
            }

            if (!$plugin_file) {
                wp_send_json_error(['message' => __('Plugin file not found.', 'ai-flash-tune')]);
            }

            if (is_plugin_active($plugin_file)) {
                wp_send_json_error(['message' => __('Deactivate this module before deleting it.', 'ai-flash-tune')]);
            }

            self::delete_addon_license($addon_slug);

            if (!function_exists('delete_plugins')) {
                require_once ABSPATH . 'wp-admin/includes/plugin.php';
            }
            $deleted = delete_plugins([$plugin_file]);
            if (is_wp_error($deleted)) {
                wp_send_json_error(['message' => $deleted->get_error_message()]);
            }

            wp_send_json_success(['message' => __('Feature deleted successfully.', 'ai-flash-tune'), 'reload' => true]);
        }

        if ($action === 'install') {
            if (!current_user_can('install_plugins')) {
                wp_send_json_error(['message' => __('You do not have permission to install plugins.', 'ai-flash-tune')]);
            }

            if (empty(self::get_license_key())) {
                wp_send_json_error(['message' => __('A valid license key is required to install this module.', 'ai-flash-tune')]);
            }

            // Activate this exact site/domain before download. The server can reject if the domain
            // is not allowed or the license has reached its site limit.
            $license_result = self::activate_addon_license($addon_slug);
            if (!$license_result['success']) {
                wp_send_json_error(['message' => $license_result['message']]);
            }

            $package = self::download_addon_package($addon_slug, $token);
            if (is_wp_error($package)) {
                wp_send_json_error(['message' => $package->get_error_message()]);
            }

            include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
            include_once ABSPATH . 'wp-admin/includes/plugin-install.php';
            include_once ABSPATH . 'wp-admin/includes/file.php';

            $skin = new WP_Ajax_Upgrader_Skin();
            $upgrader = new Plugin_Upgrader($skin);
            $result = $upgrader->install($package);

            if (is_wp_error($result)) {
                wp_send_json_error(['message' => $result->get_error_message()]);
            }
            if (!$result) {
                $error_message = __('Module installation failed.', 'ai-flash-tune');
                if (!empty($skin->result) && is_wp_error($skin->result)) {
                    $error_message = $skin->result->get_error_message();
                }
                wp_send_json_error(['message' => $error_message]);
            }

            $plugin_file = $upgrader->plugin_info();
            if ($plugin_file) {
                $license_result = self::activate_addon_license($addon_slug);
                if (!$license_result['success']) {
                    wp_send_json_error(['message' => $license_result['message']]);
                }

                $activate_result = activate_plugin($plugin_file);
                if (is_wp_error($activate_result)) {
                    wp_send_json_error(['message' => $activate_result->get_error_message()]);
                }
            }

            wp_send_json_success(['message' => __('Feature installed and activated successfully!', 'ai-flash-tune'), 'reload' => true]);
        }

        wp_send_json_error(['message' => __('Invalid action specified.', 'ai-flash-tune')]);
    }

    /**
     * Contacts the license server to activate an addon for the current site.
     *
     * @param string $addon_slug
     * @return array ['success' => bool, 'message' => string]
     */
    public static function activate_addon_license($addon_slug) {
        if (empty(self::get_license_key())) {
            return ['success' => false, 'message' => __('A valid license key is required to activate this module.', 'ai-flash-tune')];
        }

        $response = self::remote_post('activate', self::base_payload($addon_slug));
        if (!$response['success']) {
            return ['success' => false, 'message' => $response['message']];
        }

        if (self::response_is_success($response['body'])) {
            $message = self::response_message($response['body'], __('License activated for this site.', 'ai-flash-tune'));
            self::store_activation_from_response($addon_slug, $response['body'], 'active', $message);
            delete_transient(self::CACHE_KEY_DATA);
            set_transient(self::CACHE_KEY_STATUS, 'valid', DAY_IN_SECONDS);
            return ['success' => true, 'message' => $message];
        }

        $message = self::response_message($response['body'], __('License could not be activated for this site.', 'ai-flash-tune'));
        self::mark_activation_status($addon_slug, 'locked', $message);
        return ['success' => false, 'message' => $message];
    }

    /**
     * Contacts the license server to deactivate an addon for the current site.
     *
     * @param string $addon_slug
     * @param bool   $blocking
     * @return bool
     */
    public static function deactivate_addon_license($addon_slug, $blocking = false) {
        if (empty(self::get_license_key())) {
            return false;
        }

        $response = self::remote_post('deactivate', self::base_payload($addon_slug), $blocking);
        self::mark_activation_status($addon_slug, 'inactive', __('Module deactivated on this site.', 'ai-flash-tune'));
        delete_transient(self::CACHE_KEY_DATA);
        return $response['success'];
    }

    /**
     * Notifies the license server that a local addon copy/activation should be removed.
     *
     * @param string $addon_slug
     * @return bool
     */
    public static function delete_addon_license($addon_slug) {
        if (empty(self::get_license_key())) {
            return false;
        }

        $response = self::remote_post('delete', self::base_payload($addon_slug));
        self::mark_activation_status($addon_slug, 'deleted', __('Module deleted from this site.', 'ai-flash-tune'));
        delete_transient(self::CACHE_KEY_DATA);
        return $response['success'];
    }

    /**
     * Check available update for a plugin/addon using GET /update-check.
     *
     * @param string $plugin_slug
     * @param string $current_version
     * @return array|WP_Error
     */
    public static function check_update($plugin_slug, $current_version = '') {
        $plugin_slug = self::normalize_addon_slug($plugin_slug);
        $payload = self::base_payload($plugin_slug, $current_version);
        $response = self::remote_get('update-check', $payload);

        if (!$response['success']) {
            return new WP_Error('aiflt_update_check_failed', $response['message']);
        }

        $body = is_array($response['body']) ? $response['body'] : [];
        $new_version = self::response_value($body, ['new_version', 'latest_version', 'version', 'data.new_version', 'data.version'], '');
        $has_update = false;

        if (!empty($new_version) && !empty($current_version)) {
            $has_update = version_compare($new_version, $current_version, '>');
        }

        $explicit_update = self::response_value($body, ['update_available', 'has_update', 'data.update_available'], null);
        if ($explicit_update !== null) {
            $has_update = (bool) $explicit_update;
        }

        $body['update_available'] = $has_update;
        if (!isset($body['version']) && !empty($new_version)) {
            $body['version'] = $new_version;
        }

        return $body;
    }

    /**
     * Add update objects to WordPress' plugin update transient.
     */
    public static function inject_plugin_updates($transient) {
        if (!is_object($transient)) {
            return $transient;
        }

        if (!function_exists('get_plugins')) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }

        $plugins = get_plugins();
        foreach ($plugins as $plugin_file => $details) {
            $slug = dirname($plugin_file);
            if (!self::is_managed_plugin_slug($slug)) {
                continue;
            }

            $version = $details['Version'] ?? '';
            $cache_key = 'aiflt_update_' . md5($slug . '|' . $version . '|' . self::get_license_key());
            $update = get_transient($cache_key);
            if ($update === false) {
                $update = self::check_update($slug, $version);
                if (is_wp_error($update)) {
                    set_transient($cache_key, ['update_available' => false], 6 * HOUR_IN_SECONDS);
                    continue;
                }
                set_transient($cache_key, $update, 6 * HOUR_IN_SECONDS);
            }

            if (empty($update['update_available'])) {
                continue;
            }

            $new_version = self::response_value($update, ['new_version', 'latest_version', 'version', 'data.new_version', 'data.version'], '');
            if (empty($new_version) || version_compare($new_version, $version, '<=')) {
                continue;
            }

            $package = self::response_value($update, ['package', 'download_url', 'url', 'data.package', 'data.download_url'], '');
            $transient->response[$plugin_file] = (object) [
                'id'          => $plugin_file,
                'slug'        => $slug,
                'plugin'      => $plugin_file,
                'new_version' => $new_version,
                'url'         => self::response_value($update, ['homepage', 'plugin_url', 'url', 'data.homepage'], 'https://www.aiflashtune.com'),
                'package'     => esc_url_raw($package),
                'tested'      => self::response_value($update, ['tested', 'tested_wp', 'data.tested'], ''),
                'requires'    => self::response_value($update, ['requires', 'requires_wp', 'data.requires'], ''),
                'requires_php'=> self::response_value($update, ['requires_php', 'data.requires_php'], ''),
            ];
        }

        return $transient;
    }

    /**
     * Populate WordPress' plugin details modal from update-check response.
     */
    public static function plugins_api_details($result, $action, $args) {
        if ($action !== 'plugin_information' || empty($args->slug) || !self::is_managed_plugin_slug($args->slug)) {
            return $result;
        }

        $update = self::check_update($args->slug, '0.0.0');
        if (is_wp_error($update)) {
            return $result;
        }

        $name = self::response_value($update, ['name', 'plugin_name', 'data.name'], ucwords(str_replace('-', ' ', $args->slug)));
        $version = self::response_value($update, ['new_version', 'latest_version', 'version', 'data.version'], '');
        $sections = self::response_value($update, ['sections', 'data.sections'], []);
        if (!is_array($sections)) {
            $sections = ['description' => (string) $sections];
        }
        if (empty($sections['description'])) {
            $sections['description'] = self::response_value($update, ['description', 'data.description'], __('AI Flash Tune module update.', 'ai-flash-tune'));
        }

        return (object) [
            'name'          => $name,
            'slug'          => $args->slug,
            'version'       => $version,
            'author'        => '<a href="https://www.aiflashtune.com">AI Flash Tune</a>',
            'homepage'      => self::response_value($update, ['homepage', 'plugin_url', 'data.homepage'], 'https://www.aiflashtune.com'),
            'requires'      => self::response_value($update, ['requires', 'requires_wp', 'data.requires'], ''),
            'tested'        => self::response_value($update, ['tested', 'tested_wp', 'data.tested'], ''),
            'requires_php'  => self::response_value($update, ['requires_php', 'data.requires_php'], ''),
            'download_link' => self::response_value($update, ['package', 'download_url', 'data.download_url'], ''),
            'sections'      => $sections,
        ];
    }

    /**
     * Create/upgrade the local activation ledger used by addons at runtime.
     */
    public static function maybe_create_activation_table() {
        global $wpdb;
        $table = self::activation_table();
        $charset_collate = $wpdb->get_charset_collate();
        $sql = "CREATE TABLE {$table} (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            addon_slug varchar(190) NOT NULL,
            product_code varchar(120) NOT NULL DEFAULT '',
            license_hash char(64) NOT NULL DEFAULT '',
            site_hash char(64) NOT NULL DEFAULT '',
            site_url text NULL,
            home_url text NULL,
            activation_id varchar(191) NULL,
            activation_token varchar(255) NULL,
            activation_token_hash char(64) NULL,
            status varchar(30) NOT NULL DEFAULT 'active',
            server_message text NULL,
            last_checked_at datetime NULL,
            next_check_at datetime NULL,
            grace_until datetime NULL,
            expires_at datetime NULL,
            created_at datetime NOT NULL,
            updated_at datetime NOT NULL,
            PRIMARY KEY  (id),
            UNIQUE KEY addon_slug (addon_slug),
            KEY status (status),
            KEY license_hash (license_hash),
            KEY site_hash (site_hash)
        ) {$charset_collate};";

        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        dbDelta($sql);
        update_option('aiflt_license_activation_table_version', self::ACTIVATION_TABLE_VERSION);
    }

    public static function maybe_schedule_license_cron() {
        if (!wp_next_scheduled('aiflt_weekly_license_check_event')) {
            wp_schedule_event(time() + HOUR_IN_SECONDS, 'weekly', 'aiflt_weekly_license_check_event');
        }
    }

    public static function cron_check_all_activations() {
        global $wpdb;
        self::maybe_create_activation_table();
        $table = self::activation_table();
        $rows = $wpdb->get_results("SELECT addon_slug FROM {$table} WHERE status IN ('active','valid','grace')", ARRAY_A);
        if (!is_array($rows)) {
            return;
        }
        foreach ($rows as $row) {
            if (!empty($row['addon_slug'])) {
                self::refresh_addon_activation($row['addon_slug'], true);
            }
        }
    }

    public static function clear_activation_records($reason = '') {
        global $wpdb;
        self::maybe_create_activation_table();
        $message = $reason ? sanitize_text_field($reason) : __('License changed. Activation records cleared.', 'ai-flash-tune');
        $wpdb->query(
            $wpdb->prepare(
                "UPDATE " . self::activation_table() . " SET status=%s, server_message=%s, updated_at=%s WHERE 1=1",
                'locked',
                $message,
                self::mysql_now()
            )
        );
        delete_transient(self::CACHE_KEY_DATA);
        delete_transient(self::CACHE_KEY_STATUS);
    }

    public static function is_addon_licensed($addon_slug, $force_check = false) {
        $addon_slug = self::normalize_addon_slug($addon_slug);
        if ($addon_slug === '' || $addon_slug === 'ai-flash-tune') {
            return self::has_saved_license_key();
        }
        if (!self::has_saved_license_key()) {
            return false;
        }

        $record = self::get_activation_record($addon_slug);
        if (!is_array($record)) {
            return false;
        }

        if (!self::record_matches_current_site($record)) {
            self::mark_activation_status($addon_slug, 'locked', __('This activation record does not match the current license key or domain.', 'ai-flash-tune'));
            return false;
        }

        if ($force_check || self::record_check_is_due($record)) {
            $refresh = self::refresh_addon_activation($addon_slug, true);
            if (!empty($refresh['success'])) {
                return true;
            }
            $record = self::get_activation_record($addon_slug);
            if (is_array($record) && self::record_is_in_grace($record)) {
                return true;
            }
            return false;
        }

        return self::record_status_allows_runtime($record);
    }

    public static function verify_addon_runtime_license($addon_slug, $addon_name = '') {
        $addon_slug = self::normalize_addon_slug($addon_slug);
        if (self::is_addon_licensed($addon_slug, false)) {
            return true;
        }
        self::queue_locked_addon_notice($addon_slug, $addon_name);
        return false;
    }

    public static function render_locked_addon_notices() {
        if (!is_admin() || empty(self::$locked_addon_notices) || !current_user_can('manage_options')) {
            return;
        }
        foreach (self::$locked_addon_notices as $notice) {
            $addon_name = !empty($notice['name']) ? $notice['name'] : $notice['slug'];
            $message = sprintf(
                /* translators: %s: addon name */
                __('%s is installed but not licensed for this domain. Activate it from AI Flash Tune → Addon Manager. Copied addon files will stay locked unless the license server approves this site.', 'ai-flash-tune'),
                $addon_name
            );
            echo '<div class="notice notice-error"><p><strong>' . esc_html__('AI Flash Tune License Required:', 'ai-flash-tune') . '</strong> ' . esc_html($message) . ' <a href="' . esc_url(admin_url('admin.php?page=aiflt_addon_manager')) . '">' . esc_html__('Open Addon Manager', 'ai-flash-tune') . '</a></p></div>';
        }
    }

    public static function get_activation_record($addon_slug) {
        global $wpdb;
        $addon_slug = self::normalize_addon_slug($addon_slug);
        if ($addon_slug === '') {
            return null;
        }
        self::maybe_create_activation_table();
        return $wpdb->get_row(
            $wpdb->prepare("SELECT * FROM " . self::activation_table() . " WHERE addon_slug = %s LIMIT 1", $addon_slug),
            ARRAY_A
        );
    }

    private static function refresh_addon_activation($addon_slug, $blocking = true) {
        $addon_slug = self::normalize_addon_slug($addon_slug);
        if ($addon_slug === '' || !self::has_saved_license_key()) {
            return ['success' => false, 'message' => __('Missing license key.', 'ai-flash-tune')];
        }

        $response = self::remote_post('check', self::base_payload($addon_slug), $blocking);
        if (!$response['success']) {
            $record = self::get_activation_record($addon_slug);
            if (is_array($record) && self::record_is_in_grace($record)) {
                self::mark_activation_status($addon_slug, 'grace', $response['message']);
                return ['success' => true, 'message' => $response['message'], 'grace' => true];
            }
            self::mark_activation_status($addon_slug, 'locked', $response['message']);
            return ['success' => false, 'message' => $response['message']];
        }

        if (self::response_is_success($response['body'])) {
            $message = self::response_message($response['body'], __('License verified for this site.', 'ai-flash-tune'));
            self::store_activation_from_response($addon_slug, $response['body'], 'active', $message);
            return ['success' => true, 'message' => $message];
        }

        $message = self::response_message($response['body'], __('This site is not licensed for this module.', 'ai-flash-tune'));
        self::mark_activation_status($addon_slug, 'locked', $message);
        return ['success' => false, 'message' => $message];
    }

    private static function store_activation_from_response($addon_slug, $body, $status = 'active', $message = '') {
        global $wpdb;
        self::maybe_create_activation_table();
        $addon_slug = self::normalize_addon_slug($addon_slug);
        $product_code = self::addon_slug_to_product_code($addon_slug);
        $now = self::mysql_now();
        $existing = self::get_activation_record($addon_slug);

        $activation_id = self::response_value($body, [
            'activation_id', 'site_id', 'instance_id', 'data.activation_id', 'data.site_id', 'data.instance_id',
            'data.activation.id', 'data.site.id', 'data.license.activation_id'
        ], is_array($existing) ? ($existing['activation_id'] ?? '') : '');

        $activation_token = self::response_value($body, [
            'activation_token', 'site_token', 'token', 'data.activation_token', 'data.site_token', 'data.token',
            'data.activation.token', 'data.site.token', 'data.license.activation_token'
        ], is_array($existing) ? ($existing['activation_token'] ?? '') : '');

        $expires_at = self::normalize_datetime(self::response_value($body, [
            'expires_at', 'expiry', 'expires', 'data.expires_at', 'data.expiry', 'data.license.expires_at'
        ], is_array($existing) ? ($existing['expires_at'] ?? null) : null));

        $data = [
            'addon_slug'            => $addon_slug,
            'product_code'          => $product_code,
            'license_hash'          => self::license_hash(),
            'site_hash'             => self::site_hash(),
            'site_url'              => site_url(),
            'home_url'              => home_url(),
            'activation_id'         => sanitize_text_field((string) $activation_id),
            'activation_token'      => sanitize_text_field((string) $activation_token),
            'activation_token_hash' => $activation_token !== '' ? hash('sha256', (string) $activation_token) : '',
            'status'                => sanitize_key($status),
            'server_message'        => sanitize_text_field((string) $message),
            'last_checked_at'       => $now,
            'next_check_at'         => self::mysql_from_timestamp(time() + self::ACTIVATION_CHECK_INTERVAL),
            'grace_until'           => self::mysql_from_timestamp(time() + self::ACTIVATION_GRACE_PERIOD),
            'expires_at'            => $expires_at,
            'updated_at'            => $now,
        ];

        if (is_array($existing)) {
            $wpdb->update(self::activation_table(), $data, ['addon_slug' => $addon_slug]);
        } else {
            $data['created_at'] = $now;
            $wpdb->insert(self::activation_table(), $data);
        }
    }

    private static function mark_activation_status($addon_slug, $status, $message = '') {
        global $wpdb;
        self::maybe_create_activation_table();
        $addon_slug = self::normalize_addon_slug($addon_slug);
        $existing = self::get_activation_record($addon_slug);
        $data = [
            'status'         => sanitize_key($status),
            'server_message' => sanitize_text_field((string) $message),
            'updated_at'     => self::mysql_now(),
        ];
        if (in_array($status, ['locked', 'grace'], true)) {
            $data['next_check_at'] = self::mysql_from_timestamp(time() + DAY_IN_SECONDS);
        }
        if (is_array($existing)) {
            $wpdb->update(self::activation_table(), $data, ['addon_slug' => $addon_slug]);
        } else {
            $data = array_merge([
                'addon_slug'            => $addon_slug,
                'product_code'          => self::addon_slug_to_product_code($addon_slug),
                'license_hash'          => self::license_hash(),
                'site_hash'             => self::site_hash(),
                'site_url'              => site_url(),
                'home_url'              => home_url(),
                'activation_id'         => '',
                'activation_token'      => '',
                'activation_token_hash' => '',
                'last_checked_at'       => self::mysql_now(),
                'next_check_at'         => self::mysql_from_timestamp(time() + DAY_IN_SECONDS),
                'grace_until'           => self::mysql_now(),
                'expires_at'            => null,
                'created_at'            => self::mysql_now(),
            ], $data);
            $wpdb->insert(self::activation_table(), $data);
        }
    }

    private static function queue_locked_addon_notice($addon_slug, $addon_name = '') {
        self::$locked_addon_notices[$addon_slug] = [
            'slug' => self::normalize_addon_slug($addon_slug),
            'name' => $addon_name ? sanitize_text_field($addon_name) : self::normalize_addon_slug($addon_slug),
        ];
    }

    private static function record_matches_current_site($record) {
        return isset($record['license_hash'], $record['site_hash'])
            && hash_equals((string) $record['license_hash'], self::license_hash())
            && hash_equals((string) $record['site_hash'], self::site_hash());
    }

    private static function record_status_allows_runtime($record) {
        $status = strtolower((string) ($record['status'] ?? ''));
        if (!in_array($status, ['active', 'valid', 'grace'], true)) {
            return false;
        }
        if (!empty($record['expires_at']) && strtotime($record['expires_at']) && strtotime($record['expires_at']) < time()) {
            return false;
        }
        return true;
    }

    private static function record_check_is_due($record) {
        if (empty($record['next_check_at'])) {
            return true;
        }
        $ts = strtotime($record['next_check_at']);
        return !$ts || $ts <= time();
    }

    private static function record_is_in_grace($record) {
        if (!self::record_matches_current_site($record)) {
            return false;
        }
        if (!self::record_status_allows_runtime($record) && strtolower((string) ($record['status'] ?? '')) !== 'grace') {
            return false;
        }
        if (empty($record['grace_until'])) {
            return false;
        }
        $ts = strtotime($record['grace_until']);
        return $ts && $ts > time();
    }

    private static function activation_table() {
        global $wpdb;
        return $wpdb->prefix . 'aiflt_license_activations';
    }

    private static function license_hash() {
        return hash('sha256', self::get_license_key());
    }

    private static function site_hash() {
        return hash('sha256', strtolower(self::site_domain() . '|' . home_url()));
    }

    private static function mysql_now() {
        return gmdate('Y-m-d H:i:s');
    }

    private static function mysql_from_timestamp($timestamp) {
        return gmdate('Y-m-d H:i:s', (int) $timestamp);
    }

    private static function normalize_datetime($value) {
        if (empty($value)) {
            return null;
        }
        if (is_numeric($value)) {
            return self::mysql_from_timestamp((int) $value);
        }
        $timestamp = strtotime((string) $value);
        return $timestamp ? self::mysql_from_timestamp($timestamp) : null;
    }

    /**
     * Download addon zip through POST /download. Supports JSON download_url or raw ZIP response.
     *
     * @param string $addon_slug
     * @param string $token
     * @return string|WP_Error URL or local temp ZIP path.
     */
    private static function download_addon_package($addon_slug, $token = '') {
        if (!self::is_addon_licensed($addon_slug, true)) {
            return new WP_Error('aiflt_download_license_required', __('This site is not activated for this module. Please refresh the license or activate the module again.', 'ai-flash-tune'));
        }

        $payload = self::base_payload($addon_slug);
        if (!empty($token)) {
            $payload['token'] = $token;
        }

        $response = wp_remote_post(self::endpoint('download'), [
            'body'      => $payload,
            'timeout'   => 60,
            'sslverify' => self::sslverify(),
            'headers'   => [
                'Accept' => 'application/json, application/zip, application/octet-stream',
            ],
        ]);

        if (is_wp_error($response)) {
            return $response;
        }

        $code = (int) wp_remote_retrieve_response_code($response);
        $body = wp_remote_retrieve_body($response);
        $content_type = strtolower((string) wp_remote_retrieve_header($response, 'content-type'));

        if ($code < 200 || $code >= 300) {
            $decoded = json_decode($body, true);
            return new WP_Error('aiflt_download_failed', is_array($decoded) ? self::response_message($decoded, __('Download request failed.', 'ai-flash-tune')) : __('Download request failed.', 'ai-flash-tune'));
        }

        $decoded = json_decode($body, true);
        if (is_array($decoded)) {
            if (!self::response_is_success($decoded) && (isset($decoded['status']) || isset($decoded['success']))) {
                return new WP_Error('aiflt_download_denied', self::response_message($decoded, __('Download was denied by the license server.', 'ai-flash-tune')));
            }

            $url = self::response_value($decoded, ['download_url', 'package', 'package_url', 'url', 'data.download_url', 'data.package'], '');
            if (!empty($url)) {
                return esc_url_raw($url);
            }
        }

        $looks_like_zip = (strpos($content_type, 'zip') !== false || substr($body, 0, 2) === 'PK');
        if ($looks_like_zip && !empty($body)) {
            if (!function_exists('wp_tempnam')) {
                require_once ABSPATH . 'wp-admin/includes/file.php';
            }
            $tmp = wp_tempnam($addon_slug . '.zip');
            if (!$tmp) {
                return new WP_Error('aiflt_temp_file_failed', __('Could not create a temporary file for the addon package.', 'ai-flash-tune'));
            }
            file_put_contents($tmp, $body);
            return $tmp;
        }

        return new WP_Error('aiflt_download_invalid_response', __('The license server did not return a valid addon package.', 'ai-flash-tune'));
    }

    private static function remote_post($endpoint, $payload, $blocking = true) {
        $response = wp_remote_post(self::endpoint($endpoint), [
            'body'      => $payload,
            'timeout'   => $blocking ? 20 : 5,
            'blocking'  => $blocking,
            'sslverify' => self::sslverify(),
            'headers'   => ['Accept' => 'application/json'],
        ]);

        if (!$blocking) {
            return ['success' => !is_wp_error($response), 'message' => '', 'body' => []];
        }

        return self::normalize_remote_response($response);
    }

    private static function remote_get($endpoint, $payload) {
        $url = add_query_arg(array_filter($payload, static function($value) {
            return $value !== '' && $value !== null;
        }), self::endpoint($endpoint));

        $response = wp_remote_get($url, [
            'timeout'   => 20,
            'sslverify' => self::sslverify(),
            'headers'   => ['Accept' => 'application/json'],
        ]);

        return self::normalize_remote_response($response);
    }

    private static function normalize_remote_response($response) {
        if (is_wp_error($response)) {
            return ['success' => false, 'message' => $response->get_error_message(), 'body' => []];
        }

        $code = (int) wp_remote_retrieve_response_code($response);
        $raw_body = wp_remote_retrieve_body($response);
        $decoded = json_decode($raw_body, true);
        $body = is_array($decoded) ? $decoded : ['raw_body' => $raw_body];

        if ($code < 200 || $code >= 300) {
            return [
                'success' => false,
                'message' => self::response_message($body, __('Could not connect to the license server.', 'ai-flash-tune')),
                'body'    => $body,
            ];
        }

        return [
            'success' => true,
            'message' => self::response_message($body, ''),
            'body'    => $body,
        ];
    }

    private static function base_payload($addon_slug = '', $current_version = '') {
        $license_key = self::get_license_key();
        $addon_slug = self::normalize_addon_slug($addon_slug);
        $product_code = self::addon_slug_to_product_code($addon_slug);

        $payload = [
            'license_key'     => $license_key,
            'license'         => $license_key,
            'key'             => $license_key,
            'transactionkey'  => $license_key, // Backward-compatible with the old checker.
            'site_url'        => site_url(),
            'home_url'        => home_url(),
            'domain'          => self::site_domain(),
            'site_hash'       => self::site_hash(),
            'plugin_slug'     => $addon_slug,
            'addon_slug'      => $addon_slug,
            'product_slug'    => $addon_slug,
            'product_code'    => $product_code,
            'productcode'     => $product_code,
            'current_version' => $current_version,
            'version'         => $current_version,
            'wp_version'      => get_bloginfo('version'),
            'php_version'     => PHP_VERSION,
            'locale'          => get_locale(),
        ];

        if ($addon_slug !== '') {
            $activation = self::get_activation_record($addon_slug);
            if (is_array($activation)) {
                if (!empty($activation['activation_id'])) {
                    $payload['activation_id'] = $activation['activation_id'];
                }
                if (!empty($activation['activation_token'])) {
                    $payload['activation_token'] = $activation['activation_token'];
                    $payload['site_token'] = $activation['activation_token'];
                }
            }
        }

        return $payload;
    }

    private static function api_base() {
        $base = defined('AIFLT_LICENSE_API_BASE') ? AIFLT_LICENSE_API_BASE : get_option('aiflt_license_server_url', self::DEFAULT_API_BASE);
        $base = apply_filters('aiflt_license_api_base', $base);
        return untrailingslashit((string) $base);
    }

    private static function endpoint($name) {
        return self::api_base() . '/' . ltrim($name, '/');
    }

    private static function sslverify() {
        $base = self::api_base();
        if (strpos($base, 'localhost') !== false || strpos($base, '127.0.0.1') !== false) {
            return false;
        }
        return (bool) apply_filters('aiflt_license_sslverify', true);
    }

    private static function get_license_key() {
        return trim((string) get_option('aiflt_pro_license_key', ''));
    }

    private static function site_domain() {
        $host = wp_parse_url(home_url(), PHP_URL_HOST);
        return $host ? $host : home_url();
    }

    private static function find_plugin_file($addon_slug, $plugins) {
        $addon_slug = self::normalize_addon_slug($addon_slug);
        foreach ($plugins as $file => $details) {
            if (strpos($file, $addon_slug . '/') === 0) {
                return $file;
            }
        }
        return null;
    }

    private static function is_managed_plugin_slug($slug) {
        return $slug === 'ai-flash-tune' || strpos($slug, 'ai-flash-tune-') === 0;
    }

    private static function normalize_addon_slug($slug) {
        $slug = sanitize_title(str_replace('_', '-', (string) $slug));
        if ($slug === '') {
            return '';
        }
        if ($slug === 'base' || $slug === 'core') {
            return 'ai-flash-tune';
        }
        if (strpos($slug, 'ai-flash-tune') === 0) {
            return $slug;
        }
        return 'ai-flash-tune-' . $slug;
    }

    private static function addon_slug_to_product_code($addon_slug) {
        $addon_slug = self::normalize_addon_slug($addon_slug);
        if ($addon_slug === 'ai-flash-tune') {
            return 'base';
        }
        return preg_replace('/^ai-flash-tune-/', '', $addon_slug);
    }

    private static function normalize_entitlements($body) {
        $entitlements = self::response_value($body, [
            'entitlements',
            'addons',
            'modules',
            'products',
            'active_products',
            'allowed_products',
            'licensed_products',
            'data.entitlements',
            'data.addons',
            'data.modules',
            'data.products',
            'data.active_products',
            'data.allowed_products',
            'data.licensed_products',
            'data.license.entitlements',
            'data.license.addons',
            'data.license.modules',
            'data.license.products',
        ], []);
        $normalized = [];

        if (!is_array($entitlements)) {
            return $normalized;
        }

        foreach ($entitlements as $key => $item) {
            if (is_string($key) && !is_numeric($key)) {
                $slug = self::normalize_addon_slug($key);
                if ($slug !== '') {
                    $normalized[$slug] = is_array($item) ? $item : ['enabled' => self::truthy($item)];
                }
                continue;
            }

            if (is_string($item)) {
                $slug = self::normalize_addon_slug($item);
                if ($slug !== '') {
                    $normalized[$slug] = ['enabled' => true];
                }
                continue;
            }

            if (is_array($item)) {
                $code = self::response_value($item, [
                    'slug',
                    'addon_slug',
                    'module_slug',
                    'plugin_slug',
                    'product_slug',
                    'product_code',
                    'productcode',
                    'code',
                    'name',
                ], '');

                $allowed = self::response_value($item, [
                    'enabled',
                    'active',
                    'allowed',
                    'licensed',
                    'has_access',
                    'included',
                    'status',
                ], true);

                if (!empty($code) && self::truthy($allowed)) {
                    $normalized[self::normalize_addon_slug($code)] = $item;
                }
            }
        }

        return $normalized;
    }

    private static function response_is_success($body) {
        if (!is_array($body)) {
            return false;
        }

        if (array_key_exists('success', $body)) {
            return self::truthy($body['success']);
        }

        if (array_key_exists('valid', $body)) {
            return self::truthy($body['valid']);
        }

        $status = strtolower((string) self::response_value($body, ['status', 'license_status', 'data.status', 'data.license.status'], ''));
        return in_array($status, ['success', 'valid', 'active', 'ok', 'activated', 'paid', 'completed', 'enabled'], true);
    }

    private static function truthy($value) {
        if (is_bool($value)) {
            return $value;
        }
        if (is_numeric($value)) {
            return ((int) $value) === 1;
        }
        $value = strtolower(trim((string) $value));
        if ($value === '') {
            return false;
        }
        return in_array($value, ['1', 'true', 'yes', 'y', 'on', 'success', 'valid', 'active', 'ok', 'activated', 'paid', 'completed', 'enabled', 'licensed', 'allowed', 'included'], true);
    }

    private static function response_message($body, $fallback = '') {
        if (!is_array($body)) {
            return $fallback;
        }
        $message = self::response_value($body, ['message', 'error', 'errors.0', 'data.message', 'data.error'], '');
        return $message !== '' ? (string) $message : $fallback;
    }

    private static function response_value($array, $paths, $default = null) {
        if (!is_array($array)) {
            return $default;
        }

        foreach ((array) $paths as $path) {
            $parts = explode('.', (string) $path);
            $value = $array;
            $found = true;
            foreach ($parts as $part) {
                if (is_array($value) && array_key_exists($part, $value)) {
                    $value = $value[$part];
                } else {
                    $found = false;
                    break;
                }
            }
            if ($found && $value !== null && $value !== '') {
                return $value;
            }
        }

        return $default;
    }
}


if (!function_exists('aiflt_is_addon_licensed')) {
    function aiflt_is_addon_licensed($addon_slug, $force_check = false) {
        return class_exists('AIFLT_License_Manager') && AIFLT_License_Manager::is_addon_licensed($addon_slug, $force_check);
    }
}

if (!function_exists('aiflt_require_addon_license')) {
    function aiflt_require_addon_license($addon_slug, $addon_name = '') {
        return class_exists('AIFLT_License_Manager') && AIFLT_License_Manager::verify_addon_runtime_license($addon_slug, $addon_name);
    }
}
