<?php
declare(strict_types=1);
// ==================================================
// Nuclear Fancy Pants Edition™ — NUCLEAR+++
// File: /includes/class-settings-page.php
// Description: Settings admin page — system info, email testing, blog approval mode, danger zone - NUCLEAR+++
// Last Edited: 2026-04-27
// ==================================================

namespace AutoMarketerAI;

defined('ABSPATH') || exit;

final class SettingsPage
{
    private static ?self $instance = null;

    public static function init(): self
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    private function __construct()
    {
        add_action('admin_menu',                              [$this, 'addMenu']);
        add_action('admin_enqueue_scripts',                   [$this, 'enqueueAssets']);
        add_action('wp_ajax_amai_send_test_email',            [$this, 'handleSendTestEmail']);
        add_action('wp_ajax_amai_save_email_template',  [$this, 'handleSaveEmailTemplate']);
        add_action('wp_ajax_amai_reset_email_template', [$this, 'handleResetEmailTemplate']);
        add_action('wp_ajax_amai_load_email_template',  [$this, 'handleLoadEmailTemplate']);
        add_action('wp_ajax_amai_ping_core',                  [$this, 'handlePingCore']);
        add_action('wp_ajax_amai_flush_cache',                [$this, 'handleFlushCache']);
        add_action('wp_ajax_amai_reset_plugin',               [$this, 'handleResetPlugin']);
        add_action('wp_ajax_amai_competitor_add',             [$this, 'handleCompetitorAdd']);
        add_action('wp_ajax_amai_competitor_remove',          [$this, 'handleCompetitorRemove']);
        add_action('wp_ajax_amai_save_blog_approval_mode',    [$this, 'handleSaveBlogApprovalMode']);
        add_action('wp_ajax_amai_save_blog_image_source',     [$this, 'handleSaveBlogImageSource']);
        add_action('wp_ajax_amai_create_release',             [$this, 'handleCreateRelease']);
        add_action('wp_ajax_amai_get_handoffs_since',       [$this, 'handleGetHandoffsSince']);
        add_action('wp_ajax_amai_make_current_release',       [$this, 'handleMakeCurrentRelease']);
        add_action('wp_ajax_amai_delete_release',             [$this, 'handleDeleteRelease']);
        add_action('wp_ajax_amai_save_release_changelog',     [$this, 'handleSaveReleaseChangelog']);
        add_action('wp_ajax_amai_save_nudge_thresholds',       [$this, 'handleSaveNudgeThresholds']);
        add_action('wp_ajax_amai_save_recovery_discount',     [$this, 'handleSaveRecoveryDiscount']);
        add_action('wp_ajax_amai_save_twilio_credentials',     [$this, 'handleSaveTwilioCredentials']);
        add_action('wp_ajax_amai_save_sms_provider',            [$this, 'handleSaveSmsProvider']);
        add_action('wp_ajax_amai_save_store_provider', [$this, 'handleSaveStoreProvider']);
        add_action('wp_ajax_amai_save_legal_content',       [$this, 'handleSaveLegalContent']);
        add_action('wp_ajax_amai_downgrade_to_free',        [$this, 'handleDowngradeToFree']);
        add_action('wp_ajax_amai_generate_legal_content',   [$this, 'handleGenerateLegalContent']);
        add_action('wp_ajax_amai_save_comment_settings',   [$this, 'handleSaveCommentSettings']);
        add_action('wp_ajax_amai_generate_comment_draft',  [$this, 'handleGenerateCommentDraft']);
        add_action('wp_ajax_amai_send_comment_reply',      [$this, 'handleSendCommentReply']);
        add_action('wp_ajax_amai_ignore_comment',          [$this, 'handleIgnoreComment']);
        add_action('wp_ajax_amai_save_comment_form_settings', [$this, 'handleSaveCommentFormSettings']);
        add_action('wp_ajax_amai_save_legal_urls',           [$this, 'handleSaveLegalUrls']);
    }

    public function addMenu(): void
    {
        add_submenu_page(
            'automarketer-ai',
            'Settings',
            'Settings',
            'manage_automarketer_ai',
            'automarketer-ai-settings',
            [$this, 'renderPage']
        );
        add_submenu_page(
            null,
            'Install AutoMarketer AI',
            '',
            'install_plugins',
            'amai-install-onboarding',
            [$this, 'renderInstallOnboarding']
        );
    }

    public function enqueueAssets(string $hook): void
    {
        $page = sanitize_key((string) ($_GET['page'] ?? ''));
        if (strpos($hook, 'automarketer-ai') === false && $page !== 'amai-install-onboarding') {
            return;
        }

        if ($page === 'amai-install-onboarding') {
            wp_enqueue_style(
                'amai-install-onboarding',
                AMAI_PLUGIN_URL . 'assets/css/install-onboarding.css',
                [],
                (string) filemtime(AMAI_PLUGIN_DIR . 'assets/css/install-onboarding.css')
            );
            wp_enqueue_script(
                'amai-install-onboarding',
                AMAI_PLUGIN_URL . 'assets/js/amai-install-onboarding.js',
                [],
                (string) filemtime(AMAI_PLUGIN_DIR . 'assets/js/amai-install-onboarding.js'),
                true
            );
            wp_localize_script('amai-install-onboarding', 'amaiInstall', [
                'ajaxUrl' => admin_url('admin-ajax.php'),
                'nonce'   => wp_create_nonce('amai_install_onboarding'),
            ]);
            return;
        }

        wp_enqueue_style(
            'amai-settings',
            AMAI_PLUGIN_URL . 'assets/css/settings.css',
            [],
            (string) filemtime(AMAI_PLUGIN_DIR . 'assets/css/settings.css')
        );

        wp_enqueue_script(
            'amai-settings',
            AMAI_PLUGIN_URL . 'assets/js/amai-settings.js',
            [],
            (string) filemtime(AMAI_PLUGIN_DIR . 'assets/js/amai-settings.js'),
            true
        );

        wp_enqueue_editor();
        wp_localize_script('amai-settings', 'amaiSettings', [
            'ajaxUrl'           => admin_url('admin-ajax.php'),
            'nonce'             => wp_create_nonce('amai_settings_actions'),
            'blogApprovalNonce' => wp_create_nonce('amai_save_blog_approval_mode'),
            'blogImageSourceNonce' => wp_create_nonce('amai_save_blog_image_source'),
            'blogImageSource'      => (string) get_option('amai_blog_image_source', 'unsplash'),
            'adminEmail'        => get_option('admin_email', ''),
            'claudeApiKey'      => \AutoMarketerAI\KeyVault::decrypt((string) get_option('amai_api_key_ai-claude', '')),
            'handoffNonce'      => wp_create_nonce('amai_get_handoffs_since'),
            'lastReleaseDate'       => (string) get_option('amai_last_release_date', ''),
            'saveChangelogNonce'    => wp_create_nonce('amai_save_release_changelog'),
            'legalGenerateNonce'    => wp_create_nonce('amai_generate_legal_content'),
            'commentFormNonce'     => wp_create_nonce('amai_comment_form_actions'),
        ]);
    }

    public function renderPage(): void
    {
        if (!current_user_can('manage_automarketer_ai')) {
            wp_die('Unauthorized');
        }

        include AMAI_PLUGIN_DIR . 'templates/settings.php';
    }

    // --------------------------------------------------
    // AJAX -- send test email
    // --------------------------------------------------
    public function handleSendTestEmail(): void
    {
        if (!current_user_can('manage_automarketer_ai')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }

        check_ajax_referer('amai_settings_actions', '_ajax_nonce');

        $type   = sanitize_key((string) ($_POST['email_type'] ?? ''));
        $result = WebhookReceiver::sendTestEmail($type);

        if (!$result) {
            wp_send_json_error(['message' => 'Unknown email type.']);
            return;
        }

        wp_send_json_success(['message' => 'Test email sent to ' . get_option('admin_email', '')]);
    }

    // --------------------------------------------------
    // AJAX — ping Core API
    // --------------------------------------------------
    public function handlePingCore(): void
    {
        if (!current_user_can('manage_automarketer_ai')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }

        check_ajax_referer('amai_settings_actions', '_ajax_nonce');

        $start    = microtime(true);
        $response = wp_remote_get('https://core.automarketer-ai.com/healthz', ['timeout' => 10]);
        $elapsed  = round((microtime(true) - $start) * 1000);

        if (is_wp_error($response)) {
            wp_send_json_error(['message' => 'Core unreachable: ' . $response->get_error_message()]);
            return;
        }

        $code = wp_remote_retrieve_response_code($response);
        if ($code === 200) {
            wp_send_json_success(['message' => 'Core API responding (' . $elapsed . 'ms)']);
        } else {
            wp_send_json_error(['message' => 'Core returned HTTP ' . $code]);
        }
    }

    // --------------------------------------------------
    // AJAX — flush Redis cache for this tenant
    // --------------------------------------------------
    public function handleFlushCache(): void
    {
        if (!current_user_can('manage_automarketer_ai')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }

        check_ajax_referer('amai_settings_actions', '_ajax_nonce');

        wp_cache_flush();

        global $wpdb;
        $wpdb->query(
            "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_amai_%' OR option_name LIKE '_transient_timeout_amai_%'"
        );

        wp_send_json_success(['message' => 'Cache cleared.']);
    }

    // --------------------------------------------------
    // AJAX — reset plugin (nuclear)
    // --------------------------------------------------
    public function handleResetPlugin(): void
    {
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }

        check_ajax_referer('amai_settings_actions', '_ajax_nonce');

        $confirm = sanitize_text_field((string) ($_POST['confirm'] ?? ''));
        if ($confirm !== 'RESET') {
            wp_send_json_error(['message' => 'Confirmation text did not match.']);
            return;
        }

        // Plan and license state
        delete_option('automarketer_ai_plan_id');
        delete_option('automarketer_ai_license_status');
        delete_option('automarketer_ai_stripe_sub_id');
        delete_option('automarketer_ai_stripe_customer_id');
        delete_option('amai_primary_user_id');
        delete_option('amai_tenant_id');
        delete_option('amai_active_modules');
        delete_option('amai_approval_mode');
        delete_option('amai_blog_approval_mode');

        // AI provider keys and models
        global $wpdb;
        $wpdb->query(
            "DELETE FROM {$wpdb->options} WHERE option_name LIKE 'amai_api_key_%' OR option_name LIKE 'amai_model_%' OR option_name LIKE 'amai_model_label_%'"
        );

        // Transients
        $wpdb->query(
            "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_amai_%' OR option_name LIKE '_transient_timeout_amai_%'"
        );

        // Note: does NOT touch wp_amai_* tables, Facebook/Instagram/LinkedIn connection tables,
        // wp_amai_scheduled_posts, or Core tenant data. Data stays, credentials reset.

        wp_send_json_success(['message' => 'Plugin reset. Reloading...', 'reload' => true]);
    }

    // --------------------------------------------------
    // AJAX — save blog approval mode
    // --------------------------------------------------
    public function handleSaveBlogApprovalMode(): void
    {
        if (!current_user_can('manage_automarketer_ai')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }

        check_ajax_referer('amai_save_blog_approval_mode', '_ajax_nonce');

        $mode = sanitize_key((string) ($_POST['mode'] ?? ''));
        if (!in_array($mode, ['review', 'autopilot'], true)) {
            wp_send_json_error(['message' => 'Invalid mode.']);
            return;
        }

        update_option('amai_blog_approval_mode', $mode, false);
        wp_send_json_success(['mode' => $mode]);
    }

    public function handleSaveBlogImageSource(): void
    {
        if (!current_user_can('manage_automarketer_ai')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }
        check_ajax_referer('amai_save_blog_image_source', '_ajax_nonce');
        $source = sanitize_key((string) ($_POST['source'] ?? ''));
        if (!in_array($source, ['unsplash', 'ai'], true)) {
            wp_send_json_error(['message' => 'Invalid source.']);
            return;
        }
        update_option('amai_blog_image_source', $source, false);
        wp_send_json_success(['source' => $source]);
    }

    // --------------------------------------------------
    // AJAX: save WordPress/WooCommerce comments enabled toggle.
    // --------------------------------------------------
    public function handleSaveWpCommentsEnabled(): void
    {
        $enabled = (int) ($_POST['enabled'] ?? 0);
        update_option('amai_wp_comments_enabled', $enabled === 1, false);
        wp_send_json_success(['enabled' => $enabled === 1]);
    }

    // --------------------------------------------------
    // System info helpers (called from template)
    // --------------------------------------------------
    public static function getSystemInfo(): array
    {
        global $wpdb;

        // Active modules
        $activeModules = (array) get_option('amai_active_modules', []);
        $activeAi      = array_filter($activeModules, static fn($m) => str_starts_with((string) $m, 'ai-'));
        $activeImg     = array_filter($activeModules, static fn($m) => str_starts_with((string) $m, 'img-'));

        // Channels -- data-driven, only connected ones surfaced
        $channels = [];
        $fbConnection = FacebookAuth::getConnection();
        if ($fbConnection !== null) {
            $fbName = ($fbConnection->page_name ?? '') !== '' ? ' - ' . $fbConnection->page_name : '';
            $channels['Facebook'] = 'Connected' . $fbName;
        }
        $igId = FacebookAuth::getInstagramAccountId();
        if ($igId !== null) {
            $channels['Instagram'] = 'Connected';
        }
        $tkConnection = TikTokAuth::getConnection();
        if ($tkConnection !== null) {
            $tkName = ($tkConnection['username'] ?? '') !== '' ? ' - @' . $tkConnection['username'] : '';
            $channels['TikTok'] = 'Connected' . $tkName;
        }
        $xConnection = XAuth::getConnection();
        if ($xConnection !== null) {
            $xName = ($xConnection['username'] ?? '') !== '' ? ' - @' . $xConnection['username'] : '';
            $channels['X'] = 'Connected';
        }
        $liRow = $wpdb->get_row("SELECT organization_name, status FROM {$wpdb->prefix}amai_linkedin_connection WHERE status = 'active' LIMIT 1");
        if ($liRow !== null) {
            $liName = ($liRow->organization_name ?? '') !== '' ? ' - ' . $liRow->organization_name : '';
            $channels['LinkedIn'] = 'Connected' . $liName;
        }

        // Scheduled posts counts
        $totalPosts   = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}amai_scheduled_posts");
        $postedCount  = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}amai_scheduled_posts WHERE status = 'posted'");
        $pendingCount = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}amai_scheduled_posts WHERE status = 'draft'");

        return [
            'php_version'    => PHP_VERSION,
            'wp_version'     => get_bloginfo('version'),
            'plugin_version' => defined('AMAI_VERSION') ? AMAI_VERSION : 'unknown',
            'tenant_id'      => (string) get_option('amai_tenant_id', 'not set'),
            'plan'           => (function() { $map = ['starter' => 'Starter', 'starter_pro' => 'Starter Pro', 'unlimited_ai' => 'Unlimited AI', 'agency' => 'Agency', 'free' => 'Free']; $raw = (string) get_option('automarketer_ai_plan_id', 'free'); return $map[$raw] ?? ucwords(str_replace('_', ' ', $raw)); })(),
            'license_status' => (string) get_option('automarketer_ai_license_status', 'inactive'),
            'active_ai'      => !empty($activeAi) ? implode(', ', array_map(fn($v) => ['ai-claude'=>'Claude','ai-openai'=>'OpenAI','ai-gemini'=>'Gemini','ai-grok'=>'Grok','ai-mistral'=>'Mistral'][$v] ?? $v, array_values($activeAi))) : 'None',
            'active_img'     => !empty($activeImg) ? implode(', ', array_map(fn($v) => ['img-imagen'=>'Imagen','img-dalle'=>'DALL-E','img-aurora'=>'Aurora'][$v] ?? $v, array_values($activeImg))) : 'None',
            'channels'       => $channels,
            'total_posts'    => $totalPosts,
            'posted_count'   => $postedCount,
            'pending_count'  => $pendingCount,
            'admin_email'    => (string) get_option('admin_email', ''),
            'site_url'       => (string) get_bloginfo('url'),
            'wp_debug'       => defined('WP_DEBUG') && WP_DEBUG ? 'On' : 'Off',
        ];
    }

    // --------------------------------------------------
    // AJAX -- save abandoned cart nudge delay thresholds
    // --------------------------------------------------
    public function handleSaveRecoveryDiscount(): void
    {
        if (!current_user_can('manage_automarketer_ai')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }
        check_ajax_referer('amai_settings_actions', '_ajax_nonce');
        $enabled = isset($_POST['enabled']) && $_POST['enabled'] === '1' ? '1' : '0';
        $pct_raw = preg_replace('/[^0-9]/', '', (string) ($_POST['discount_pct'] ?? ''));
        $days_raw = preg_replace('/[^0-9]/', '', (string) ($_POST['discount_days'] ?? ''));
        if ($pct_raw === '' || (int) $pct_raw < 1 || (int) $pct_raw > 100) {
            wp_send_json_error(['message' => 'Discount must be a whole number between 1 and 100.']);
            return;
        }
        if ($days_raw === '' || (int) $days_raw < 1 || (int) $days_raw > 30) {
            wp_send_json_error(['message' => 'Expiry must be a whole number between 1 and 30 days.']);
            return;
        }
        update_option('amai_cart_recovery_discount_enabled', $enabled, false);
        update_option('amai_cart_recovery_discount_pct', $pct_raw, false);
        update_option('amai_cart_recovery_discount_days', $days_raw, false);
        wp_send_json_success(['message' => 'Recovery discount settings saved.']);
    }

        public function handleSaveNudgeThresholds(): void
    {
        if (!current_user_can('manage_automarketer_ai')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }

        check_ajax_referer('amai_settings_actions', '_ajax_nonce');

        $fields = [
            'amai_cart_nudge_1_hours' => (string) ($_POST['nudge_1_hours'] ?? ''),
            'amai_cart_nudge_2_hours' => (string) ($_POST['nudge_2_hours'] ?? ''),
            'amai_cart_nudge_3_hours' => (string) ($_POST['nudge_3_hours'] ?? ''),
        ];

        foreach ($fields as $key => $raw) {
            $clean = preg_replace('/[^0-9]/', '', $raw);
            if ($clean === '' || (int) $clean < 1 || (int) $clean > 720) {
                wp_send_json_error(['message' => 'Each delay must be a whole number between 1 and 720 hours.']);
                return;
            }
            update_option($key, $clean, false);
        }

        wp_send_json_success(['message' => 'Reminder delays saved.']);
    }

    public function handleCompetitorAdd(): void
    {
        if (!current_user_can('manage_automarketer_ai')) { wp_send_json_error(['message' => 'Unauthorized']); return; }
        check_ajax_referer('amai_settings_actions', '_ajax_nonce');
        $page_url = esc_url_raw(trim((string) ($_POST['page_url'] ?? '')));
        $result   = CompetitorAnalyzer::addPage($page_url);
        if ($result === true) {
            wp_send_json_success(['message' => 'Page added. Posts will be fetched on the next weekly run.']);
        } else {
            wp_send_json_error(['message' => (string) $result]);
        }
    }

    public function handleCompetitorRemove(): void
    {
        if (!current_user_can('manage_automarketer_ai')) { wp_send_json_error(['message' => 'Unauthorized']); return; }
        check_ajax_referer('amai_settings_actions', '_ajax_nonce');
        $competitor_id = (int) ($_POST['competitor_id'] ?? 0);
        if ($competitor_id <= 0) { wp_send_json_error(['message' => 'Invalid ID.']); return; }
        CompetitorAnalyzer::removePage($competitor_id);
        wp_send_json_success(['message' => 'Removed.']);
    }

    // --------------------------------------------------
    // --------------------------------------------------
    // Helpers -- release management
    // --------------------------------------------------

    private const RELEASES_DIR  = '/home/ploi/releases';
    private const RELEASES_KEEP = 4;

    // Returns sorted array of full paths to release ZIPs, oldest first.
    private static function getReleaseFiles(): array
    {
        $files = glob(self::RELEASES_DIR . '/automarketer-ai-v*.zip');
        if ($files === false) {
            return [];
        }
        usort($files, function (string $a, string $b): int {
            preg_match('/v(\d+\.\d+\.\d+)\.zip$/', $a, $ma);
            preg_match('/v(\d+\.\d+\.\d+)\.zip$/', $b, $mb);
            return version_compare($mb[1] ?? '0', $ma[1] ?? '0');
        });
        return $files;
    }

    // Returns version string from ZIP filename, or empty string on failure.
    private static function versionFromFile(string $file): string
    {
        $base = basename($file);
        if (!preg_match('/^automarketer-ai-v(\d+\.\d+\.\d+)\.zip$/', $base, $m)) {
            return '';
        }
        return $m[1];
    }

    // Returns true if $candidate is strictly greater than $current (semver).
    private static function isVersionGreater(string $candidate, string $current): bool
    {
        return version_compare($candidate, $current, '>');
    }

    // Returns the current-release ZIP filename from wp_options, falling back to latest.
    private static function getCurrentReleaseFile(): string
    {
        $stored = (string) get_option('amai_current_release', '');
        if ($stored !== '' && is_file(self::RELEASES_DIR . '/' . $stored)) {
            return $stored;
        }
        // Fall back to latest
        $files = self::getReleaseFiles();
        if (empty($files)) {
            return '';
        }
        return basename((string) reset($files));
    }

    // Prunes oldest releases keeping at most RELEASES_KEEP total.
    // Never deletes the current-release file.
    private static function pruneReleases(): void
    {
        $files   = self::getReleaseFiles();
        $current = self::getCurrentReleaseFile();
        while (count($files) > self::RELEASES_KEEP) {
            $oldest = array_pop($files);
            if ($oldest === null) {
                break;
            }
            if (basename($oldest) === $current) {
                continue;
            }
            @unlink($oldest);
        }
    }

    // --------------------------------------------------
    // AJAX: Create a versioned release ZIP of the plugin.
    // --------------------------------------------------
    public function handleGetHandoffsSince(): void
    {
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }
        check_ajax_referer('amai_get_handoffs_since', '_ajax_nonce');
        $since = sanitize_text_field(wp_unslash((string) ($_POST['since'] ?? '')));
        global $wpdb;
        $rows = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT option_name, option_value FROM {$wpdb->options} WHERE option_name LIKE %s ORDER BY option_name ASC",
                'amai_handoff_%'
            ),
            ARRAY_A
        );
        $entries = [];
        foreach ($rows as $row) {
            $date = substr((string) $row['option_name'], strlen('amai_handoff_'));
            if ($since === '' || $date > $since) {
                $entries[] = (string) $row['option_value'];
            }
        }
        wp_send_json_success(['entries' => $entries]);
    }

    public function handleCreateRelease(): void
    {
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }
        check_ajax_referer('amai_create_release', '_ajax_nonce');

        $version = sanitize_text_field(wp_unslash((string) ($_POST['version'] ?? '')));
        if (!preg_match('/^\d+\.\d+\.\d+$/', $version)) {
            wp_send_json_error(['message' => 'Invalid version format. Use x.y.z']);
            return;
        }

        // Forward-only enforcement
        $files   = self::getReleaseFiles();
        if (!empty($files)) {
            $latestFile = (string) reset($files);
            $latestVer  = self::versionFromFile($latestFile);
            if ($latestVer !== '' && !self::isVersionGreater($version, $latestVer)) {
                wp_send_json_error(['message' => 'Version ' . $version . ' must be greater than current ' . $latestVer . '.']);
                return;
            }
        }

        if (!is_dir(self::RELEASES_DIR)) {
            wp_send_json_error(['message' => 'Releases directory not found.']);
            return;
        }

        // Auto-bump version in plugin bootstrap before zipping
        $bootstrapFile = rtrim((string) AMAI_PLUGIN_DIR, '/\\') . '/automarketer-ai.php';
        if (is_file($bootstrapFile)) {
            $src = file_get_contents($bootstrapFile);
            if ($src !== false) {
                $src = preg_replace('/(\/\/ Version: )\d+\.\d+\.\d+/', '${1}' . $version, $src, 1);
                $src = preg_replace("/define\('AMAI_VERSION', '\d+\.\d+\.\d+'\)/", "define('AMAI_VERSION', '" . $version . "')", $src, 1);
                file_put_contents($bootstrapFile, $src);
                if (function_exists('opcache_invalidate')) {
                    opcache_invalidate($bootstrapFile, true);
                }
            }
        }
        $zipName = 'automarketer-ai-v' . $version . '.zip';
        $zipFinal = self::RELEASES_DIR . '/' . $zipName;

        if (is_file($zipFinal)) {
            wp_send_json_error(['message' => 'Release ' . $version . ' already exists.']);
            return;
        }

        $pluginDir = rtrim((string) AMAI_PLUGIN_DIR, '/\\');
        $excluded  = [
            'AUDIT_CHECKLIST.md',
            'SESSION_NOTES.md',
            'CLAUDE.md',
            'CONTRIBUTING.md',
            'composer.json',
            'composer.lock',
        ];

        $zip    = new \ZipArchive();
        $opened = $zip->open($zipFinal, \ZipArchive::CREATE | \ZipArchive::EXCL);
        if ($opened !== true) {
            wp_send_json_error(['message' => 'Could not create ZIP. Code: ' . $opened]);
            return;
        }

        $iterator = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($pluginDir, \RecursiveDirectoryIterator::SKIP_DOTS),
            \RecursiveIteratorIterator::SELF_FIRST
        );
        $errors = 0;
        foreach ($iterator as $item) {
            $realItem = realpath((string) $item);
            if ($realItem === false) {
                continue;
            }
            $basename = basename($realItem);
            if (in_array($basename, $excluded, true) && dirname($realItem) === $pluginDir) {
                continue;
            }
            if (dirname($realItem) === $pluginDir && str_starts_with($basename, 'patch-') && str_ends_with($basename, '.js')) {
                continue;
            }
            if (dirname($realItem) === $pluginDir && str_starts_with($basename, 'patch-') && str_ends_with($basename, '.py')) {
                continue;
            }
            $relative = 'automarketer-ai/' . ltrim(substr($realItem, strlen($pluginDir)), '/\\');
            if ($item->isDir()) {
                $zip->addEmptyDir($relative);
            } else {
                if (!$zip->addFile($realItem, $relative)) {
                    $errors++;
                }
            }
        }
        $zip->close();

        if ($errors > 0) {
            @unlink($zipFinal);
            wp_send_json_error(['message' => 'ZIP build failed - ' . $errors . ' file(s) could not be added.']);
            return;
        }

        // Save changelog if provided
        $changelog = sanitize_textarea_field(wp_unslash((string) ($_POST['changelog'] ?? '')));
        if ($changelog !== '') {
            update_option('amai_release_changelog_' . $version, $changelog, false);
        }

        // Set as current release
        update_option('amai_current_release', $zipName, false);
        update_option('amai_last_release_date', gmdate('Y-m-d H:i:s'), false);

        // Prune old releases
        self::pruneReleases();

        $size   = filesize($zipFinal);
        $sizeMb = $size !== false ? round($size / 1048576, 2) : 0;


        wp_send_json_success([
            'message' => $zipName . ' created (' . $sizeMb . ' MB) and set as current.',
            'version' => $version,
            'zip'     => $zipName,
        ]);
    }

    // --------------------------------------------------
    // AJAX: Set a release as the current download.
    // --------------------------------------------------
    public function handleMakeCurrentRelease(): void
    {
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }
        check_ajax_referer('amai_make_current_release', '_ajax_nonce');

        $zip = sanitize_file_name(wp_unslash((string) ($_POST['zip'] ?? '')));
        if (!preg_match('/^automarketer-ai-v\d+\.\d+\.\d+\.zip$/', $zip)) {
            wp_send_json_error(['message' => 'Invalid ZIP name.']);
            return;
        }
        $fullPath = self::RELEASES_DIR . '/' . $zip;
        if (!is_file($fullPath)) {
            wp_send_json_error(['message' => 'Release file not found.']);
            return;
        }

        update_option('amai_current_release', $zip, false);
        error_log('[AMAI] Current release set to: ' . $zip . ' by user_id=' . get_current_user_id());

        update_option('amai_last_release_date', gmdate('Y-m-d H:i:s'));
        wp_send_json_success(['message' => $zip . ' is now the current release.', 'zip' => $zip]);
    }

    // --------------------------------------------------
    // AJAX: Delete a release ZIP.
    // --------------------------------------------------
    public function handleDeleteRelease(): void
    {
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }
        check_ajax_referer('amai_delete_release', '_ajax_nonce');

        $zip = sanitize_file_name(wp_unslash((string) ($_POST['zip'] ?? '')));
        if (!preg_match('/^automarketer-ai-v\d+\.\d+\.\d+\.zip$/', $zip)) {
            wp_send_json_error(['message' => 'Invalid ZIP name.']);
            return;
        }

        $current = self::getCurrentReleaseFile();
        if ($zip === $current) {
            wp_send_json_error(['message' => 'Cannot delete the current release. Set another version as current first.']);
            return;
        }

        $fullPath = self::RELEASES_DIR . '/' . $zip;
        $realPath = realpath($fullPath);
        if ($realPath === false || !str_starts_with($realPath, self::RELEASES_DIR . '/')) {
            wp_send_json_error(['message' => 'Invalid path.']);
            return;
        }
        if (!is_file($realPath)) {
            wp_send_json_error(['message' => 'File not found.']);
            return;
        }

        if (!unlink($realPath)) {
            wp_send_json_error(['message' => 'Could not delete file.']);
            return;
        }

        // Clean up changelog option
        preg_match('/^automarketer-ai-v(\d+\.\d+\.\d+)\.zip$/', $zip, $m);
        if (!empty($m[1])) {
            delete_option('amai_release_changelog_' . $m[1]);
        }

        error_log('[AMAI] Release deleted: ' . $zip . ' by user_id=' . get_current_user_id());
        wp_send_json_success(['message' => $zip . ' deleted.', 'zip' => $zip]);
    }

    // --------------------------------------------------
    // AJAX: Save or update changelog for a release version.
    // --------------------------------------------------
    public function handleSaveReleaseChangelog(): void
    {
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }
        check_ajax_referer('amai_save_release_changelog', '_ajax_nonce');

        $version   = sanitize_text_field(wp_unslash((string) ($_POST['version'] ?? '')));
        $changelog = sanitize_textarea_field(wp_unslash((string) ($_POST['changelog'] ?? '')));

        if (!preg_match('/^\d+\.\d+\.\d+$/', $version)) {
            wp_send_json_error(['message' => 'Invalid version.']);
            return;
        }

        update_option('amai_release_changelog_' . $version, $changelog, false);
        wp_send_json_success(['message' => 'Changelog saved.']);
    }

    // --------------------------------------------------
    // AJAX -- generate legal page content via active AI provider
    // --------------------------------------------------
    public function handleGenerateLegalContent(): void
    {
        if (!current_user_can('manage_automarketer_ai')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }
        check_ajax_referer('amai_generate_legal_content', '_ajax_nonce');
        $page = sanitize_key((string) ($_POST['page'] ?? ''));
        $tone = sanitize_key((string) ($_POST['tone'] ?? 'professional'));
        if (!in_array($page, ['privacy', 'terms'], true)) {
            wp_send_json_error(['message' => 'Invalid page.']);
            return;
        }
        if (!in_array($tone, ['professional', 'plain', 'friendly'], true)) {
            $tone = 'professional';
        }
        $active_modules = (array) get_option('amai_active_modules', []);
        $provider_id    = '';
        foreach ($active_modules as $mod) {
            if (is_string($mod) && str_starts_with($mod, 'ai-')) {
                $provider_id = $mod;
                break;
            }
        }
        if ($provider_id === '') {
            wp_send_json_error(['message' => 'No AI provider active. Activate one on the Modules page first.']);
            return;
        }
        $api_key = KeyVault::decrypt((string) get_option('amai_api_key_' . $provider_id, ''));
        $model   = (string) get_option('amai_model_' . $provider_id, '');
        if ($api_key === '' || $model === '') {
            wp_send_json_error(['message' => 'AI provider not fully configured.']);
            return;
        }
        $site_name = get_bloginfo('name');
        $site_url  = get_bloginfo('url');
        $tone_instruction = match ($tone) {
            'plain'     => 'Use plain, simple English. Short sentences. No legal jargon.',
            'friendly'  => 'Use a warm, approachable tone. Still professional, but conversational.',
            default     => 'Use formal, professional legal language appropriate for a business website.',
        };
        if ($page === 'privacy') {
            $prompt = "Write a complete Privacy Policy for a small business website called {$site_name} ({$site_url}). "
                . 'The business uses AutoMarketer AI, a WordPress plugin, to manage social media content. '
                . 'Cover: what data is collected (name, email, contact form submissions, analytics), how it is used, '
                . 'cookies and third-party services (WordPress, Stripe for payments, social media platforms), '
                . 'data retention, user rights, and a contact email for privacy requests. '
                . "Tone: {$tone_instruction} "
                . 'Format the output as clean HTML using <h2>, <p>, and <ul> tags only. No inline styles. No markdown. '
                . 'Do not include a <html>, <head>, or <body> tag. Start directly with the first <h2>.';
        } else {
            $prompt = "Write a complete Terms of Service for a small business website called {$site_name} ({$site_url}). "
                . 'Cover: acceptance of terms, description of services offered, payment and refund policy '
                . '(cancellations are immediate with a prorated refund for unused time, access ends immediately, Stripe handles billing), '
                . 'prohibited uses, limitation of liability, governing law, and how to contact the business. '
                . "Tone: {$tone_instruction} "
                . 'Format the output as clean HTML using <h2>, <p>, and <ul> tags only. No inline styles. No markdown. '
                . 'Do not include a <html>, <head>, or <body> tag. Start directly with the first <h2>.';
        }
        $module_dir = AMAI_PLUGIN_DIR . '/modules';
        $module     = null;
        if (is_dir($module_dir)) {
            $iterator = new \RecursiveIteratorIterator(
                new \RecursiveDirectoryIterator($module_dir)
            );
            foreach ($iterator as $file) {
                if ($file->isDir() || $file->getExtension() !== 'php') {
                    continue;
                }
                $basename = $file->getBasename('.php');
                if ($basename !== $provider_id) {
                    continue;
                }
                require_once $file->getPathname();
                $clean = str_replace(['ai-', '-'], ['', ''], $basename);
                $class = '\\AutoMarketerAI\\Modules\\' . ucfirst($clean);
                if (class_exists($class)) {
                    $module = new $class();
                }
                break;
            }
        }
        // Module loaded but we call the API directly here to use a longer timeout.
        // generate_raw() hardcodes 15s which is too short for full legal documents.
        $provider_prefix = '';
        if ($module !== null && method_exists($module, 'providerPrefix')) {
            $provider_prefix = $module->providerPrefix();
        }
        // Build the API request based on provider type.
        // All active AI providers supported here are OpenAI-compatible or Anthropic.
        // For simplicity we call Anthropic format for claude, OpenAI format for others.
        try {
            $result = '';
            if (str_starts_with($provider_id, 'ai-claude')) {
                $response = wp_remote_post('https://api.anthropic.com/v1/messages', [
                    'headers' => [
                        'x-api-key'         => $api_key,
                        'anthropic-version' => '2023-06-01',
                        'Content-Type'      => 'application/json',
                    ],
                    'body'    => (string) json_encode([
                        'model'      => $model,
                        'max_tokens' => 4096,
                        'messages'   => [['role' => 'user', 'content' => $prompt]],
                    ]),
                    'timeout' => 120,
                ]);
                if (!is_wp_error($response) && (int) wp_remote_retrieve_response_code($response) === 200) {
                    $body   = json_decode(wp_remote_retrieve_body($response), true);
                    $result = trim((string) ($body['content'][0]['text'] ?? ''));
                }
            } elseif ($module !== null && method_exists($module, 'generate_raw')) {
                // For non-Claude providers fall back to generate_raw with high token count.
                $result = trim($module->generate_raw($prompt, $api_key, $model, 3000));
            }
            if ($result === '') {
                wp_send_json_error(['message' => 'AI returned an empty response. Try again.']);
                return;
            }
            wp_send_json_success(['content' => $result]);
        } catch (\Throwable $e) {
            wp_send_json_error(['message' => 'AI generation failed. Try again.']);
        }
    }

    // --------------------------------------------------
    // AJAX -- save legal page content
    // --------------------------------------------------
    public function handleSaveLegalContent(): void
    {
        if (!current_user_can('manage_automarketer_ai')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }
        check_ajax_referer('amai_settings_actions', '_ajax_nonce');
        $page    = sanitize_key((string) ($_POST['page'] ?? ''));
        $content = wp_kses_post(wp_unslash((string) ($_POST['content'] ?? '')));
        if (!in_array($page, ['privacy', 'terms'], true)) {
            wp_send_json_error(['message' => 'Invalid page.']);
            return;
        }
        update_option('amai_legal_' . $page, $content, false);
        wp_send_json_success(['message' => 'Saved.']);
    }

    // --------------------------------------------------
    // AJAX -- save legal page URLs
    // --------------------------------------------------
    public function handleSaveLegalUrls(): void
    {
        if (!current_user_can('manage_automarketer_ai')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }
        check_ajax_referer('amai_settings_actions', '_ajax_nonce');
        $privacy_url = esc_url_raw(wp_unslash((string) ($_POST['privacy_url'] ?? '')));
        $terms_url   = esc_url_raw(wp_unslash((string) ($_POST['terms_url'] ?? '')));
        update_option('amai_legal_privacy_url', $privacy_url, false);
        update_option('amai_legal_terms_url',   $terms_url,   false);
        wp_send_json_success(['message' => 'Saved.']);
    }

    // --------------------------------------------------
    // AJAX -- save SMS provider credentials via Modules page modal
    // --------------------------------------------------
    public function handleSaveSmsProvider(): void
    {
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }
        check_ajax_referer('amai_provider_actions', '_ajax_nonce');
        $provider_id = sanitize_text_field((string) ($_POST['provider_id'] ?? ''));
        if (!in_array($provider_id, ['sms-twilio', 'sms-plivo', 'sms-vonage', 'sms-sinch'], true)) {
            wp_send_json_error(['message' => 'Invalid SMS provider.']);
            return;
        }
        // Validate and save credentials for the selected provider
        if ($provider_id === 'sms-twilio') {
            $sid   = sanitize_text_field((string) ($_POST['account_sid'] ?? ''));
            $token = sanitize_text_field((string) ($_POST['auth_token']  ?? ''));
            $from  = sanitize_text_field((string) ($_POST['from_number'] ?? ''));
            if ($sid === '' || $token === '' || $from === '') {
                wp_send_json_error(['message' => 'All three fields are required.']);
                return;
            }
            // Validate against Twilio Accounts API
            $response = wp_remote_get(
                'https://api.twilio.com/2010-04-01/Accounts/' . rawurlencode($sid) . '.json',
                ['timeout' => 10, 'headers' => ['Authorization' => 'Basic ' . base64_encode($sid . ':' . $token)]]
            );
            if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
                wp_send_json_error(['message' => 'Invalid Twilio credentials - please check your Account SID and Auth Token.']);
                return;
            }
            update_option('amai_twilio_account_sid', $sid,   false);
            update_option('amai_twilio_auth_token',  $token, false);
            update_option('amai_twilio_from_number', $from,  false);
        } elseif ($provider_id === 'sms-plivo') {
            $auth_id = sanitize_text_field((string) ($_POST['auth_id']     ?? ''));
            $token   = sanitize_text_field((string) ($_POST['auth_token']  ?? ''));
            $from    = sanitize_text_field((string) ($_POST['from_number'] ?? ''));
            if ($auth_id === '' || $token === '' || $from === '') {
                wp_send_json_error(['message' => 'All three fields are required.']);
                return;
            }
            // Validate against Plivo Account API
            $response = wp_remote_get(
                'https://api.plivo.com/v1/Account/' . rawurlencode($auth_id) . '/',
                ['timeout' => 10, 'headers' => ['Authorization' => 'Basic ' . base64_encode($auth_id . ':' . $token)]]
            );
            if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
                wp_send_json_error(['message' => 'Invalid Plivo credentials - please check your Auth ID and Auth Token.']);
                return;
            }
            update_option('amai_plivo_auth_id',      $auth_id, false);
            update_option('amai_plivo_auth_token',   $token,   false);
            update_option('amai_plivo_from_number',  $from,    false);
        } elseif ($provider_id === 'sms-vonage') {
            $key    = sanitize_text_field((string) ($_POST['api_key']     ?? ''));
            $secret = sanitize_text_field((string) ($_POST['api_secret']  ?? ''));
            $from   = sanitize_text_field((string) ($_POST['from_number'] ?? ''));
            if ($key === '' || $secret === '' || $from === '') {
                wp_send_json_error(['message' => 'All three fields are required.']);
                return;
            }
            // Validate against Vonage balance endpoint
            $response = wp_remote_get(
                'https://rest.nexmo.com/account/get-balance?api_key=' . rawurlencode($key) . '&api_secret=' . rawurlencode($secret),
                ['timeout' => 10]
            );
            $body = is_wp_error($response) ? '' : wp_remote_retrieve_body($response);
            $data = json_decode($body, true);
            if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200 || empty($data['value'])) {
                wp_send_json_error(['message' => 'Invalid Vonage credentials - please check your API Key and API Secret.']);
                return;
            }
            update_option('amai_vonage_api_key',     $key,    false);
            update_option('amai_vonage_api_secret',  $secret, false);
            update_option('amai_vonage_from_number', $from,   false);
        } elseif ($provider_id === 'sms-sinch') {
            $plan_id = sanitize_text_field((string) ($_POST['service_plan_id'] ?? ''));
            $token   = sanitize_text_field((string) ($_POST['api_token']       ?? ''));
            $from    = sanitize_text_field((string) ($_POST['from_number']     ?? ''));
            if ($plan_id === '' || $token === '' || $from === '') {
                wp_send_json_error(['message' => 'All three fields are required.']);
                return;
            }
            // Validate against Sinch batches endpoint
            $response = wp_remote_get(
                'https://us.sms.api.sinch.com/xms/v1/' . rawurlencode($plan_id) . '/batches?page_size=1',
                ['timeout' => 10, 'headers' => ['Authorization' => 'Bearer ' . $token]]
            );
            if (is_wp_error($response) || (int) wp_remote_retrieve_response_code($response) !== 200) {
                wp_send_json_error(['message' => 'Invalid Sinch credentials - please check your Service Plan ID and API Token.']);
                return;
            }
            update_option('amai_sinch_service_plan_id', $plan_id, false);
            update_option('amai_sinch_api_token',       $token,   false);
            update_option('amai_sinch_from_number',     $from,    false);
        }
        // Deactivate any other SMS provider, then activate this one
        $active_modules = (array) get_option('amai_active_modules', []);
        $active_modules = array_values(array_filter(
            $active_modules,
            static fn($m) => !in_array($m, ['sms-twilio', 'sms-plivo', 'sms-vonage', 'sms-sinch'], true)
        ));
        $active_modules[] = $provider_id;
        update_option('amai_active_modules', array_unique(array_values($active_modules)));
        wp_send_json_success(['message' => 'Connected successfully.']);
    }
    // --------------------------------------------------
    // AJAX -- connect WooCommerce store provider
    // --------------------------------------------------
    public function handleSaveStoreProvider(): void
    {
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }
        check_ajax_referer('amai_provider_actions', '_ajax_nonce');

        $provider_id = sanitize_text_field((string) ($_POST['provider_id'] ?? ''));
        if ($provider_id !== 'store-woocommerce') {
            wp_send_json_error(['message' => 'Invalid store provider.']);
            return;
        }

        if (!class_exists('WooCommerce')) {
            wp_send_json_error(['message' => 'WooCommerce must be installed and active.']);
            return;
        }

        // Activate store-woocommerce and store-integration modules
        $active_modules = (array) get_option('amai_active_modules', []);

        if (!in_array('store-woocommerce', $active_modules, true)) {
            $active_modules[] = 'store-woocommerce';
        }
        if (!in_array('store-integration', $active_modules, true)) {
            $active_modules[] = 'store-integration';
        }

        update_option('amai_active_modules', array_unique(array_values($active_modules)));

        wp_send_json_success(['message' => 'WooCommerce connected successfully.']);
    }


    // --------------------------------------------------
    // AJAX -- save SMS provider credentials
    // --------------------------------------------------
    public function handleSaveTwilioCredentials(): void
    {
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }
        $active_modules = (array) get_option('amai_active_modules', []);
        $provider = '';
        foreach (['sms-twilio', 'sms-plivo', 'sms-vonage', 'sms-sinch'] as $p) {
            if (in_array($p, $active_modules, true)) {
                $provider = $p;
                break;
            }
        }
        if ($provider === '') {
            wp_send_json_error(['message' => 'No SMS provider is active.']);
            return;
        }
        if ($provider === 'sms-twilio') {
            $sid   = sanitize_text_field((string) ($_POST['account_sid'] ?? ''));
            $token = sanitize_text_field((string) ($_POST['auth_token'] ?? ''));
            $from  = sanitize_text_field((string) ($_POST['from_number'] ?? ''));
            if ($sid === '' || $token === '' || $from === '') {
                wp_send_json_error(['message' => 'All three fields are required.']);
                return;
            }
            update_option('amai_twilio_account_sid', $sid, false);
            update_option('amai_twilio_auth_token', $token, false);
            update_option('amai_twilio_from_number', $from, false);
        } elseif ($provider === 'sms-plivo') {
            $auth_id = sanitize_text_field((string) ($_POST['auth_id'] ?? ''));
            $token   = sanitize_text_field((string) ($_POST['auth_token'] ?? ''));
            $from    = sanitize_text_field((string) ($_POST['from_number'] ?? ''));
            if ($auth_id === '' || $token === '' || $from === '') {
                wp_send_json_error(['message' => 'All three fields are required.']);
                return;
            }
            update_option('amai_plivo_auth_id', $auth_id, false);
            update_option('amai_plivo_auth_token', $token, false);
            update_option('amai_plivo_from_number', $from, false);
        } elseif ($provider === 'sms-vonage') {
            $key    = sanitize_text_field((string) ($_POST['api_key'] ?? ''));
            $secret = sanitize_text_field((string) ($_POST['api_secret'] ?? ''));
            $from   = sanitize_text_field((string) ($_POST['from_number'] ?? ''));
            if ($key === '' || $secret === '' || $from === '') {
                wp_send_json_error(['message' => 'All three fields are required.']);
                return;
            }
            update_option('amai_vonage_api_key', $key, false);
            update_option('amai_vonage_api_secret', $secret, false);
            update_option('amai_vonage_from_number', $from, false);
        }
        wp_send_json_success(['message' => 'Credentials saved.']);
    }

    // AJAX -- downgrade to free plan
    // --------------------------------------------------
    public function handleDowngradeToFree(): void
    {
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }
        check_ajax_referer('amai_settings_actions', '_ajax_nonce');
        $confirm = sanitize_text_field((string) ($_POST['confirm'] ?? ''));
        if ($confirm !== 'Switch to Free') {
            wp_send_json_error(['message' => 'Confirmation text did not match.']);
            return;
        }
        $current_plan = (string) get_option('automarketer_ai_plan_id', 'free');
        if ($current_plan === 'free') {
            wp_send_json_error(['message' => 'You are already on the free plan.']);
            return;
        }
        // Cancel Stripe subscription if one exists
        $sub_id = (string) get_option('automarketer_ai_stripe_sub_id', '');
        if ($sub_id !== '') {
            wp_send_json_error(['message' => 'You have an active paid subscription. Please cancel it from the Plans page first, then come back here.']);
            return;
        }
        // Downgrade plan state
        update_option('automarketer_ai_plan_id', 'free', false);
        update_option('automarketer_ai_license_status', 'free', false);
        delete_option('automarketer_ai_addon_seats');
        // Reconcile modules -- strips anything not allowed on free
        \AutoMarketerAI\PlanEnforcer::reconcile('free');
        wp_send_json_success(['message' => 'Done. Your account is now on the free plan.', 'reload' => true]);
    }

    public function renderInstallOnboarding(): void
    {
        require_once AMAI_PLUGIN_DIR . '/templates/install-onboarding.php';
    }

    // --------------------------------------------------
    // AJAX: save comment reply mode and tone per channel.
    // --------------------------------------------------
    public function handleSaveCommentSettings(): void
    {
        check_ajax_referer('amai_comment_actions', 'nonce');
        if (!current_user_can('manage_automarketer_ai')) {
            wp_send_json_error(['message' => 'Unauthorized.'], 403);
        }
        $raw_modes = sanitize_text_field(wp_unslash((string) ($_POST['modes'] ?? '')));
        $raw_tones = sanitize_text_field(wp_unslash((string) ($_POST['tones'] ?? '')));
        $modes = json_decode($raw_modes, true);
        $tones = json_decode($raw_tones, true);
        if (!is_array($modes) || !is_array($tones)) {
            wp_send_json_error(['message' => 'Invalid data.']);
        }
        $valid_modes    = ['manual', 'ai_assisted', 'autopilot'];
        $valid_tones    = ['friendly', 'warm', 'professional', 'playful', 'formal'];
        $valid_channels = ['facebook', 'instagram', 'x', 'wordpress', 'woocommerce'];
        foreach ($valid_channels as $ch) {
            if (isset($modes[$ch]) && in_array($modes[$ch], $valid_modes, true)) {
                update_option('amai_comment_reply_mode_' . $ch, $modes[$ch]);
            }
            if (isset($tones[$ch]) && in_array($tones[$ch], $valid_tones, true)) {
                update_option('amai_comment_reply_tone_' . $ch, $tones[$ch]);
            }
        }
        wp_send_json_success(['message' => 'Saved.']);
    }

    // --------------------------------------------------
    // AJAX: generate an AI draft for a comment on demand.
    // Used in manual mode when the client clicks Generate AI Draft.
    // --------------------------------------------------
    public function handleGenerateCommentDraft(): void
    {
        check_ajax_referer('amai_comment_actions', 'nonce');
        if (!current_user_can('manage_automarketer_ai')) {
            wp_send_json_error(['message' => 'Unauthorized.'], 403);
        }
        $comment_id = (int) ($_POST['comment_id'] ?? 0);
        if ($comment_id <= 0) {
            wp_send_json_error(['message' => 'Invalid comment ID.']);
        }
        global $wpdb;
        $table = $wpdb->prefix . 'amai_social_comments';
        $row   = $wpdb->get_row(
            $wpdb->prepare("SELECT * FROM `{$table}` WHERE id = %d LIMIT 1", $comment_id),
            ARRAY_A
        );
        if (!is_array($row)) {
            wp_send_json_error(['message' => 'Comment not found.']);
        }
        $draft = \AutoMarketerAI\CommentResponder::generateDraftPublic($row);
        if ($draft === '') {
            wp_send_json_error(['message' => 'AI generation failed. Check your AI provider is active.']);
        }
        wp_send_json_success(['draft' => $draft]);
    }

    // --------------------------------------------------
    // AJAX: post a reply to a comment from the inbox UI.
    // --------------------------------------------------
    public function handleSendCommentReply(): void
    {
        check_ajax_referer('amai_comment_actions', 'nonce');
        if (!current_user_can('manage_automarketer_ai')) {
            wp_send_json_error(['message' => 'Unauthorized.'], 403);
        }
        $comment_id = (int) ($_POST['comment_id'] ?? 0);
        $reply_text = sanitize_textarea_field((string) ($_POST['reply_text'] ?? ''));
        if ($comment_id <= 0 || $reply_text === '') {
            wp_send_json_error(['message' => 'Invalid input.']);
        }
        $ok = \AutoMarketerAI\CommentResponder::postReply($comment_id, $reply_text);
        if ($ok) {
            wp_send_json_success(['message' => 'Reply sent.']);
        } else {
            wp_send_json_error(['message' => 'Could not send reply. Check the channel is still connected.']);
        }
    }

    // --------------------------------------------------
    // AJAX: mark a comment as ignored.
    // --------------------------------------------------
    public function handleIgnoreComment(): void
    {
        check_ajax_referer('amai_comment_actions', 'nonce');
        if (!current_user_can('manage_automarketer_ai')) {
            wp_send_json_error(['message' => 'Unauthorized.'], 403);
        }
        $comment_id = (int) ($_POST['comment_id'] ?? 0);
        if ($comment_id <= 0) {
            wp_send_json_error(['message' => 'Invalid comment ID.']);
        }
        global $wpdb;
        $table = $wpdb->prefix . 'amai_social_comments';
        $wpdb->update(
            $table,
            ['status' => 'ignored', 'updated_at' => gmdate('Y-m-d H:i:s')],
            ['id' => $comment_id],
            ['%s', '%s'],
            ['%d']
        );
        wp_send_json_success(['message' => 'Ignored.']);
    }

    // --------------------------------------------------
    // AJAX: save comment form settings (enabled toggles, rate limit, reply modes, profanity list).
    // --------------------------------------------------
    public function handleSaveCommentFormSettings(): void
    {
        check_ajax_referer('amai_comment_form_actions', 'nonce');
        if (!current_user_can('manage_automarketer_ai')) {
            wp_send_json_error(['message' => 'Unauthorized.'], 403);
        }

        $posts_enabled    = isset($_POST['posts_enabled'])    && $_POST['posts_enabled']    === '1' ? '1' : '0';
        $products_enabled = isset($_POST['products_enabled']) && $_POST['products_enabled'] === '1' ? '1' : '0';
        $rate_limit       = max(1, min(100, (int) ($_POST['rate_limit'] ?? 10)));
        $profanity_raw    = isset($_POST['profanity_words']) ? sanitize_textarea_field(wp_unslash((string) $_POST['profanity_words'])) : '';

        $valid_modes       = ['manual', 'ai_assisted', 'autopilot'];
        $reply_mode_posts  = sanitize_key((string) ($_POST['reply_mode_posts'] ?? 'manual'));
        $reply_mode_prods  = sanitize_key((string) ($_POST['reply_mode_products'] ?? 'manual'));
        if (!in_array($reply_mode_posts, $valid_modes, true)) {
            $reply_mode_posts = 'manual';
        }
        if (!in_array($reply_mode_prods, $valid_modes, true)) {
            $reply_mode_prods = 'manual';
        }

        update_option('amai_comment_form_posts_enabled',    $posts_enabled);
        update_option('amai_comment_form_products_enabled', $products_enabled);
        update_option('amai_comment_form_rate_limit',       (string) $rate_limit);
        update_option('amai_comment_form_reply_mode_posts',     $reply_mode_posts);
        update_option('amai_comment_form_reply_mode_products',  $reply_mode_prods);
        update_option('amai_comment_profanity_words',       $profanity_raw);
        update_option('amai_hot_thread_alerts_enabled', isset($_POST['ht_alerts_enabled']) && $_POST['ht_alerts_enabled'] === '1' ? '1' : '0');
        update_option('amai_review_velocity_alerts_enabled', isset($_POST['rv_alerts_enabled']) && $_POST['rv_alerts_enabled'] === '1' ? '1' : '0');
        update_option('amai_hot_thread_threshold',      (string) max(1, min(100, (int) ($_POST['ht_threshold'] ?? 5))));
        update_option('amai_hot_thread_window',         (string) max(1, min(24,  (int) ($_POST['ht_window']    ?? 2))));

        wp_send_json_success(['message' => 'Saved.']);
    }

    // --------------------------------------------------
    // AJAX -- save custom email template
    // --------------------------------------------------
    public function handleSaveEmailTemplate(): void
    {
        if (!current_user_can('manage_automarketer_ai')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }
        check_ajax_referer('amai_settings_actions', '_ajax_nonce');
        $type    = sanitize_key((string) ($_POST['tpl_type'] ?? ''));
        $subject = sanitize_text_field(wp_unslash((string) ($_POST['tpl_subject'] ?? '')));
        $body    = wp_kses_post(wp_unslash((string) ($_POST['tpl_body'] ?? '')));
        if ($type === '' || $subject === '' || $body === '') {
            wp_send_json_error(['message' => 'Missing required fields.']);
            return;
        }
        $saved = \AutoMarketerAI\EmailTemplates::save($type, $subject, $body);
        if (!$saved) {
            wp_send_json_error(['message' => 'Unknown template type.']);
            return;
        }
        wp_send_json_success(['message' => 'Template saved.']);
    }

    // --------------------------------------------------
    // AJAX -- reset custom email template to default
    // --------------------------------------------------
    public function handleResetEmailTemplate(): void
    {
        if (!current_user_can('manage_automarketer_ai')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }
        check_ajax_referer('amai_settings_actions', '_ajax_nonce');
        $type = sanitize_key((string) ($_POST['tpl_type'] ?? ''));
        if ($type === '') {
            wp_send_json_error(['message' => 'Missing type.']);
            return;
        }
        \AutoMarketerAI\EmailTemplates::reset($type);
        wp_send_json_success(['message' => 'Template reset to default.']);
    }

    // --------------------------------------------------
    // AJAX -- load saved template body + subject for editor
    // --------------------------------------------------
    public function handleLoadEmailTemplate(): void
    {
        if (!current_user_can('manage_automarketer_ai')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }
        check_ajax_referer('amai_settings_actions', '_ajax_nonce');
        $type = sanitize_key((string) ($_POST['tpl_type'] ?? ''));
        if ($type === '') {
            wp_send_json_error(['message' => 'Missing type.']);
            return;
        }
        $registry = \AutoMarketerAI\EmailTemplates::getRegistry();
        if (!isset($registry[$type])) {
            wp_send_json_error(['message' => 'Unknown type.']);
            return;
        }
        $has_custom = \AutoMarketerAI\EmailTemplates::hasCustom($type);
        $body       = $has_custom
            ? \AutoMarketerAI\EmailTemplates::getBody($type)
            : \AutoMarketerAI\EmailTemplates::getDefault($type);
        wp_send_json_success([
            'subject'        => \AutoMarketerAI\EmailTemplates::getSubject($type),
            'body'           => $body,
            'has_custom'     => $has_custom,
            'tokens'         => $registry[$type]['tokens'],
            'dynamic_tokens' => $registry[$type]['dynamic_tokens'],
            'label'          => $registry[$type]['label'],
        ]);
    }
}