<?php
/**
 * Name: Lean Analytics
 * Description: Privacy-first analytics tracking pageviews and visits in single database table without storing personal data, cookies, or requiring consent for GDPR compliance
 * Category: Utilities
 * Icon: -
 * UseCases: [
 *   {
 *     "title": "Add Useful Functionality",
 *     "description": "Extend WordPress with helpful tools that make site management easier."
 *   },
 *   {
 *     "title": "Save Time",
 *     "description": "Automate or simplify tasks that would otherwise require manual work or additional tools."
 *   }
 * ]
 * Added: 2025-09-28
 * Tags: analytics, tracking, privacy, statistics, reporting, metrics
 * WhereToFind: {
 *   "location": "Switchboard → Analytics",
 *   "url": "admin.php?page=lean-analytics",
 *   "instructions": "View analytics dashboard with pageviews, visits, and reports",
 *   "newTab": false
 * }
 *
 * @package Switchboard
 * @since 1.0.0
 */

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

class SwitchboardLeanAnalytics {
    
    private $table_name;
    private $option_name = 'switchboard_lean_analytics';
    private $settings;
    
    public function __construct() {
        global $wpdb;
        $this->table_name = $wpdb->prefix . 'lean_events';
        $this->settings = $this->get_settings();

        // Setup database table (checked on admin_init)
        add_action('admin_init', [$this, 'maybe_create_table']);

        // Tracking
        add_action('wp_enqueue_scripts', [$this, 'enqueue_beacon'], 999);
        add_action('wp_ajax_nopriv_lean_track', [$this, 'handle_tracking']);
        add_action('wp_ajax_lean_track', [$this, 'handle_tracking']);

        // Admin menu
        add_action('admin_menu', [$this, 'add_admin_menu']);

        // AJAX handlers for dashboard
        add_action('wp_ajax_lean_analytics_data', [$this, 'ajax_get_analytics_data']);
        add_action('wp_ajax_lean_analytics_cleanup', [$this, 'ajax_cleanup']);
        add_action('wp_ajax_lean_analytics_export', [$this, 'ajax_export']);
        add_action('wp_ajax_lean_analytics_realtime', [$this, 'ajax_get_realtime_data']);

        // Setup cron for cleanup
        add_action('switchboard_lean_analytics_cleanup', [$this, 'auto_cleanup']);

        // Enqueue admin scripts
        add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_assets']);

        // Settings save handler
        add_action('switchboard_save_module_settings', [$this, 'save_settings'], 10, 2);
    }

    /**
     * Get data for email report
     *
     * @param array $date_range ['from' => 'Y-m-d', 'to' => 'Y-m-d']
     * @return array Report data
     */
    public function get_report_data($date_range) {
        $dates = [
            'from' => $date_range['from'],
            'to'   => $date_range['to'],
        ];

        $overview = $this->get_overview_data($dates);
        $top_pages = $this->get_top_pages($dates);
        $top_referrers = $this->get_top_referrers($dates);

        return [
            'pageviews'       => $overview['stats']['pageviews'] ?? 0,
            'visits'          => $overview['stats']['visits'] ?? 0,
            'unique_visitors' => $overview['stats']['unique_visitors'] ?? 0,
            'bounce_rate'     => $overview['stats']['bounce_rate'] ?? 0,
            'top_pages'       => array_slice($top_pages, 0, 5),
            'top_referrers'   => array_slice($top_referrers, 0, 5),
        ];
    }

    /**
     * Called when module is activated
     * Schedules cron events
     */
    public function activate() {
        if (!wp_next_scheduled('switchboard_lean_analytics_cleanup')) {
            wp_schedule_event(time(), 'daily', 'switchboard_lean_analytics_cleanup');
        }
    }

    /**
     * Called when module is deactivated
     * Clears all scheduled cron events
     */
    public function deactivate() {
        wp_clear_scheduled_hook('switchboard_lean_analytics_cleanup');
    }
    
    /**
     * Get settings with defaults
     */
    private function get_settings() {
        $defaults = [
            'track_logged_in' => false,
            'exclude_roles' => ['administrator', 'editor'],
            'respect_dnt' => true,
            'country_detection' => true,
            'bot_filter' => true,
            'retention_days' => 180,
            'allow_editor_access' => false,
            'salt' => wp_generate_password(32, true, true)
        ];
        
        $options = get_option($this->option_name, []);
        
        // Ensure salt is always set
        if (empty($options['salt'])) {
            $options['salt'] = wp_generate_password(32, true, true);
            update_option($this->option_name, $options);
        }
        
        return wp_parse_args($options, $defaults);
    }

    /**
     * Check if current user can view analytics
     * Admins always have access. Editors have access only if setting is enabled.
     *
     * @return bool
     */
    private function can_view_analytics() {
        // Admins always have access
        if (current_user_can('manage_options')) {
            return true;
        }

        // Check if editor access is enabled (edit_others_posts = Editor role and above)
        if (!empty($this->settings['allow_editor_access']) && current_user_can('edit_others_posts')) {
            return true;
        }

        return false;
    }

    /**
     * Check if current user is an editor (not admin)
     * Used to determine menu placement
     *
     * @return bool
     */
    private function is_editor_user() {
        return !current_user_can('manage_options') && current_user_can('edit_others_posts');
    }

    /**
     * Save settings from Switchboard settings page
     */
    public function save_settings($module, $settings) {
        if ($module !== 'lean-analytics') {
            return;
        }
        
        $saved_settings = $this->get_settings();
        
        // Update settings
        $saved_settings['track_logged_in'] = isset($settings['track_logged_in']) ? true : false;
        $saved_settings['exclude_roles'] = isset($settings['exclude_roles']) ? (array) $settings['exclude_roles'] : [];
        $saved_settings['respect_dnt'] = isset($settings['respect_dnt']) ? true : false;
        $saved_settings['country_detection'] = isset($settings['country_detection']) ? true : false;
        $saved_settings['bot_filter'] = isset($settings['bot_filter']) ? true : false;
        $saved_settings['retention_days'] = isset($settings['retention_days']) ? intval($settings['retention_days']) : 180;
        $saved_settings['allow_editor_access'] = isset($settings['allow_editor_access']) ? true : false;
        
        update_option($this->option_name, $saved_settings);
        $this->settings = $saved_settings;
    }
    
    /**
     * Create database table
     */
    public function create_table() {
        global $wpdb;
        
        $charset_collate = $wpdb->get_charset_collate();
        
        // Check if MySQL version supports generated columns
        $mysql_version = $wpdb->db_version();
        $supports_generated = version_compare($mysql_version, '5.7.0', '>=');
        
        if ($supports_generated) {
            $sql = "CREATE TABLE IF NOT EXISTS {$this->table_name} (
                id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
                ts DATETIME NOT NULL,
                day DATE GENERATED ALWAYS AS (DATE(ts)) STORED,
                hour TINYINT GENERATED ALWAYS AS (HOUR(ts)) STORED,
                
                path VARCHAR(255) NOT NULL,
                full_url VARCHAR(255) DEFAULT NULL,
                url_hash CHAR(64) DEFAULT NULL,
                
                ref VARCHAR(255) DEFAULT '',
                ref_domain VARCHAR(128) DEFAULT NULL,
                
                utm_source VARCHAR(64) DEFAULT '',
                utm_medium VARCHAR(64) DEFAULT '',
                utm_campaign VARCHAR(128) DEFAULT '',
                
                device ENUM('mobile','tablet','desktop') DEFAULT NULL,
                country_code CHAR(2) DEFAULT NULL,
                
                post_id BIGINT UNSIGNED DEFAULT NULL,
                post_type VARCHAR(32) DEFAULT NULL,
                
                user_id BIGINT UNSIGNED DEFAULT NULL,
                event_type VARCHAR(32) DEFAULT 'pageview',
                
                visitor_day_key CHAR(64) NOT NULL,
                is_first_visit_day TINYINT(1) NOT NULL,
                
                PRIMARY KEY (id),
                KEY idx_day (day),
                KEY idx_day_hour (day, hour),
                KEY idx_day_path (day, path(191)),
                KEY idx_day_refdom (day, ref_domain),
                KEY idx_day_country (day, country_code),
                KEY idx_post (post_type, post_id),
                KEY idx_day_post (day, post_type, post_id),
                KEY idx_urlhash (url_hash),
                KEY idx_user_day (user_id, day),
                KEY idx_ts (ts),
                KEY idx_visitor_day (visitor_day_key, day)
            ) ENGINE=InnoDB $charset_collate;";
        } else {
            // Fallback for older MySQL without generated columns
            $sql = "CREATE TABLE IF NOT EXISTS {$this->table_name} (
                id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
                ts DATETIME NOT NULL,
                day DATE NOT NULL,
                hour TINYINT NOT NULL,
                
                path VARCHAR(255) NOT NULL,
                full_url VARCHAR(255) DEFAULT NULL,
                url_hash CHAR(64) DEFAULT NULL,
                
                ref VARCHAR(255) DEFAULT '',
                ref_domain VARCHAR(128) DEFAULT NULL,
                
                utm_source VARCHAR(64) DEFAULT '',
                utm_medium VARCHAR(64) DEFAULT '',
                utm_campaign VARCHAR(128) DEFAULT '',
                
                device ENUM('mobile','tablet','desktop') DEFAULT NULL,
                country_code CHAR(2) DEFAULT NULL,
                
                post_id BIGINT UNSIGNED DEFAULT NULL,
                post_type VARCHAR(32) DEFAULT NULL,
                
                user_id BIGINT UNSIGNED DEFAULT NULL,
                event_type VARCHAR(32) DEFAULT 'pageview',
                
                visitor_day_key CHAR(64) NOT NULL,
                is_first_visit_day TINYINT(1) NOT NULL,
                
                PRIMARY KEY (id),
                KEY idx_day (day),
                KEY idx_day_hour (day, hour),
                KEY idx_day_path (day, path(191)),
                KEY idx_day_refdom (day, ref_domain),
                KEY idx_day_country (day, country_code),
                KEY idx_post (post_type, post_id),
                KEY idx_day_post (day, post_type, post_id),
                KEY idx_urlhash (url_hash),
                KEY idx_user_day (user_id, day),
                KEY idx_ts (ts),
                KEY idx_visitor_day (visitor_day_key, day)
            ) ENGINE=InnoDB $charset_collate;";
        }
        
        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        dbDelta($sql);
    }
    
    /**
     * Maybe create table on admin init
     */
    public function maybe_create_table() {
        global $wpdb;
        if ($wpdb->get_var("SHOW TABLES LIKE '{$this->table_name}'") != $this->table_name) {
            $this->create_table();
        }
    }
    
    /**
     * Enqueue tracking beacon
     */
    public function enqueue_beacon() {
        // Check if tracking is enabled
        if (!$this->should_track()) {
            return;
        }
        
        wp_enqueue_script(
            'lean-analytics-beacon',
            plugins_url('../../assets/lean-analytics/beacon.js', __FILE__),
            [],
            '1.0.0',
            true
        );
        
        wp_localize_script('lean-analytics-beacon', 'leanAnalytics', [
            'ajaxUrl' => admin_url('admin-ajax.php'),
            'nonce' => wp_create_nonce('lean_track')
        ]);
    }
    
    /**
     * Check if should track current request
     */
    private function should_track() {
        // Bot filter
        if ($this->settings['bot_filter'] && $this->is_bot()) {
            return false;
        }
        
        // Respect DNT header
        if ($this->settings['respect_dnt'] && !empty($_SERVER['HTTP_DNT']) && $_SERVER['HTTP_DNT'] == '1') {
            return false;
        }
        
        // Check logged-in user
        if (is_user_logged_in() && !$this->settings['track_logged_in']) {
            return false;
        }
        
        // Check excluded roles
        if (is_user_logged_in() && $this->settings['track_logged_in']) {
            $user = wp_get_current_user();
            $excluded_roles = $this->settings['exclude_roles'];
            if (array_intersect($user->roles, $excluded_roles)) {
                return false;
            }
        }
        
        return true;
    }
    
    /**
     * Check if current request is from a bot
     */
    private function is_bot() {
        $ua = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
        
        if (empty($ua)) {
            return true; // No UA = likely bot
        }
        
        // Common bot patterns
        $bot_patterns = [
            'bot', 'crawler', 'spider', 'crawling', 'slurp', 'mediapartners',
            'adsbot', 'bingbot', 'googlebot', 'bingpreview', 'msnbot', 'duckduckbot',
            'teoma', 'slurp', 'yandex', 'baidu', 'facebookexternalhit', 'facebot',
            'ia_archiver', 'wget', 'curl', 'python-requests', 'http_request',
            'Go-http-client', 'Headless', 'PhantomJS', 'Scrapy'
        ];
        
        foreach ($bot_patterns as $pattern) {
            if (stripos($ua, $pattern) !== false) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Handle tracking request
     */
    public function handle_tracking() {
        check_ajax_referer('lean_track', 'nonce');
        
        if (!$this->should_track()) {
            wp_send_json_success();
            return;
        }
        
        // Get data from beacon
        $path = isset($_POST['path']) ? sanitize_text_field($_POST['path']) : '';
        $full_url = isset($_POST['url']) ? esc_url_raw($_POST['url']) : null;
        $ref = isset($_POST['ref']) ? esc_url_raw($_POST['ref']) : '';
        
        if (empty($path)) {
            wp_send_json_error(esc_html__('Missing path', 'wpswitchboard'));
            return;
        }
        
        // Parse UTM params
        $utm_source = isset($_POST['utm_source']) ? sanitize_text_field($_POST['utm_source']) : '';
        $utm_medium = isset($_POST['utm_medium']) ? sanitize_text_field($_POST['utm_medium']) : '';
        $utm_campaign = isset($_POST['utm_campaign']) ? sanitize_text_field($_POST['utm_campaign']) : '';
        
        // Detect device
        $device = $this->detect_device();
        
        // Detect country
        $country_code = $this->settings['country_detection'] ? $this->detect_country() : null;
        
        // Parse referrer domain
        $ref_domain = $ref ? $this->parse_domain($ref) : null;
        
        // Resolve post
        $post_data = $this->resolve_post($path);
        
        // Get user ID if logged in and tracking enabled
        $user_id = (is_user_logged_in() && $this->settings['track_logged_in']) ? get_current_user_id() : null;
        
        // Generate visitor day key
        $visitor_day_key = $this->generate_visitor_key();
        
        // Check if first visit of the day
        $is_first_visit = $this->is_first_visit_today($visitor_day_key);
        
        // URL hash
        $url_hash = $full_url ? hash('sha256', $full_url) : null;
        
        // Current timestamp
        $ts = current_time('mysql', true); // UTC
        
        // Check if MySQL supports generated columns
        global $wpdb;
        $mysql_version = $wpdb->db_version();
        $supports_generated = version_compare($mysql_version, '5.7.0', '>=');
        
        // Prepare data
        $data = [
            'ts' => $ts,
            'path' => $path,
            'full_url' => $full_url,
            'url_hash' => $url_hash,
            'ref' => $ref,
            'ref_domain' => $ref_domain,
            'utm_source' => $utm_source,
            'utm_medium' => $utm_medium,
            'utm_campaign' => $utm_campaign,
            'device' => $device,
            'country_code' => $country_code,
            'post_id' => $post_data['post_id'],
            'post_type' => $post_data['post_type'],
            'user_id' => $user_id,
            'visitor_day_key' => $visitor_day_key,
            'is_first_visit_day' => $is_first_visit ? 1 : 0
        ];
        
        // If no generated columns, set day and hour manually
        if (!$supports_generated) {
            $data['day'] = gmdate('Y-m-d', strtotime($ts));
            $data['hour'] = (int) gmdate('G', strtotime($ts));
        }
        
        // Insert
        $wpdb->insert($this->table_name, $data);
        
        wp_send_json_success();
    }
    
    /**
     * Generate visitor day key (privacy-preserving)
     */
    private function generate_visitor_key() {
        $ip = $_SERVER['REMOTE_ADDR'] ?? '';
        $ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
        $day = gmdate('Y-m-d');
        
        // Use only first 3 octets of IP for privacy
        $ip_parts = explode('.', $ip);
        $ip_prefix = isset($ip_parts[0], $ip_parts[1], $ip_parts[2]) 
            ? $ip_parts[0] . '.' . $ip_parts[1] . '.' . $ip_parts[2] 
            : $ip;
        
        $string = $ip_prefix . $ua . $day . $this->settings['salt'];
        return hash('sha256', $string);
    }
    
    /**
     * Check if first visit today (with transient cache)
     */
    private function is_first_visit_today($visitor_day_key) {
        global $wpdb;
        
        $day = gmdate('Y-m-d');
        $cache_key = 'lean_visit_' . substr($visitor_day_key, 0, 16) . '_' . $day;
        
        // Check transient cache (60s)
        if (get_transient($cache_key)) {
            return false;
        }
        
        // Check database
        $exists = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$this->table_name} 
            WHERE visitor_day_key = %s AND DATE(ts) = %s",
            $visitor_day_key,
            $day
        ));
        
        if ($exists > 0) {
            set_transient($cache_key, 1, 60);
            return false;
        }
        
        set_transient($cache_key, 1, 60);
        return true;
    }
    
    /**
     * Detect device type (mobile, tablet, desktop)
     */
    private function detect_device() {
        $ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
        
        // Check for tablets first (before mobile check)
        if (preg_match('/ipad|tablet|kindle|playbook|nexus 7|nexus 10|xoom|sch-i800|gt-p1000|gt-p7510|shw-m180s|sm-t|sgh-i987|zte-t/i', $ua)) {
            return 'tablet';
        }
        
        // Then check for mobile
        if (preg_match('/mobile|android|iphone|ipod|phone|blackberry|opera mini|mobile.*firefox|netfront|mqqbrowser|ucbrowser|series60/i', $ua)) {
            return 'mobile';
        }
        
        return 'desktop';
    }
    
    /**
     * Detect country from IP (basic implementation)
     */
    private function detect_country() {
        // Basic detection - can be enhanced with GeoIP database
        // For now, return null - can integrate with CloudFlare headers or GeoIP
        $country = $_SERVER['HTTP_CF_IPCOUNTRY'] ?? null; // CloudFlare
        
        if ($country && strlen($country) === 2) {
            return strtoupper($country);
        }
        
        return null;
    }
    
    /**
     * Parse domain from URL
     */
    private function parse_domain($url) {
        $host = parse_url($url, PHP_URL_HOST);
        if (!$host) {
            return null;
        }
        
        // Remove www.
        $domain = preg_replace('/^www\./i', '', $host);
        return strtolower($domain);
    }
    
    /**
     * Resolve post from path
     */
    private function resolve_post($path) {
        // Try to get post by URL
        $url = home_url($path);
        $post_id = url_to_postid($url);
        
        if ($post_id) {
            $post_type = get_post_type($post_id);
            return ['post_id' => $post_id, 'post_type' => $post_type];
        }
        
        return ['post_id' => null, 'post_type' => null];
    }
    
    /**
     * Add admin menu
     * For admins: Shows under Switchboard menu
     * For editors (if enabled): Shows under Dashboard menu
     */
    public function add_admin_menu() {
        // Always add for admins under Switchboard
        add_submenu_page(
            'switchboard',
            'Analytics',
            'Analytics',
            'manage_options',
            'lean-analytics',
            [$this, 'render_dashboard']
        );

        // Add under Dashboard for editors if setting is enabled
        // Use edit_others_posts capability - admins have this too but we handle that
        // by removing the menu item for admins after it's added
        if (!empty($this->settings['allow_editor_access'])) {
            add_submenu_page(
                'index.php',  // Dashboard parent
                'Analytics',
                'Analytics',
                'edit_others_posts',  // Editor capability
                'lean-analytics-editor',
                [$this, 'render_dashboard']
            );

            // Remove the Dashboard submenu for admins (they use Switchboard menu)
            if (current_user_can('manage_options')) {
                remove_submenu_page('index.php', 'lean-analytics-editor');
            }
        }
    }
    
    /**
     * Render dashboard
     */
    public function render_dashboard() {
        // Security check - only allow users who can view analytics
        if (!$this->can_view_analytics()) {
            wp_die(esc_html__('You do not have permission to access this page.', 'wpswitchboard'));
        }

        $dashboard_file = plugin_dir_path(__FILE__) . '../../assets/lean-analytics/dashboard.php';
        if (file_exists($dashboard_file)) {
            include $dashboard_file;
        }
    }
    
    /**
     * Enqueue admin assets
     */
    public function enqueue_admin_assets($hook) {
        // Only load on our analytics pages (admin or editor version)
        $page = isset($_GET['page']) ? $_GET['page'] : '';
        if (!in_array($page, ['lean-analytics', 'lean-analytics-editor'], true)) {
            return;
        }

        // Load main Switchboard admin styles (contains base color variables)
        wp_enqueue_style(
            'switchboard-admin',
            SWITCHBOARD_URL . 'switchboard-admin.css',
            [],
            SWITCHBOARD_VERSION
        );

        // Note: color-themes.css and swatch-selector.js are loaded by
        // assets/color-swatch-selector/enqueue.php on all switchboard pages

        wp_enqueue_style(
            'lean-analytics-admin',
            plugins_url('../../assets/lean-analytics/admin.css', __FILE__),
            ['switchboard-admin'],
            '1.0.3'
        );

        wp_register_script(
            'lean-analytics-admin',
            plugins_url('../../assets/lean-analytics/admin.js', __FILE__),
            ['jquery'],
            '1.0.5',
            true
        );

        // Localize before enqueuing
        $host = parse_url(home_url(), PHP_URL_HOST);
        // Use wpswitchboard.com for localhost/local dev environments
        if (in_array($host, array('localhost', '127.0.0.1')) || strpos($host, '.local') !== false || strpos($host, '.test') !== false) {
            $host = 'wpswitchboard.com';
        }
        wp_localize_script('lean-analytics-admin', 'leanAnalyticsAdmin', array(
            'ajaxUrl' => admin_url('admin-ajax.php'),
            'nonce' => wp_create_nonce('lean_analytics_admin'),
            'siteUrl' => $host,
            'canManage' => current_user_can('manage_options')  // false for editors = view-only
        ));

        // Now enqueue
        wp_enqueue_script('lean-analytics-admin');
    }
    
    /**
     * AJAX: Get analytics data
     * Allows editors if setting is enabled (view-only)
     */
    public function ajax_get_analytics_data() {
        check_ajax_referer('lean_analytics_admin', 'nonce');

        if (!$this->can_view_analytics()) {
            wp_send_json_error(esc_html__('Unauthorized', 'wpswitchboard'));
        }
        
        $range = isset($_POST['range']) ? sanitize_text_field($_POST['range']) : 'last_30_days';
        $metric = isset($_POST['metric']) ? sanitize_text_field($_POST['metric']) : 'overview';
        
        $dates = $this->get_date_range($range);
        
        $data = [];
        
        switch ($metric) {
            case 'overview':
                $data = $this->get_overview_data($dates);
                break;
            case 'pages':
                $data = $this->get_top_pages($dates);
                break;
            case 'referrers':
                $data = $this->get_top_referrers($dates);
                break;
            case 'countries':
                $data = $this->get_top_countries($dates);
                break;
            case 'posts':
                $data = $this->get_top_posts($dates);
                break;
            case 'other_content':
                $data = $this->get_top_other_content($dates);
                break;
            case 'hourly':
                $data = $this->get_hourly_data($dates);
                break;
            case 'campaigns':
                $data = $this->get_utm_campaigns($dates);
                break;
            case 'entry_pages':
                $data = $this->get_entry_pages($dates);
                break;
            case 'exit_pages':
                $data = $this->get_exit_pages($dates);
                break;
        }
        
        wp_send_json_success($data);
    }
    
    /**
     * Get date range
     */
    private function get_date_range($range) {
        $today = current_time('Y-m-d');
        $dates = ['from' => $today, 'to' => $today];
        
        switch ($range) {
            case 'today':
                break;
            
            case 'yesterday':
                $dates['from'] = $dates['to'] = date('Y-m-d', strtotime('-1 day'));
                break;
            
            case 'this_week':
                $dates['from'] = date('Y-m-d', strtotime('monday this week'));
                $dates['to'] = $today;
                break;
            
            case 'last_week':
                $dates['from'] = date('Y-m-d', strtotime('monday last week'));
                $dates['to'] = date('Y-m-d', strtotime('sunday last week'));
                break;
            
            case 'this_month':
                $dates['from'] = date('Y-m-01');
                $dates['to'] = $today;
                break;
            
            case 'last_month':
                $dates['from'] = date('Y-m-01', strtotime('first day of last month'));
                $dates['to'] = date('Y-m-t', strtotime('last day of last month'));
                break;
            
            case 'last_30_days':
                $dates['from'] = date('Y-m-d', strtotime('-30 days'));
                break;
            
            case 'last_90_days':
                $dates['from'] = date('Y-m-d', strtotime('-90 days'));
                break;
            
            case 'last_365_days':
                $dates['from'] = date('Y-m-d', strtotime('-365 days'));
                break;
            
            case 'this_year':
                $dates['from'] = date('Y-01-01');
                $dates['to'] = $today;
                break;
            
            case 'last_year':
                $dates['from'] = date('Y-01-01', strtotime('last year'));
                $dates['to'] = date('Y-12-31', strtotime('last year'));
                break;
            
            case 'all_time':
                global $wpdb;
                $min_date = $wpdb->get_var("SELECT MIN(DATE(ts)) FROM {$this->table_name}");
                $dates['from'] = $min_date ?: $today;
                break;
        }
        
        return $dates;
    }
    
    /**
     * Get overview data with advanced metrics
     */
    private function get_overview_data($dates) {
        global $wpdb;
        
        // Total stats
        $stats = $wpdb->get_row($wpdb->prepare(
            "SELECT 
                COUNT(*) as pageviews,
                SUM(is_first_visit_day) as visits,
                COUNT(DISTINCT visitor_day_key) as unique_visitors
            FROM {$this->table_name}
            WHERE DATE(ts) BETWEEN %s AND %s",
            $dates['from'],
            $dates['to']
        ), ARRAY_A);
        
        // Calculate derived metrics
        $visits = intval($stats['visits']);
        $pageviews = intval($stats['pageviews']);
        
        // Pages per visit
        $stats['pages_per_visit'] = $visits > 0 ? round($pageviews / $visits, 2) : 0;
        
        // Bounce rate (single page visits / total visits)
        // Count visitors who only have 1 pageview in the date range
        $single_page_visits = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(DISTINCT visitor_day_key)
            FROM (
                SELECT visitor_day_key, COUNT(*) as page_count
                FROM {$this->table_name}
                WHERE DATE(ts) BETWEEN %s AND %s
                GROUP BY visitor_day_key
                HAVING page_count = 1
            ) as single_visits",
            $dates['from'],
            $dates['to']
        ));

        // Total unique visitors in the date range
        $total_visitors = intval($stats['unique_visitors']);

        $stats['bounce_rate'] = $total_visitors > 0 ? round(($single_page_visits / $total_visitors) * 100, 1) : 0;
        
        // Daily series
        $series = $wpdb->get_results($wpdb->prepare(
            "SELECT 
                DATE(ts) as date,
                COUNT(*) as pageviews,
                SUM(is_first_visit_day) as visits
            FROM {$this->table_name}
            WHERE DATE(ts) BETWEEN %s AND %s
            GROUP BY DATE(ts)
            ORDER BY date ASC",
            $dates['from'],
            $dates['to']
        ), ARRAY_A);
        
        // Device breakdown
        $devices = $wpdb->get_results($wpdb->prepare(
            "SELECT 
                device,
                COUNT(*) as count
            FROM {$this->table_name}
            WHERE DATE(ts) BETWEEN %s AND %s AND device IS NOT NULL
            GROUP BY device",
            $dates['from'],
            $dates['to']
        ), ARRAY_A);
        
        return [
            'stats' => $stats,
            'series' => $series,
            'devices' => $devices
        ];
    }
    
    /**
     * Get top pages with URLs
     */
    private function get_top_pages($dates) {
        global $wpdb;

        $results = $wpdb->get_results($wpdb->prepare(
            "SELECT
                post_id,
                post_type,
                COUNT(*) as pageviews,
                SUM(is_first_visit_day) as visits
            FROM {$this->table_name}
            WHERE DATE(ts) BETWEEN %s AND %s AND post_id IS NOT NULL AND post_type = 'page'
            GROUP BY post_id, post_type
            ORDER BY pageviews DESC
            LIMIT 50",
            $dates['from'],
            $dates['to']
        ), ARRAY_A);

        // Enrich with page data
        foreach ($results as &$result) {
            $post = get_post($result['post_id']);
            if ($post) {
                $result['path'] = str_replace(home_url(), '', get_permalink($post->ID));
                $result['page_url'] = get_permalink($post->ID);
                $result['post_title'] = $post->post_title;
            } else {
                $result['path'] = '';
                $result['page_url'] = '';
                $result['post_title'] = 'Page #' . $result['post_id'];
            }
        }

        return $results;
    }
    
    /**
     * Get top referrers
     */
    private function get_top_referrers($dates) {
        global $wpdb;
        
        return $wpdb->get_results($wpdb->prepare(
            "SELECT 
                ref_domain,
                COUNT(*) as pageviews,
                SUM(is_first_visit_day) as visits
            FROM {$this->table_name}
            WHERE DATE(ts) BETWEEN %s AND %s AND ref_domain IS NOT NULL
            GROUP BY ref_domain
            ORDER BY pageviews DESC
            LIMIT 50",
            $dates['from'],
            $dates['to']
        ), ARRAY_A);
    }
    
    /**
     * Get top countries
     */
    private function get_top_countries($dates) {
        global $wpdb;
        
        return $wpdb->get_results($wpdb->prepare(
            "SELECT 
                country_code,
                COUNT(*) as pageviews,
                SUM(is_first_visit_day) as visits
            FROM {$this->table_name}
            WHERE DATE(ts) BETWEEN %s AND %s AND country_code IS NOT NULL
            GROUP BY country_code
            ORDER BY pageviews DESC
            LIMIT 50",
            $dates['from'],
            $dates['to']
        ), ARRAY_A);
    }
    
    /**
     * Get top posts with titles and URLs
     */
    private function get_top_posts($dates) {
        global $wpdb;

        $results = $wpdb->get_results($wpdb->prepare(
            "SELECT
                post_id,
                post_type,
                COUNT(*) as pageviews,
                SUM(is_first_visit_day) as visits
            FROM {$this->table_name}
            WHERE DATE(ts) BETWEEN %s AND %s AND post_id IS NOT NULL AND post_type = 'post'
            GROUP BY post_id, post_type
            ORDER BY pageviews DESC
            LIMIT 50",
            $dates['from'],
            $dates['to']
        ), ARRAY_A);
        
        // Enrich with post data
        foreach ($results as &$result) {
            $post = get_post($result['post_id']);
            if ($post) {
                $result['post_title'] = $post->post_title;
                $result['post_url'] = get_permalink($post->ID);
            } else {
                $result['post_title'] = 'Post #' . $result['post_id'];
                $result['post_url'] = '';
            }
        }

        return $results;
    }

    /**
     * Get top other content (custom post types, excluding posts and pages)
     */
    private function get_top_other_content($dates) {
        global $wpdb;

        $results = $wpdb->get_results($wpdb->prepare(
            "SELECT
                post_id,
                post_type,
                COUNT(*) as pageviews,
                SUM(is_first_visit_day) as visits
            FROM {$this->table_name}
            WHERE DATE(ts) BETWEEN %s AND %s AND post_id IS NOT NULL AND post_type NOT IN ('post', 'page')
            GROUP BY post_id, post_type
            ORDER BY pageviews DESC
            LIMIT 50",
            $dates['from'],
            $dates['to']
        ), ARRAY_A);

        // Enrich with post data
        foreach ($results as &$result) {
            $post = get_post($result['post_id']);
            if ($post) {
                $result['post_title'] = $post->post_title;
                $result['post_url'] = get_permalink($post->ID);
                // Get human-readable post type label
                $post_type_obj = get_post_type_object($result['post_type']);
                $result['post_type_label'] = $post_type_obj ? $post_type_obj->labels->singular_name : $result['post_type'];
            } else {
                $result['post_title'] = 'Content #' . $result['post_id'];
                $result['post_url'] = '';
                $result['post_type_label'] = $result['post_type'];
            }
        }

        return $results;
    }

    /**
     * Get hourly data (today only)
     */
    private function get_hourly_data($dates) {
        global $wpdb;
        
        return $wpdb->get_results($wpdb->prepare(
            "SELECT 
                HOUR(ts) as hour,
                COUNT(*) as pageviews,
                SUM(is_first_visit_day) as visits
            FROM {$this->table_name}
            WHERE DATE(ts) = %s
            GROUP BY HOUR(ts)
            ORDER BY hour ASC",
            $dates['to']
        ), ARRAY_A);
    }
    
    /**
     * AJAX: Cleanup old data
     */
    public function ajax_cleanup() {
        check_ajax_referer('lean_analytics_admin', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(esc_html__('Unauthorized', 'wpswitchboard'));
        }

        $action = isset($_POST['cleanup_action']) ? sanitize_text_field($_POST['cleanup_action']) : '';

        if ($action === 'preview') {
            $cutoff = isset($_POST['cutoff']) ? sanitize_text_field($_POST['cutoff']) : '';
            $data = $this->preview_cleanup($cutoff);
            wp_send_json_success($data);
        } elseif ($action === 'execute') {
            $cutoff = isset($_POST['cutoff']) ? sanitize_text_field($_POST['cutoff']) : '';
            $deleted = $this->execute_cleanup($cutoff);
            wp_send_json_success(['deleted' => $deleted]);
        } else {
            wp_send_json_error(esc_html__('Invalid action', 'wpswitchboard'));
        }
    }
    
    /**
     * Preview cleanup
     */
    private function preview_cleanup($cutoff) {
        global $wpdb;
        
        $count = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$this->table_name} WHERE DATE(ts) < %s",
            $cutoff
        ));
        
        // Estimate size (avg 0.7 KB per row including indexes)
        $size_mb = ($count * 0.7) / 1024;
        
        return [
            'count' => $count,
            'size_mb' => round($size_mb, 2)
        ];
    }
    
    /**
     * Execute cleanup (batched)
     */
    private function execute_cleanup($cutoff) {
        global $wpdb;
        
        $batch_size = 50000;
        $total_deleted = 0;
        
        do {
            $deleted = $wpdb->query($wpdb->prepare(
                "DELETE FROM {$this->table_name} WHERE DATE(ts) < %s LIMIT %d",
                $cutoff,
                $batch_size
            ));
            
            $total_deleted += $deleted;
            
            // Small delay to reduce lock contention
            usleep(100000); // 100ms
            
        } while ($deleted === $batch_size);
        
        return $total_deleted;
    }
    
    /**
     * Get UTM campaign data
     */
    private function get_utm_campaigns($dates) {
        global $wpdb;
        
        return $wpdb->get_results($wpdb->prepare(
            "SELECT 
                utm_source,
                utm_medium,
                utm_campaign,
                COUNT(*) as pageviews,
                SUM(is_first_visit_day) as visits
            FROM {$this->table_name}
            WHERE DATE(ts) BETWEEN %s AND %s 
            AND (utm_source != '' OR utm_medium != '' OR utm_campaign != '')
            GROUP BY utm_source, utm_medium, utm_campaign
            ORDER BY pageviews DESC
            LIMIT 50",
            $dates['from'],
            $dates['to']
        ), ARRAY_A);
    }
    
    /**
     * Get entry pages (first page in a visit)
     */
    private function get_entry_pages($dates) {
        global $wpdb;
        
        return $wpdb->get_results($wpdb->prepare(
            "SELECT 
                path,
                COUNT(*) as entries,
                SUM(is_first_visit_day) as new_visitors
            FROM {$this->table_name}
            WHERE DATE(ts) BETWEEN %s AND %s 
            AND is_first_visit_day = 1
            GROUP BY path
            ORDER BY entries DESC
            LIMIT 50",
            $dates['from'],
            $dates['to']
        ), ARRAY_A);
    }
    
    /**
     * Get exit pages (last page before leaving)
     */
    private function get_exit_pages($dates) {
        global $wpdb;
        
        // Try MySQL 8.0+ approach with ROW_NUMBER()
        $supports_window_functions = version_compare($wpdb->db_version(), '8.0', '>=');
        
        if ($supports_window_functions) {
            return $wpdb->get_results($wpdb->prepare(
                "SELECT 
                    path,
                    COUNT(*) as exits
                FROM (
                    SELECT 
                        visitor_day_key,
                        path,
                        ROW_NUMBER() OVER (PARTITION BY visitor_day_key ORDER BY ts DESC) as rn
                    FROM {$this->table_name}
                    WHERE DATE(ts) BETWEEN %s AND %s
                ) as ranked
                WHERE rn = 1
                GROUP BY path
                ORDER BY exits DESC
                LIMIT 50",
                $dates['from'],
                $dates['to']
            ), ARRAY_A);
        } else {
            // Fallback for MySQL 5.7 and earlier
            return $wpdb->get_results($wpdb->prepare(
                "SELECT 
                    path,
                    COUNT(*) as exits
                FROM {$this->table_name} t1
                WHERE DATE(ts) BETWEEN %s AND %s
                AND ts = (
                    SELECT MAX(ts) 
                    FROM {$this->table_name} t2 
                    WHERE t2.visitor_day_key = t1.visitor_day_key
                    AND DATE(t2.ts) BETWEEN %s AND %s
                )
                GROUP BY path
                ORDER BY exits DESC
                LIMIT 50",
                $dates['from'],
                $dates['to'],
                $dates['from'],
                $dates['to']
            ), ARRAY_A);
        }
    }
    
    /**
     * Auto cleanup based on retention setting
     */
    public function auto_cleanup() {
        $retention_days = $this->settings['retention_days'];
        if ($retention_days <= 0) {
            return;
        }
        
        $cutoff = date('Y-m-d', strtotime("-{$retention_days} days"));
        $this->execute_cleanup($cutoff);
    }
    
    /**
     * AJAX: Get real-time data
     * Allows editors if setting is enabled (view-only)
     */
    public function ajax_get_realtime_data() {
        check_ajax_referer('lean_analytics_admin', 'nonce');

        if (!$this->can_view_analytics()) {
            wp_send_json_error(esc_html__('Unauthorized', 'wpswitchboard'));
        }

        global $wpdb;

        // Get visitors active in the last 5 minutes
        $active_threshold = gmdate('Y-m-d H:i:s', strtotime('-5 minutes'));

        // Count unique active visitors
        $active_visitors = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(DISTINCT visitor_day_key)
            FROM {$this->table_name}
            WHERE ts >= %s",
            $active_threshold
        ));

        // Get current pages being viewed (last 5 minutes)
        $active_pages = $wpdb->get_results($wpdb->prepare(
            "SELECT
                path,
                COUNT(DISTINCT visitor_day_key) as visitors,
                MAX(ts) as last_view
            FROM {$this->table_name}
            WHERE ts >= %s
            GROUP BY path
            ORDER BY visitors DESC, last_view DESC
            LIMIT 10",
            $active_threshold
        ), ARRAY_A);

        // Add page titles for active pages
        foreach ($active_pages as &$page) {
            $url = home_url($page['path']);
            $post_id = url_to_postid($url);
            if ($post_id) {
                $page['title'] = get_the_title($post_id);
            } else {
                // Try to make the path more readable
                $page['title'] = $page['path'] === '/' ? 'Homepage' : ucwords(str_replace(['/', '-', '_'], ' ', trim($page['path'], '/')));
            }
            $page['seconds_ago'] = time() - strtotime($page['last_view']);
        }

        // Get pageviews in last 30 minutes (for sparkline)
        $pageviews_30min = $wpdb->get_results($wpdb->prepare(
            "SELECT
                CONCAT(
                    DATE_FORMAT(ts, '%%H:'),
                    LPAD(FLOOR(MINUTE(ts) / 5) * 5, 2, '0')
                ) as time_bucket,
                COUNT(*) as pageviews
            FROM {$this->table_name}
            WHERE ts >= %s
            GROUP BY time_bucket
            ORDER BY time_bucket ASC",
            gmdate('Y-m-d H:i:s', strtotime('-30 minutes'))
        ), ARRAY_A);

        wp_send_json_success([
            'active_visitors' => intval($active_visitors),
            'active_pages' => $active_pages,
            'pageviews_30min' => $pageviews_30min,
            'timestamp' => current_time('mysql')
        ]);
    }

    /**
     * AJAX: Export CSV
     */
    public function ajax_export() {
        check_ajax_referer('lean_analytics_admin', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_die(esc_html__('Unauthorized', 'wpswitchboard'));
        }
        
        $range = isset($_GET['range']) ? sanitize_text_field($_GET['range']) : 'last_30_days';
        $metric = isset($_GET['metric']) ? sanitize_text_field($_GET['metric']) : 'pages';
        
        $dates = $this->get_date_range($range);
        
        // Get data based on metric
        $data = [];
        $filename = 'lean-analytics-' . $metric . '-' . $dates['from'] . '-to-' . $dates['to'] . '.csv';
        
        switch ($metric) {
            case 'pages':
                $data = $this->get_top_pages($dates);
                $headers = ['Path', 'Pageviews', 'Visits'];
                $fields = ['path', 'pageviews', 'visits'];
                break;
            case 'referrers':
                $data = $this->get_top_referrers($dates);
                $headers = ['Domain', 'Pageviews', 'Visits'];
                $fields = ['ref_domain', 'pageviews', 'visits'];
                break;
            case 'countries':
                $data = $this->get_top_countries($dates);
                $headers = ['Country', 'Pageviews', 'Visits'];
                $fields = ['country_code', 'pageviews', 'visits'];
                break;
            case 'posts':
                $data = $this->get_top_posts($dates);
                $headers = ['Post ID', 'Post Type', 'Pageviews', 'Visits'];
                $fields = ['post_id', 'post_type', 'pageviews', 'visits'];
                break;
            default:
                wp_die(esc_html__('Invalid metric', 'wpswitchboard'));
        }
        
        // Output CSV
        header('Content-Type: text/csv');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        
        $output = fopen('php://output', 'w');
        fputcsv($output, $headers);
        
        foreach ($data as $row) {
            $csv_row = [];
            foreach ($fields as $field) {
                $csv_row[] = $row[$field] ?? '';
            }
            fputcsv($output, $csv_row);
        }
        
        fclose($output);
        exit;
    }

    /**
     * Render settings panel
     *
     * @param array $settings Current module settings
     * @return void Outputs HTML directly
     */
    public function render_settings($settings = []) {
        $track_logged_in = isset($settings['track_logged_in']) ? $settings['track_logged_in'] : false;
        $exclude_roles = isset($settings['exclude_roles']) ? $settings['exclude_roles'] : ['administrator', 'editor'];
        $respect_dnt = isset($settings['respect_dnt']) ? $settings['respect_dnt'] : true;
        $country_detection = isset($settings['country_detection']) ? $settings['country_detection'] : true;
        $bot_filter = isset($settings['bot_filter']) ? $settings['bot_filter'] : true;
        $retention_days = isset($settings['retention_days']) ? intval($settings['retention_days']) : 180;
        $allow_editor_access = isset($settings['allow_editor_access']) ? $settings['allow_editor_access'] : false;

        $all_roles = wp_roles()->roles;

        ?>
        <input type="hidden" name="module" value="lean-analytics">

        <div class="sb-sp-info-box sb-sp-info-box-info" style="margin-bottom: 24px;">
            <h4 class="sb-sp-heading-sm">Privacy-First Analytics</h4>
            <p class="sb-sp-description" style="margin: 0;">Track pageviews, visits, and user behavior without storing personal data, cookies, or requiring consent for GDPR compliance. All data is stored in a single database table.</p>
        </div>

        <div class="sb-sp-group" style="margin-bottom: 24px;">
            <h3 class="sb-sp-heading">Tracking Settings</h3>
            
            <div style="display: flex; flex-direction: column; gap: 12px; margin-top: 16px;">
                <div class="sb-module-option-item sb-module-toggle-item">
                    <div class="sb-module-option-content">
                        <div class="sb-module-toggle-header">
                            <strong class="sb-module-option-title"><?php esc_html_e('Respect Do Not Track (DNT)', 'wpswitchboard'); ?></strong>
                            <label class="sb-module-toggle">
                                <input type="checkbox" name="setting_respect_dnt" value="1" <?php checked($respect_dnt, true); ?>>
                                <span class="sb-module-toggle-slider"></span>
                            </label>
                        </div>
                        <p class="sb-module-option-text"><?php esc_html_e('Don\'t track users who have DNT enabled in their browser', 'wpswitchboard'); ?></p>
                    </div>
                </div>

                <div class="sb-module-option-item sb-module-toggle-item">
                    <div class="sb-module-option-content">
                        <div class="sb-module-toggle-header">
                            <strong class="sb-module-option-title"><?php esc_html_e('Filter Bots & Crawlers', 'wpswitchboard'); ?></strong>
                            <label class="sb-module-toggle">
                                <input type="checkbox" name="setting_bot_filter" value="1" <?php checked($bot_filter, true); ?>>
                                <span class="sb-module-toggle-slider"></span>
                            </label>
                        </div>
                        <p class="sb-module-option-text"><?php esc_html_e('Exclude known bots from analytics (recommended)', 'wpswitchboard'); ?></p>
                    </div>
                </div>

                <div class="sb-module-option-item sb-module-toggle-item">
                    <div class="sb-module-option-content">
                        <div class="sb-module-toggle-header">
                            <strong class="sb-module-option-title"><?php esc_html_e('Country Detection', 'wpswitchboard'); ?></strong>
                            <label class="sb-module-toggle">
                                <input type="checkbox" name="setting_country_detection" value="1" <?php checked($country_detection, true); ?>>
                                <span class="sb-module-toggle-slider"></span>
                            </label>
                        </div>
                        <p class="sb-module-option-text"><?php esc_html_e('Detect visitor country (requires CloudFlare or similar CDN service with geo headers)', 'wpswitchboard'); ?></p>
                    </div>
                </div>
            </div>
        </div>

        <hr class="sb-sp-divider">

        <div class="sb-sp-group" style="margin-bottom: 24px;">
            <h3 class="sb-sp-heading">Logged-In User Tracking</h3>
            
            <div style="display: flex; flex-direction: column; gap: 12px; margin-top: 16px;">
                <div class="sb-module-option-item sb-module-toggle-item">
                    <div class="sb-module-option-content">
                        <div class="sb-module-toggle-header">
                            <strong class="sb-module-option-title"><?php esc_html_e('Track Logged-In Users', 'wpswitchboard'); ?></strong>
                            <label class="sb-module-toggle">
                                <input type="checkbox" name="setting_track_logged_in" value="1" <?php checked($track_logged_in, true); ?> id="track_logged_in_checkbox">
                                <span class="sb-module-toggle-slider"></span>
                            </label>
                        </div>
                        <p class="sb-module-option-text"><?php esc_html_e('Include logged-in users in analytics (user ID will be stored)', 'wpswitchboard'); ?></p>
                    </div>
                </div>
            </div>

            <div id="exclude_roles_section" style="margin-top: 16px; <?php echo !$track_logged_in ? 'display: none;' : ''; ?>">
                <label class="sb-sp-label"><?php esc_html_e('Exclude Roles from Tracking:', 'wpswitchboard'); ?></label>
                <div style="display: flex; flex-direction: column; gap: 12px; margin-top: 12px;">

        <?php foreach ($all_roles as $role_slug => $role_data) {
            $checked = in_array($role_slug, $exclude_roles) ? 'checked' : '';
            ?>
                        <div class="sb-module-option-item sb-module-toggle-item">
                            <div class="sb-module-option-content">
                                <div class="sb-module-toggle-header">
                                    <strong class="sb-module-option-title"><?php echo esc_html($role_data['name']); ?></strong>
                                    <label class="sb-module-toggle">
                                        <input type="checkbox" name="setting_exclude_roles[]" value="<?php echo esc_attr($role_slug); ?>" <?php echo $checked; ?>>
                                        <span class="sb-module-toggle-slider"></span>
                                    </label>
                                </div>
                            </div>
                        </div>
            <?php
        }
        ?>
                </div>
            </div>
        </div>

        <hr class="sb-sp-divider">

        <div class="sb-sp-group" style="margin-bottom: 24px;">
            <h3 class="sb-sp-heading">Data Retention</h3>
        </div>

        <div class="sb-sp-group sb-sp-field-horizontal" style="margin-bottom: 24px;">
            <div class="sb-sp-field-horizontal-left">
                <label for="setting_retention_days" class="sb-sp-label"><?php esc_html_e('Auto-Delete Data Older Than', 'wpswitchboard'); ?></label>
                <p class="sb-sp-description"><?php esc_html_e('Set to 0 to keep data forever. Runs daily cleanup automatically.', 'wpswitchboard'); ?></p>
            </div>
            <div style="display: flex; align-items: center; gap: 8px;">
                <input type="number" name="setting_retention_days" id="setting_retention_days" value="<?php echo esc_attr($retention_days); ?>" min="0" max="3650" class="sb-sp-input" style="width: 120px;">
                <span class="sb-sp-text"><?php esc_html_e('days', 'wpswitchboard'); ?></span>
            </div>
        </div>

        <hr class="sb-sp-divider">

        <div class="sb-sp-group" style="margin-bottom: 24px;">
            <h3 class="sb-sp-heading">Access Permissions</h3>

            <div style="display: flex; flex-direction: column; gap: 12px; margin-top: 16px;">
                <div class="sb-module-option-item sb-module-toggle-item">
                    <div class="sb-module-option-content">
                        <div class="sb-module-toggle-header">
                            <strong class="sb-module-option-title"><?php esc_html_e('Allow Editor Access', 'wpswitchboard'); ?></strong>
                            <label class="sb-module-toggle">
                                <input type="checkbox" name="setting_allow_editor_access" value="1" <?php checked($allow_editor_access, true); ?>>
                                <span class="sb-module-toggle-slider"></span>
                            </label>
                        </div>
                        <p class="sb-module-option-text"><?php esc_html_e('Allow users with Editor role to view analytics (view-only, no export or data cleanup). Analytics will appear under Dashboard menu for editors.', 'wpswitchboard'); ?></p>
                    </div>
                </div>
            </div>
        </div>

        <div class="sb-sp-info-box sb-sp-info-box-info">
            <h4 class="sb-sp-heading-sm">Privacy Information</h4>
            <ul class="sb-sp-list">
                <li class="sb-sp-list-item"><?php esc_html_e('No raw IP addresses or User-Agent strings are stored', 'wpswitchboard'); ?></li>
                <li class="sb-sp-list-item"><?php esc_html_e('Visitor identification uses a daily-rotated privacy-preserving hash', 'wpswitchboard'); ?></li>
                <li class="sb-sp-list-item"><?php esc_html_e('Only page paths, referrer domains, and device types are tracked', 'wpswitchboard'); ?></li>
                <li class="sb-sp-list-item"><?php esc_html_e('All tracking is first-party (no external services)', 'wpswitchboard'); ?></li>
                <li class="sb-sp-list-item"><?php esc_html_e('Full GDPR & privacy regulation compliance', 'wpswitchboard'); ?></li>
            </ul>
        </div>

        <div class="sb-sp-info-box sb-sp-info-box-tip" style="margin-top: 24px;">
            <h4 class="sb-sp-heading-sm">View Analytics</h4>
            <p class="sb-sp-description" style="margin: 0;"><?php printf(esc_html__('After enabling, visit %s to view your reports and insights.', 'wpswitchboard'), '<strong>Switchboard → Analytics</strong>'); ?></p>
        </div>

        <script>
        jQuery(document).ready(function($) {
            $("#track_logged_in_checkbox").on("change", function() {
                if ($(this).is(":checked")) {
                    $("#exclude_roles_section").slideDown();
                } else {
                    $("#exclude_roles_section").slideUp();
                }
            });
        });
        </script>
        <?php

    }

    /**
     * Register data locations for cleanup system
     *
     * @return array Data storage locations
     */
    public function register_data_locations() {
        return [
            'options' => [
                'switchboard_lean_analytics',
            ],
            'option_patterns' => [
                '_transient_lean_visit_%',
                '_transient_timeout_lean_visit_%',
                '_transient_lean_geo_%',
                '_transient_timeout_lean_geo_%',
            ],
            'tables' => [
                'lean_events',
            ],
            'post_meta' => [],
            'user_meta' => [],
        ];
    }
}

// Initialize and register with data system
// Make instance globally accessible for settings renderer
global $SwitchboardLeanAnalytics;
$SwitchboardLeanAnalytics = new SwitchboardLeanAnalytics();
Switchboard_Data_Registry::get_instance()->auto_register($SwitchboardLeanAnalytics, __FILE__);
