<?php
/**
 * Maintenance GENIUS - Settings
 * Gestion centralisée des paramètres par défaut
 * Version 2.9.126
 * 
 * IMPORTANT: C'est LE SEUL fichier où les defaults sont définis.
 * Toute modification des valeurs par défaut doit être faite ICI.
 */

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

class MG_Settings {

    /**
     * Retourne tous les paramètres par défaut
     * @return array
     */
    public static function get_defaults() {
        return [
            'maintenance_enabled'     => false,
            'page_mode'               => 'maintenance', // 'maintenance' | 'coming_soon'
            'title'                   => __('Site Under Maintenance', 'maintenance-genius'),
            'subtitle'                => '',
            'message'                 => __('We are currently performing maintenance. We will be back soon.', 'maintenance-genius'),
            'logo_url'                => '',

            'visitor_access_enabled'  => false,
            'visitor_password'        => '',
            'visitor_access_duration' => 86400, // secondes (24 h) — v2.9.73
            'visitor_token'           => '',

            'show_admin_button'      => true,

            // Nouvelles installations seulement (sites existants conservent leurs choix).
            'bg_color_1'              => '#1F2937',
            'bg_color_1_opacity'      => 100,
            'bg_color_2'              => '#0F172A',
            'bg_color_2_opacity'      => 100,

            'title_color'             => '#F97316',
            'title_opacity'           => 100,
            'message_color'           => '#FFEDD5',
            'message_opacity'         => 100,

            'button_color'            => '#F97316',
            'button_opacity'          => 100,

            'bg_image_enabled'        => false,
            'bg_image_url'            => '',
            'bg_overlay_opacity'      => 50,

            'container_bg_color'      => '#0F172A',
            'container_bg_opacity'    => 92,
            'container_shadow_enabled'=> true,
            'container_shadow_intensity' => 30,
            'container_max_width'     => 600,
            'container_padding'       => 60,
            'container_border_radius' => 20,

            'title_font'              => 'Poppins',
            'message_font'            => 'Open Sans',
            'title_size'              => 42,
            'message_size'            => 18,

            'animation_enabled'       => true,
            'logo_animation'          => 'pulse',
            'text_animation'          => 'fadeIn',
            'button_animation'        => 'slideUp',
            'animation_duration'      => 1000,

            'rgpd_mode'               => false,

            'seo_503_enabled'         => true,
            'retry_after_hours'       => 24,
            'noindex_enabled'         => true,
            // Pour FREE : toujours visible (convention plugin gratuit WP.org).
            'show_powered_by'         => false, // v2.9.130 — crédit frontend opt-in (WP.org guideline 10). Remplace hide_powered_by.

            'sidebar_enabled'         => false,
            'sidebar_position'        => 'right',
            'sidebar_tab_color'       => '#667eea',
            'social_facebook'         => '',
            'social_twitter'          => '',
            'social_instagram'        => '',
            'social_linkedin'         => '',
            'social_youtube'          => '',
            'social_tiktok'           => '',
            'contact_email'           => '',
            'contact_phone'           => '',
            'contact_whatsapp'        => '',

            'logo_size'               => 150,

            'custom_bg_enabled'       => false,
            'custom_bg_url'           => '',


            'purge_data_on_uninstall' => false,

            'notification_enabled'    => false,
            'notification_button_text'=> __('Notify me', 'maintenance-genius'),
            'notification_success_msg'=> __('Thank you! You will be notified as soon as the site is back online.', 'maintenance-genius'),
            'notification_emails'     => [],

            'turnstile_enabled'       => false,
            'turnstile_site_key'      => '',
            'turnstile_secret_key'    => '',
            'turnstile_on_notification' => true,
            'turnstile_on_visitor'    => true,

            'countdown_enabled'       => false,
            'countdown_date'          => '',
            'countdown_time'          => '00:00',
            'countdown_title'         => __('Launching in', 'maintenance-genius'),
            'countdown_end_text'      => __('The site is now online!', 'maintenance-genius'),
            'countdown_style'         => 'boxes',
            'countdown_show_days'     => true,
            'countdown_show_hours'    => true,
            'countdown_show_minutes'  => true,
            'countdown_show_seconds'  => true,
            'countdown_auto_disable'  => true,

            'custom_presets'          => [],

            'lottie_enabled'          => false,
            'lottie_url'              => '',
            'lottie_position'         => 'above-title',
            'lottie_size'             => 200,
            'lottie_loop'             => true,
            'lottie_autoplay'         => true,
            'lottie_speed'            => 1,

            'video_bg_enabled'        => false,
            'video_bg_url'            => '',
            'video_bg_type'           => 'youtube',
            'video_bg_opacity'        => 30,
            'video_bg_muted'          => true,

            'parallax_enabled'        => false,
            'parallax_intensity'      => 50,

            'stats_enabled'           => false,
            // mg_stats_visits + mg_stats). Voir MG_Settings::get_stats() / record_visit().
            'stats_start_date'        => '',

            'schedule_enabled'        => false,
            'schedule_start_date'     => '',
            'schedule_start_time'     => '',
            'schedule_end_date'       => '',
            'schedule_end_time'       => '',
            'schedule_timezone'       => 'America/Montreal',

            'monitoring_enabled'          => false,
            'monitoring_independent'      => true,
            'monitoring_interval'         => 5,
            'monitoring_emails'           => '',
            'monitoring_email_cooldown'   => 5,
            'monitoring_subject_down'     => '',
            'monitoring_body_down'        => '',
            'monitoring_subject_up'       => '',
            'monitoring_body_up'          => '',

            'whitelist_enabled'       => false,
            'whitelist_urls'          => [],
            'whitelist_ips'           => [],
            'whitelist_roles'         => ['administrator'],
            'whitelist_user_agents'   => ['Googlebot', 'Bingbot', 'Slurp', 'DuckDuckBot'],

            'blacklist_enabled'       => false,
            'blacklist_ips'           => [],
            'blacklist_user_agents'   => [],
            'blacklist_referrers'     => [],
            'blacklist_countries'     => [],
            'blacklist_message'       => 'Access denied.',
            'blacklist_logging'       => false,

            'custom_access_enabled'   => true,
            'custom_access_limit'     => 25,
            'custom_access_max_days'  => 90,
            'custom_access_default_days' => 7,
        ];
    }

    /**
     * Charge les paramètres depuis la BDD avec merge des defaults
     * @return array
     */
    public static function load() {
        $saved = get_option('mg_settings', []);
        return wp_parse_args($saved, self::get_defaults());
    }

    /**
     * v2.9.127 — Lecture GARANTIE fraîche depuis la base de données.
     *
     * OBLIGATOIRE dans tout handler load-modify-save (AJAX admin) : sur les
     * environnements avec object cache persistant (Redis/Memcached), get_option()
     * peut retourner une copie périmée du tableau — le save() qui suit écrase
     * alors la DB avec cet état périmé. Bug concret : appliquer un preset
     * désactivait le mode maintenance (maintenance_enabled=false hérité du cache).
     * Pattern S2 éprouvé : invalider le cache option AVANT la lecture.
     *
     * @return array
     */
    public static function load_fresh() {
        wp_cache_delete('mg_settings', 'options');
        wp_cache_delete('alloptions', 'options');
        return self::load();
    }

    /**
     * Sauvegarde les paramètres
     * @param array $settings
     * @return bool
     */
    public static function save($settings) {
        // MG_Main lit déjà cette option en SQL direct, l'autoload n'apportait aucun bénéfice.
        return update_option('mg_settings', $settings, false);
    }

    /**
     * v2.9.86 — Migration du doublon custom_bg vers bg_image (option dépréciée).
     * Idempotente : reproduit l'ancien comportement (custom_bg écrasait bg_image),
     * puis désactive définitivement custom_bg.
     */
    public static function migrate_custom_bg_to_bg_image() {
        if (get_option('mg_custom_bg_migrated')) {
            return;
        }
        $s = get_option('mg_settings', null);
        if (is_array($s)) {
            $changed = false;
            // Récupère l'image custom_bg UNIQUEMENT si aucune image standard n'est définie.
            if (!empty($s['custom_bg_enabled']) && !empty($s['custom_bg_url']) && empty($s['bg_image_url'])) {
                $s['bg_image_url'] = $s['custom_bg_url'];
                $s['bg_image_enabled'] = true;
                $changed = true;
            }
            if (!empty($s['custom_bg_enabled'])) {
                $s['custom_bg_enabled'] = false;
                $changed = true;
            }
            if (isset($s['custom_bg_url']) && $s['custom_bg_url'] !== '') {
                $s['custom_bg_url'] = '';
                $changed = true;
            }
            if ($changed) {
                update_option('mg_settings', $s, false);
            }
        }
        update_option('mg_custom_bg_migrated', 1, false);
    }

    /**
     * v2.9.70 — Force autoload=no sur l'option mg_settings (migration installs existantes).
     */
    public static function ensure_settings_autoload_off() {
        if (function_exists('wp_set_option_autoload')) { // WP 6.4+
            wp_set_option_autoload('mg_settings', false);
            return;
        }
        // Fallback WP < 6.4 : re-save avec le paramètre autoload.
        $v = get_option('mg_settings', null);
        if ($v !== null) {
            update_option('mg_settings', $v, false);
        }
    }

    /**
     * v2.9.71 — Lecture des stats (stockage isolé, non-autoloadé).
     * @return array{visits:int,unique:int}
     */
    public static function get_stats() {
        $visits = (int) get_option('mg_stats_visits', 0);
        $data   = get_option('mg_stats', []);
        $unique = ( is_array($data) && isset($data['unique']) ) ? (int) $data['unique'] : 0;
        return ['visits' => $visits, 'unique' => $unique];
    }

    /**
     * v2.9.71 — Enregistre une visite. Compteur incrémenté de façon atomique (SQL direct),
     * jamais en réécrivant le blob settings. Unique-visiteurs via set borné (10 000).
     * @param string $ip_hash Hash de l'IP visiteur (md5(ip + wp_salt()))
     * @return array{visits:int,unique:int,is_new:bool}
     */
    public static function record_visit($ip_hash) {
        global $wpdb;

        // Incrément atomique : évite les races read-modify-write sous charge.
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Atomic counter increment
        $rows = $wpdb->query($wpdb->prepare(
            "UPDATE {$wpdb->options} SET option_value = option_value + 1 WHERE option_name = %s",
            'mg_stats_visits'
        ));
        if (!$rows) {
            // Première visite : l'option n'existe pas encore.
            add_option('mg_stats_visits', 1, '', 'no');
        }
        wp_cache_delete('mg_stats_visits', 'options');

        // Unique-visiteurs (set borné).
        $data = get_option('mg_stats', []);
        if (!is_array($data)) { $data = []; }
        $ips    = ( isset($data['visitor_ips']) && is_array($data['visitor_ips']) ) ? $data['visitor_ips'] : [];
        $is_new = !in_array($ip_hash, $ips, true);
        if ($is_new) {
            $ips[] = $ip_hash;
            if (count($ips) > 10000) {
                $ips = array_slice($ips, -10000);
            }
            $data['visitor_ips'] = $ips;
            $data['unique']      = (int) ($data['unique'] ?? 0) + 1;
            update_option('mg_stats', $data, false);
        }

        $stats = self::get_stats();
        $stats['is_new'] = $is_new;
        return $stats;
    }

    /**
     * v2.9.71 — Remet les stats à zéro.
     */
    public static function reset_stats() {
        update_option('mg_stats_visits', 0, false);
        update_option('mg_stats', ['unique' => 0, 'visitor_ips' => []], false);
    }

    /**
     * v2.9.73 — Migration one-shot : convertit l'ancienne durée d'accès (heures) en secondes.
     * Avant v2.9.73 la valeur était traitée en heures (× 3600) et le menu UI était cassé.
     * Toute valeur < 3600 est donc une valeur legacy en heures → on multiplie par 3600.
     */
    public static function migrate_visitor_duration_to_seconds() {
        $settings = get_option('mg_settings', []);
        if (!is_array($settings) || !isset($settings['visitor_access_duration'])) {
            return;
        }
        $v = (int) $settings['visitor_access_duration'];
        if ($v > 0 && $v < 3600) {
            $settings['visitor_access_duration'] = min(31536000, $v * 3600);
            update_option('mg_settings', $settings, false);
        }
    }

    /**
     * v2.9.71 — Migration one-shot : déplace les anciens compteurs hors du blob settings.
     */
    public static function migrate_stats_from_settings() {
        $settings = get_option('mg_settings', []);
        if (!is_array($settings)) { return; }
        $changed = false;

        if (array_key_exists('stats_visits', $settings)) {
            if (get_option('mg_stats_visits', null) === null) {
                add_option('mg_stats_visits', (int) $settings['stats_visits'], '', 'no');
            }
            unset($settings['stats_visits']);
            $changed = true;
        }

        if (array_key_exists('stats_unique_visitors', $settings) || array_key_exists('stats_visitors_ips', $settings)) {
            $data = get_option('mg_stats', []);
            if (!is_array($data)) { $data = []; }
            if (!isset($data['unique'])) {
                $data['unique'] = (int) ($settings['stats_unique_visitors'] ?? 0);
            }
            if (!isset($data['visitor_ips'])) {
                $data['visitor_ips'] = is_array($settings['stats_visitors_ips'] ?? null) ? $settings['stats_visitors_ips'] : [];
            }
            update_option('mg_stats', $data, false);
            unset($settings['stats_unique_visitors'], $settings['stats_visitors_ips']);
            $changed = true;
        }

        if ($changed) {
            update_option('mg_settings', $settings, false);
        }
    }

    /**
     * v2.9.115 SECURITY (S2) — Emails abonnés : option dédiée mg_notification_emails,
     * autoload=false, écriture $wpdb direct + wp_cache_delete() (règle env #8 :
     * update_option() peut être bloqué par l'object cache Redis).
     * @return array Liste des abonnés [{email, date, ip}]
     */
    public static function get_notification_emails() {
        // Lecture directe SQL : contourne un éventuel cache objet stale.
        global $wpdb;
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Read-through bypassing potentially stale object cache
        $row = $wpdb->get_var( $wpdb->prepare( "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s LIMIT 1", 'mg_notification_emails' ) );
        if ( null === $row ) {
            return [];
        }
        $emails = maybe_unserialize( $row );
        return is_array( $emails ) ? $emails : [];
    }

    /**
     * v2.9.115 SECURITY (S2) — Écrit la liste des abonnés en SQL direct.
     * @param array $emails
     * @return bool Succès de l'écriture (vérifiable, contrairement à update_option).
     */
    public static function set_notification_emails( $emails ) {
        global $wpdb;
        if ( ! is_array( $emails ) ) {
            return false;
        }
        $value = maybe_serialize( $emails );

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Direct write, object cache purged below
        $exists = $wpdb->get_var( $wpdb->prepare( "SELECT option_id FROM {$wpdb->options} WHERE option_name = %s LIMIT 1", 'mg_notification_emails' ) );

        if ( null === $exists ) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Direct insert with autoload=no
            $result = $wpdb->insert(
                $wpdb->options,
                [ 'option_name' => 'mg_notification_emails', 'option_value' => $value, 'autoload' => 'no' ],
                [ '%s', '%s', '%s' ]
            );
        } else {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Direct update
            $result = $wpdb->update(
                $wpdb->options,
                [ 'option_value' => $value ],
                [ 'option_name' => 'mg_notification_emails' ],
                [ '%s' ],
                [ '%s' ]
            );
        }

        // Purge du cache objet (Redis/Memcached) pour que get_option() serve la valeur fraîche.
        wp_cache_delete( 'mg_notification_emails', 'options' );
        wp_cache_delete( 'alloptions', 'options' );
        wp_cache_delete( 'notoptions', 'options' );

        return false !== $result;
    }

    /**
     * v2.9.115 SECURITY (S2+S3) — Ajoute un abonné (append + dédup).
     * S3 : l'IP est hashée (md5(ip + wp_salt)) — jamais stockée en clair (align GDPR,
     * même mécanisme que les stats depuis v2.9.71).
     * @param string $email Email validé (is_email déjà passé par l'appelant)
     * @param string $visitor_ip IP brute (sera hashée ici)
     * @return string 'added' | 'duplicate' | 'error'
     */
    public static function add_notification_email( $email, $visitor_ip = '' ) {
        $emails = self::get_notification_emails();

        foreach ( $emails as $email_data ) {
            if ( isset( $email_data['email'] ) && $email_data['email'] === $email ) {
                return 'duplicate';
            }
        }

        $emails[] = [
            'email'   => $email,
            'date'    => current_time( 'mysql' ),
            'ip_hash' => $visitor_ip ? md5( $visitor_ip . wp_salt() ) : '',
        ];

        return self::set_notification_emails( $emails ) ? 'added' : 'error';
    }

    /**
     * v2.9.115 SECURITY (S2) — Migration douce : emails du blob mg_settings →
     * option dédiée. Idempotente. S3 appliqué au passage : IPs en clair → hashées.
     */
    public static function migrate_notification_emails_from_settings() {
        $settings = get_option( 'mg_settings', [] );
        if ( ! is_array( $settings ) || ! array_key_exists( 'notification_emails', $settings ) ) {
            return;
        }

        $legacy = is_array( $settings['notification_emails'] ) ? $settings['notification_emails'] : [];
        $current = self::get_notification_emails();
        $known = [];
        foreach ( $current as $e ) {
            if ( isset( $e['email'] ) ) { $known[ $e['email'] ] = true; }
        }

        foreach ( $legacy as $email_data ) {
            $mail = $email_data['email'] ?? '';
            if ( empty( $mail ) || isset( $known[ $mail ] ) ) {
                continue;
            }
            // S3 — IP en clair héritée → hashée ; jamais reportée brute.
            $raw_ip = $email_data['ip'] ?? '';
            $current[] = [
                'email'   => $mail,
                'date'    => $email_data['date'] ?? current_time( 'mysql' ),
                'ip_hash' => ( ! empty( $email_data['ip_hash'] ) ) ? $email_data['ip_hash'] : ( $raw_ip ? md5( $raw_ip . wp_salt() ) : '' ),
            ];
            $known[ $mail ] = true;
        }

        if ( self::set_notification_emails( $current ) ) {
            unset( $settings['notification_emails'] );
            update_option( 'mg_settings', $settings, false );
        }
    }

    /**
     * Réinitialise uniquement les paramètres de design
     * @param array $settings Settings actuels
     * @return array Settings avec design réinitialisé
     */
    public static function reset_design($settings) {
        $defaults = self::get_defaults();
        
        $design_keys = [
            'bg_color_1', 'bg_color_1_opacity',
            'bg_color_2', 'bg_color_2_opacity',
            'title_color', 'title_opacity',
            'message_color', 'message_opacity',
            'button_color', 'button_opacity',
            'bg_image_enabled', 'bg_image_url', 'bg_overlay_opacity',
            'container_bg_color', 'container_bg_opacity',
            'container_shadow_enabled', 'container_shadow_intensity',
            'container_max_width', 'container_padding', 'container_border_radius',
            'title_font', 'message_font',
            'animation_enabled', 'logo_animation', 'text_animation',
            'button_animation', 'animation_duration'
        ];

        foreach ($design_keys as $key) {
            $settings[$key] = $defaults[$key];
        }

        return $settings;
    }

    /**
     * Liste des polices disponibles
     * @return array
     */
    public static function get_fonts() {
        return [
            'Poppins'        => 'Poppins',
            'Open Sans'      => 'Open Sans',
            'Roboto'         => 'Roboto',
            'Lato'           => 'Lato',
            'Montserrat'     => 'Montserrat',
            'Playfair Display' => 'Playfair Display',
            'Raleway'        => 'Raleway',
            'Ubuntu'         => 'Ubuntu',
            'Nunito'         => 'Nunito',
            'Merriweather'   => 'Merriweather',
        ];
    }

    /**
     * Liste des animations disponibles
     * @return array
     */
    public static function get_animations() {
        return [
            'none'      => __('None', 'maintenance-genius'),
            'fadeIn'    => __('Fade In', 'maintenance-genius'),
            'slideUp'   => __('Slide Up', 'maintenance-genius'),
            'slideDown' => __('Slide Down', 'maintenance-genius'),
            'pulse'     => __('Pulse', 'maintenance-genius'),
            'bounce'    => __('Bounce', 'maintenance-genius'),
            'zoom'      => __('Zoom', 'maintenance-genius'),
        ];
    }

    /**
     * Presets de design prédéfinis
     * Version 2.5.6 - 10 presets avec images HD .webp
     * @return array
     */
    public static function get_presets() {
        $base_url = MG_PLUGIN_URL . 'assets/images/presets/';
        $logo_url = MG_PLUGIN_URL . 'assets/images/mg-icon-512.png';

        return [
            // Premier dans la liste, défaut pour nouvelles installations.
            'genius' => [
                'name' => '🔥 GENIUS Orange',
                'bg_color_1' => '#1F2937',           // Sombre pro
                'bg_color_2' => '#0F172A',           // Sombre profond (gradient)
                'title_color' => '#F97316',          // Orange chakra Racine
                'message_color' => '#FFEDD5',        // Orange ultra-clair
                'button_color' => '#F97316',         // Orange brand
                'container_bg_color' => '#0F172A',
                'container_bg_opacity' => 92,
                'bg_image_url' => $logo_url,         // Logo brand comme visual preset
                'bg_image_enabled' => true,
            ],
            'default' => [
                'name' => '🌸 Doux & Pastel',
                'bg_color_1' => '#D4BADD',
                'bg_color_2' => '#E4EFC5',
                'title_color' => '#F97316',
                'message_color' => '#555555',
                'button_color' => '#667eea',
                'container_bg_color' => '#ffffff',
                'container_bg_opacity' => 95,
                'bg_image_url' => '',
                'bg_image_enabled' => false,
            ],
            'construction' => [
                'name' => '🏗️ Chantier',
                'bg_color_1' => '#0a0a0a',
                'bg_color_2' => '#1a1a1a',
                'title_color' => '#f39c12',
                'message_color' => '#cccccc',
                'button_color' => '#f39c12',
                'container_bg_color' => '#ffffff',
                'container_bg_opacity' => 95,
                'bg_image_url' => $base_url . 'construction.webp',
                'bg_image_enabled' => true,
            ],
            'dark' => [
                'name' => '🌙 Sombre & Elegant',
                'bg_color_1' => '#1a1a2e',
                'bg_color_2' => '#16213e',
                'title_color' => '#ffffff',
                'message_color' => '#d0d0d0',
                'button_color' => '#e94560',
                'container_bg_color' => '#1a1a2e',
                'container_bg_opacity' => 90,
                'bg_image_url' => $base_url . 'dark.webp',
                'bg_image_enabled' => true,
            ],
            'light' => [
                'name' => '☀️ Clair & Epure',
                'bg_color_1' => '#f8f9fa',
                'bg_color_2' => '#e9ecef',
                'title_color' => '#212529',
                'message_color' => '#6c757d',
                'button_color' => '#495057',
                'container_bg_color' => '#ffffff',
                'container_bg_opacity' => 100,
                'bg_image_url' => '',
                'bg_image_enabled' => false,
            ],
            'corporate' => [
                'name' => '💼 Professionnel',
                'bg_color_1' => '#2c3e50',
                'bg_color_2' => '#34495e',
                'title_color' => '#2c3e50',
                'message_color' => '#555555',
                'button_color' => '#3498db',
                'container_bg_color' => '#ffffff',
                'container_bg_opacity' => 95,
                'bg_image_url' => $base_url . 'corporate.webp',
                'bg_image_enabled' => true,
            ],
            'startup' => [
                'name' => '🚀 Startup',
                'bg_color_1' => '#d4a574',
                'bg_color_2' => '#c4956a',
                'title_color' => '#2c3e50',
                'message_color' => '#555555',
                'button_color' => '#e67e22',
                'container_bg_color' => '#ffffff',
                'container_bg_opacity' => 95,
                'bg_image_url' => $base_url . 'startup.webp',
                'bg_image_enabled' => true,
            ],
            'nature' => [
                'name' => '🌿 Nature',
                'bg_color_1' => '#1a3a2a',
                'bg_color_2' => '#2d4a3a',
                'title_color' => '#ffffff',
                'message_color' => '#d4edda',
                'button_color' => '#28a745',
                'container_bg_color' => '#ffffff',
                'container_bg_opacity' => 92,
                'bg_image_url' => $base_url . 'nature.webp',
                'bg_image_enabled' => true,
            ],
            'tech' => [
                'name' => '⚡ Futuriste',
                'bg_color_1' => '#0a0f0a',
                'bg_color_2' => '#0d1a0d',
                'title_color' => '#00ff00',
                'message_color' => '#00cc00',
                'button_color' => '#00ff00',
                'container_bg_color' => '#0a0f0a',
                'container_bg_opacity' => 85,
                'bg_image_url' => $base_url . 'tech.webp',
                'bg_image_enabled' => true,
            ],
            'fun' => [
                'name' => '🎭 Comique',
                'bg_color_1' => '#f5f5f5',
                'bg_color_2' => '#eeeeee',
                'title_color' => '#2d3436',
                'message_color' => '#636e72',
                'button_color' => '#fdcb6e',
                'container_bg_color' => '#ffffff',
                'container_bg_opacity' => 95,
                'bg_image_url' => $base_url . 'fun.webp',
                'bg_image_enabled' => true,
            ],
            'christmas' => [
                'name' => '🎄 Noel',
                'bg_color_1' => '#1a3a2a',
                'bg_color_2' => '#0d2818',
                'title_color' => '#ffd700',
                'message_color' => '#f5f5dc',
                'button_color' => '#c41e3a',
                'container_bg_color' => '#ffffff',
                'container_bg_opacity' => 95,
                'bg_image_url' => $base_url . 'christmas.webp',
                'bg_image_enabled' => true,
            ],
            'video_ocean' => [
                'name' => '🌊 Vidéo Océan',
                'bg_color_1' => '#0077b6',
                'bg_color_2' => '#023e8a',
                'title_color' => '#ffffff',
                'message_color' => '#caf0f8',
                'button_color' => '#00b4d8',
                'container_bg_color' => '#03045e',
                'container_bg_opacity' => 75,
                'bg_image_enabled' => false,
                'video_bg_enabled' => true,
                'video_bg_url' => 'https://www.youtube.com/watch?v=bn9F19Hi1Lk',
                'video_bg_opacity' => 40,
            ],
            'video_nature' => [
                'name' => '🌲 Vidéo Nature',
                'bg_color_1' => '#2d6a4f',
                'bg_color_2' => '#1b4332',
                'title_color' => '#ffffff',
                'message_color' => '#d8f3dc',
                'button_color' => '#95d5b2',
                'container_bg_color' => '#081c15',
                'container_bg_opacity' => 70,
                'bg_image_enabled' => false,
                'video_bg_enabled' => true,
                'video_bg_url' => 'https://www.youtube.com/watch?v=BHACKCNDMW8',
                'video_bg_opacity' => 35,
            ],
            'video_city' => [
                'name' => '🌃 Vidéo Ville',
                'bg_color_1' => '#212529',
                'bg_color_2' => '#343a40',
                'title_color' => '#f8f9fa',
                'message_color' => '#dee2e6',
                'button_color' => '#ffc107',
                'container_bg_color' => '#000000',
                'container_bg_opacity' => 65,
                'bg_image_enabled' => false,
                'video_bg_enabled' => true,
                'video_bg_url' => 'https://www.youtube.com/watch?v=n4PR9NZlAB4',
                'video_bg_opacity' => 45,
            ],
            'video_abstract' => [
                'name' => '🎨 Vidéo Abstract',
                'bg_color_1' => '#7209b7',
                'bg_color_2' => '#3a0ca3',
                'title_color' => '#ffffff',
                'message_color' => '#e0aaff',
                'button_color' => '#f72585',
                'container_bg_color' => '#240046',
                'container_bg_opacity' => 70,
                'bg_image_enabled' => false,
                'video_bg_enabled' => true,
                'video_bg_url' => 'https://www.youtube.com/watch?v=LXb3EKWsInQ',
                'video_bg_opacity' => 50,
            ],
        ];
    }

    /**
     * Liste des pays pour le blocage géographique
     * @return array
     */
    public static function get_countries() {
        return [
            'CN' => 'China',
            'RU' => 'Russia',
            'IN' => 'India',
            'BR' => 'Brazil',
            'KP' => 'North Korea',
            'IR' => 'Iran',
            'PK' => 'Pakistan',
            'VN' => 'Vietnam',
            'ID' => 'Indonesia',
            'NG' => 'Nigeria',
            'UA' => 'Ukraine',
            'RO' => 'Romania',
            'TH' => 'Thailand',
            'PH' => 'Philippines',
            'TR' => 'Turkey',
        ];
    }

    /**
     * Liste des rôles WordPress
     * @return array
     */
    public static function get_roles() {
        $roles = [];
        $wp_roles = wp_roles();
        
        if ($wp_roles && isset($wp_roles->roles)) {
            foreach ($wp_roles->roles as $key => $role) {
                $roles[$key] = $role['name'];
            }
        }
        
        return $roles;
    }


    /**
     * Récupérer tous les tokens d'accès
     * @return array
     */
    public static function get_access_tokens() {
        return get_option('mg_access_tokens', []);
    }

    /**
     * Sauvegarder les tokens
     * @param array $tokens
     * @return bool
     */
    public static function save_access_tokens($tokens) {
        return update_option('mg_access_tokens', $tokens);
    }

    /**
     * Générer un nouveau token d'accès
     * @param array $data
     * @return string|false Token généré ou false si limite atteinte
     */
    public static function create_access_token($data) {
        $settings = self::load();
        $tokens = self::get_access_tokens();
        
        $active_count = self::count_active_tokens();
        $limit = intval($settings['custom_access_limit'] ?? 25);
        
        if ($limit > 0 && $active_count >= $limit) {
            return false;
        }
        
        $token = wp_generate_password(16, false, false);
        
        while (isset($tokens[$token])) {
            $token = wp_generate_password(16, false, false);
        }
        
        $tokens[$token] = [
            'name'        => sanitize_text_field($data['name'] ?? ''),
            'email'       => sanitize_email($data['email'] ?? ''),
            'created'     => current_time('mysql'),
            'expires'     => $data['expires'] ?? null,
            'single_use'  => !empty($data['single_use']),
            'ip_restrict' => sanitize_text_field($data['ip_restrict'] ?? ''),
            'used'        => false,
            'visits'      => 0,
            'last_visit'  => null,
        ];
        
        // échouer en silence avec un object cache). Échec → false, jamais un
        // lien fantôme remis à l'utilisateur.
        if ( ! self::save_access_tokens($tokens) ) {
            return false;
        }
        wp_cache_delete('mg_access_tokens', 'options');
        
        return $token;
    }

    /**
     * Supprimer un token
     * @param string $token
     * @return bool
     */
    public static function delete_access_token($token) {
        $tokens = self::get_access_tokens();
        
        if (isset($tokens[$token])) {
            unset($tokens[$token]);
            self::save_access_tokens($tokens);
            return true;
        }
        
        return false;
    }

    /**
     * Valider un token d'accès
     * @param string $token
     * @param string $ip IP du visiteur
     * @return bool|array False si invalide, données du token si valide
     */
    public static function validate_access_token($token, $ip = '') {
        $tokens = self::get_access_tokens();
        
        if (!isset($tokens[$token])) {
            return false;
        }
        
        $data = $tokens[$token];
        
        if (!empty($data['expires'])) {
            $expires_time = strtotime($data['expires']);
            if ($expires_time && $expires_time < time()) {
                return false;
            }
        }
        
        if ($data['single_use'] && $data['used']) {
            return false;
        }
        
        if (!empty($data['ip_restrict']) && $data['ip_restrict'] !== $ip) {
            return false;
        }
        
        return $data;
    }

    /**
     * Enregistrer une visite via token
     * @param string $token
     * @param string $ip
     * @param string $user_agent
     */
    public static function log_token_visit($token, $ip, $user_agent) {
        $tokens = self::get_access_tokens();
        
        if (!isset($tokens[$token])) {
            return;
        }
        
        $tokens[$token]['visits'] = ($tokens[$token]['visits'] ?? 0) + 1;
        $tokens[$token]['last_visit'] = current_time('mysql');
        $tokens[$token]['used'] = true;
        
        self::save_access_tokens($tokens);
        
        $logs = get_option('mg_access_logs', []);
        
        $logs[] = [
            'token'      => $token,
            'name'       => $tokens[$token]['name'],
            'time'       => current_time('mysql'),
            'ip'         => $ip,
            'user_agent' => $user_agent,
        ];
        
        if (count($logs) > 500) {
            $logs = array_slice($logs, -500);
        }
        
        update_option('mg_access_logs', $logs);
    }

    /**
     * Récupérer les logs d'accès
     * @param int $limit
     * @return array
     */
    public static function get_access_logs($limit = 100) {
        $logs = get_option('mg_access_logs', []);
        $logs = array_reverse($logs);
        return array_slice($logs, 0, $limit);
    }

    /**
     * Vider les logs d'accès
     * @return bool
     */
    public static function clear_access_logs() {
        return delete_option('mg_access_logs');
    }

    /**
     * Compter les tokens actifs (non expirés)
     * @return int
     */
    public static function count_active_tokens() {
        $tokens = self::get_access_tokens();
        $count = 0;
        $now = current_time('timestamp');
        
        foreach ($tokens as $data) {
            if (empty($data['expires'])) {
                $count++;
            } else {
                $expires_time = strtotime($data['expires']);
                if ($expires_time && $expires_time > $now) {
                    $count++;
                }
            }
        }
        
        return $count;
    }

    /**
     * Compter les tokens expirés
     * @return int
     */
    public static function count_expired_tokens() {
        $tokens = self::get_access_tokens();
        $count = 0;
        $now = current_time('timestamp');
        
        foreach ($tokens as $data) {
            if (!empty($data['expires'])) {
                $expires_time = strtotime($data['expires']);
                if ($expires_time && $expires_time <= $now) {
                    $count++;
                }
            }
        }
        
        return $count;
    }

    /**
     * Obtenir les statistiques des accès
     * @return array
     */
    public static function get_access_stats() {
        $tokens = self::get_access_tokens();
        $logs = get_option('mg_access_logs', []);
        $settings = self::load();
        
        $total_visits = 0;
        $today_visits = 0;
        $today_start = strtotime(current_time('Y-m-d') . ' 00:00:00');
        
        foreach ($tokens as $data) {
            $total_visits += ($data['visits'] ?? 0);
        }
        
        foreach ($logs as $log) {
            $log_time = strtotime($log['time']);
            if ($log_time >= $today_start) {
                $today_visits++;
            }
        }
        
        return [
            'active'       => self::count_active_tokens(),
            'expired'      => self::count_expired_tokens(),
            'total_visits' => $total_visits,
            'today_visits' => $today_visits,
            'limit'        => intval($settings['custom_access_limit'] ?? 25),
        ];
    }

    /**
     * v2.9.118 — Thèmes complets (5 thèmes : Sobre · Corporate · Festif · Lancement · Horizon).
     * Un thème applique une configuration complète : visuel + typo + layout + animations.
     * Les presets visuels existants restent intacts — les Thèmes sont une couche au-dessus.
     * @return array
     */
    public static function get_themes() {
        return [
            'sobre' => [
                'id'          => 'sobre',
                'label'       => __('Sobre', 'maintenance-genius'),
                'description' => __('Premium minimalism, zero distraction.', 'maintenance-genius'),
                'css_preview' => ['from' => '#FAFAF9', 'to' => '#E7E5E4', 'accent' => '#1C1917'],
                'font_preview'=> 'Poppins',
                'settings'    => [
                    'bg_color_1' => '#FAFAF9', 'bg_color_1_opacity' => 100,
                    'bg_color_2' => '#E7E5E4', 'bg_color_2_opacity' => 100,
                    'title_color' => '#1C1917', 'title_opacity' => 100,
                    'message_color' => '#57534E', 'message_opacity' => 100,
                    'button_color' => '#1C1917', 'button_opacity' => 100,
                    'bg_image_enabled' => false,
                    'container_bg_color' => '#FFFFFF', 'container_bg_opacity' => 100,
                    'container_shadow_enabled' => true, 'container_shadow_intensity' => 12,
                    'container_max_width' => 560, 'container_padding' => 56, 'container_border_radius' => 12,
                    'title_font' => 'Poppins', 'message_font' => 'Open Sans',
                    'title_size' => 36, 'message_size' => 17,
                    'animation_enabled' => true, 'logo_animation' => 'none',
                    'text_animation' => 'fadeIn', 'button_animation' => 'fadeIn', 'animation_duration' => 600,
                    'parallax_enabled' => false,
                    'countdown_style' => 'minimal',
                ],
            ],
            'corporate' => [
                'id'          => 'corporate',
                'label'       => __('Corporate', 'maintenance-genius'),
                'description' => __('Institutional trust — deep blue, B2B and professionals.', 'maintenance-genius'),
                'css_preview' => ['from' => '#0F2A4A', 'to' => '#1E3A5F', 'accent' => '#2563EB'],
                'font_preview'=> 'Poppins',
                'settings'    => [
                    'bg_color_1' => '#0F2A4A', 'bg_color_1_opacity' => 100,
                    'bg_color_2' => '#1E3A5F', 'bg_color_2_opacity' => 100,
                    'title_color' => '#FFFFFF', 'title_opacity' => 100,
                    'message_color' => '#CBD5E1', 'message_opacity' => 100,
                    'button_color' => '#2563EB', 'button_opacity' => 100,
                    'bg_image_enabled' => false,
                    'container_bg_color' => '#0B1F38', 'container_bg_opacity' => 88,
                    'container_shadow_enabled' => true, 'container_shadow_intensity' => 35,
                    'container_max_width' => 640, 'container_padding' => 60, 'container_border_radius' => 8,
                    'title_font' => 'Poppins', 'message_font' => 'Open Sans',
                    'title_size' => 40, 'message_size' => 18,
                    'animation_enabled' => true, 'logo_animation' => 'none',
                    'text_animation' => 'fadeIn', 'button_animation' => 'slideUp', 'animation_duration' => 800,
                    'parallax_enabled' => false,
                    'countdown_style' => 'boxes',
                ],
            ],
            'festif' => [
                'id'          => 'festif',
                'label'       => __('Festif', 'maintenance-genius'),
                'description' => __('Warm energy — events, retail, campaigns.', 'maintenance-genius'),
                'css_preview' => ['from' => '#7C3AED', 'to' => '#EC4899', 'accent' => '#F59E0B'],
                'font_preview'=> 'Poppins',
                'settings'    => [
                    'bg_color_1' => '#7C3AED', 'bg_color_1_opacity' => 100,
                    'bg_color_2' => '#EC4899', 'bg_color_2_opacity' => 100,
                    'title_color' => '#FFFFFF', 'title_opacity' => 100,
                    'message_color' => '#FDF2F8', 'message_opacity' => 95,
                    'button_color' => '#F59E0B', 'button_opacity' => 100,
                    'bg_image_enabled' => false,
                    'container_bg_color' => '#4C1D95', 'container_bg_opacity' => 72,
                    'container_shadow_enabled' => true, 'container_shadow_intensity' => 45,
                    'container_max_width' => 620, 'container_padding' => 64, 'container_border_radius' => 28,
                    'title_font' => 'Poppins', 'message_font' => 'Open Sans',
                    'title_size' => 44, 'message_size' => 18,
                    'animation_enabled' => true, 'logo_animation' => 'pulse',
                    'text_animation' => 'fadeIn', 'button_animation' => 'slideUp', 'animation_duration' => 1000,
                    'parallax_enabled' => true, 'parallax_intensity' => 40,
                    'countdown_style' => 'boxes',
                ],
            ],
            'lancement' => [
                'id'          => 'lancement',
                'label'       => __('Lancement', 'maintenance-genius'),
                'description' => __('Tech night + electric accent — startups and new sites.', 'maintenance-genius'),
                'css_preview' => ['from' => '#020617', 'to' => '#0F172A', 'accent' => '#22D3EE'],
                'font_preview'=> 'Poppins',
                'settings'    => [
                    'bg_color_1' => '#020617', 'bg_color_1_opacity' => 100,
                    'bg_color_2' => '#0F172A', 'bg_color_2_opacity' => 100,
                    'title_color' => '#22D3EE', 'title_opacity' => 100,
                    'message_color' => '#94A3B8', 'message_opacity' => 100,
                    'button_color' => '#06B6D4', 'button_opacity' => 100,
                    'bg_image_enabled' => false,
                    'container_bg_color' => '#0F172A', 'container_bg_opacity' => 65,
                    'container_shadow_enabled' => true, 'container_shadow_intensity' => 50,
                    'container_max_width' => 680, 'container_padding' => 64, 'container_border_radius' => 16,
                    'title_font' => 'Poppins', 'message_font' => 'Open Sans',
                    'title_size' => 46, 'message_size' => 18,
                    'animation_enabled' => true, 'logo_animation' => 'none',
                    'text_animation' => 'fadeIn', 'button_animation' => 'slideUp', 'animation_duration' => 900,
                    'parallax_enabled' => false,
                    'countdown_style' => 'boxes',
                    'countdown_enabled' => true,
                ],
            ],
            'horizon' => [
                'id'          => 'horizon',
                'label'       => __('Horizon', 'maintenance-genius'),
                'description' => __('Coming Soon — warm gradient, optimistic and welcoming.', 'maintenance-genius'),
                'css_preview' => ['from' => '#F59E0B', 'to' => '#EF4444', 'accent' => '#FFFFFF'],
                'font_preview'=> 'Poppins',
                'for_mode'    => 'coming_soon',
                'settings'    => [
                    'bg_color_1' => '#F59E0B', 'bg_color_1_opacity' => 100,
                    'bg_color_2' => '#EF4444', 'bg_color_2_opacity' => 100,
                    'title_color' => '#FFFFFF', 'title_opacity' => 100,
                    'message_color' => '#FEF3C7', 'message_opacity' => 100,
                    'button_color' => '#FFFFFF', 'button_opacity' => 100,
                    'bg_image_enabled' => false,
                    'container_bg_color' => '#7C2D12', 'container_bg_opacity' => 40,
                    'container_shadow_enabled' => true, 'container_shadow_intensity' => 40,
                    'container_max_width' => 620, 'container_padding' => 60, 'container_border_radius' => 20,
                    'title_font' => 'Poppins', 'message_font' => 'Open Sans',
                    'title_size' => 44, 'message_size' => 18,
                    'animation_enabled' => true, 'logo_animation' => 'pulse',
                    'text_animation' => 'fadeIn', 'button_animation' => 'slideUp', 'animation_duration' => 800,
                    'parallax_enabled' => false,
                    'countdown_style' => 'minimal',
                    'countdown_enabled' => true,
                ],
            ],
        ];
    }

    /**
     * v2.9.118 — Applique un thème complet.
     * 1. Snapshot $wpdb direct (pattern S2, option non-autoloadée).
     * 2. Merge sélectif (uniquement les clés listées §4 de la spec).
     * 3. Save vérifié — false si l'écriture échoue (Redis-safe).
     * Ne modifie JAMAIS : title, subtitle, message, logo_url,
     * ni les réglages PRO fonctionnels (visitor_*, notification_*, tokens, remote, Turnstile, SEO).
     *
     * @param string $theme_id ID du thème (sobre|corporate|festif|lancement)
     * @return bool Succès
     */
    public static function apply_theme( $theme_id ) {
        global $wpdb;

        $themes = self::get_themes();
        if ( ! isset( $themes[ $theme_id ] ) ) {
            return false;
        }

        $settings = self::load();

        // — Snapshot (option non-autoloadée, $wpdb direct, pattern S2) —
        $snapshot_value = maybe_serialize( $settings );
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Critical snapshot write, bypasses Redis object cache
        $snap_exists = $wpdb->get_var( $wpdb->prepare(
            "SELECT option_id FROM {$wpdb->options} WHERE option_name = %s LIMIT 1",
            'mg_theme_snapshot'
        ) );
        if ( null === $snap_exists ) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Direct insert, autoload=no
            $wpdb->insert(
                $wpdb->options,
                [ 'option_name' => 'mg_theme_snapshot', 'option_value' => $snapshot_value, 'autoload' => 'no' ],
                [ '%s', '%s', '%s' ]
            );
        } else {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Direct update
            $wpdb->update(
                $wpdb->options,
                [ 'option_value' => $snapshot_value ],
                [ 'option_name'  => 'mg_theme_snapshot' ],
                [ '%s' ],
                [ '%s' ]
            );
        }
        wp_cache_delete( 'mg_theme_snapshot', 'options' );
        wp_cache_delete( 'alloptions', 'options' );
        wp_cache_delete( 'notoptions', 'options' );

        // — Merge sélectif : uniquement les clés définies dans le thème —
        $theme_map = $themes[ $theme_id ]['settings'];

        if ( 'lancement' === $theme_id && isset( $theme_map['countdown_enabled'] ) ) {
            if ( ! empty( $settings['countdown_date'] ) ) {
                unset( $theme_map['countdown_enabled'] );
            }
        }

        foreach ( $theme_map as $key => $value ) {
            $settings[ $key ] = $value;
        }

        $saved = self::save( $settings );
        if ( ! $saved ) {
            return false;
        }

        // Stocker l'ID du thème actif (non-critique, best-effort).
        update_option( 'mg_active_theme', sanitize_key( $theme_id ), false );

        return true;
    }

    /**
     * v2.9.118 — Annule le dernier thème appliqué (restaure le snapshot).
     * @return bool Succès
     */
    public static function revert_theme() {
        global $wpdb;

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Read snapshot bypassing stale cache
        $row = $wpdb->get_var( $wpdb->prepare(
            "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s LIMIT 1",
            'mg_theme_snapshot'
        ) );

        if ( null === $row ) {
            return false;
        }

        $snapshot = maybe_unserialize( $row );
        if ( ! is_array( $snapshot ) ) {
            return false;
        }

        $saved = self::save( $snapshot );
        if ( ! $saved ) {
            return false;
        }

        // Supprimer snapshot + thème actif.
        delete_option( 'mg_theme_snapshot' );
        delete_option( 'mg_active_theme' );
        wp_cache_delete( 'mg_theme_snapshot', 'options' );
        wp_cache_delete( 'mg_active_theme', 'options' );
        wp_cache_delete( 'alloptions', 'options' );

        return true;
    }

    /**
     * v2.9.118 — Retourne l'ID du thème actif ou false.
     * @return string|false
     */
    public static function get_active_theme() {
        return get_option( 'mg_active_theme', false );
    }

    /**
     * v2.9.118 — Indique si un snapshot (annulation disponible) existe en base.
     * @return bool
     */
    public static function has_theme_snapshot() {
        global $wpdb;
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Presence check, bypasses stale object cache
        $exists = $wpdb->get_var( $wpdb->prepare(
            "SELECT option_id FROM {$wpdb->options} WHERE option_name = %s LIMIT 1",
            'mg_theme_snapshot'
        ) );
        return null !== $exists;
    }

    /**
     * Options d'expiration disponibles
     * @return array
     */
    public static function get_expiration_options() {
        return [
            '1h'   => __('1 hour', 'maintenance-genius'),
            '6h'   => __('6 hours', 'maintenance-genius'),
            '24h'  => __('24 hours', 'maintenance-genius'),
            '48h'  => __('48 hours', 'maintenance-genius'),
            '7d'   => __('7 days', 'maintenance-genius'),
            '14d'  => __('14 days', 'maintenance-genius'),
            '30d'  => __('30 days', 'maintenance-genius'),
            '90d'  => __('90 days', 'maintenance-genius'),
            'custom' => __('Custom date', 'maintenance-genius'),
        ];
    }
}
