<?php

/**
 * Plugin Name: Sentinel - WordPress Activity Logger
 * Plugin URI: https://www.getsentinel.dev/
 * Description: Comprehensive WordPress activity logging and monitoring system. Track user actions, system events, and security incidents with detailed reporting and real-time monitoring.
 * Version: 1.1.4
 * Author: Scarlett Media Lab
 * Author URI: https://www.scarlettmedialab.com/
 * License: GPL v2 or later
 * Text Domain: sentinel
 * Domain Path: /languages
 * Requires at least: 5.0
 * Tested up to: 6.8
 * Requires PHP: 7.4
 */

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

// Define plugin constants
define('SENTINEL_VERSION', '1.1.4');
define('SENTINEL_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('SENTINEL_PLUGIN_URL', plugin_dir_url(__FILE__));
define('SENTINEL_PLUGIN_BASENAME', plugin_basename(__FILE__));
define('SENTINEL_SITE_URL', 'https://www.getsentinel.dev/');

// Include required files
require_once SENTINEL_PLUGIN_DIR . 'includes/class-sentinel-migration.php';
require_once SENTINEL_PLUGIN_DIR . 'includes/class-sentinel-onboarding.php';
require_once SENTINEL_PLUGIN_DIR . 'includes/class-sentinel-onboarding-presets.php';
require_once SENTINEL_PLUGIN_DIR . 'includes/class-sentinel-core.php';
require_once SENTINEL_PLUGIN_DIR . 'includes/class-sentinel-events.php';
require_once SENTINEL_PLUGIN_DIR . 'includes/class-sentinel-logger.php';
require_once SENTINEL_PLUGIN_DIR . 'admin/class-sentinel-admin.php';
require_once SENTINEL_PLUGIN_DIR . 'includes/class-sentinel-cleanup.php';
require_once SENTINEL_PLUGIN_DIR . 'includes/class-sentinel-digest-reports.php';
require_once SENTINEL_PLUGIN_DIR . 'includes/class-sentinel-email-templates.php';
require_once SENTINEL_PLUGIN_DIR . 'includes/class-sentinel-privacy.php';
require_once SENTINEL_PLUGIN_DIR . 'includes/class-sentinel-public-stats.php';
require_once SENTINEL_PLUGIN_DIR . 'includes/class-sentinel-rest-api.php';
require_once SENTINEL_PLUGIN_DIR . 'includes/class-sentinel-file-monitor.php';
require_once SENTINEL_PLUGIN_DIR . 'includes/class-sentinel-integrations.php';
require_once SENTINEL_PLUGIN_DIR . 'includes/class-sentinel-auth-counters.php';
require_once SENTINEL_PLUGIN_DIR . 'includes/class-sentinel-incidents.php';



/**
 * Premium License Helper Functions
 */

/**
 * Check if Sentinel Premium is active
 */
function sentinel_is_premium()
{
    $license_key = get_option('sentinel_premium_license', '');

    // Early return if no license key
    if (empty($license_key) || strlen($license_key) < 10) {
        return false;
    }

    // Check cached validation result first (avoid excessive API calls)
    $cache_key = 'sentinel_license_valid_' . md5($license_key . get_home_url());
    $cached_result = get_transient($cache_key);

    if ($cached_result !== false) {
        $is_premium = ($cached_result === 'valid');
        return $is_premium;
    }

    // Validate license with server
    $validation_result = sentinel_validate_license_with_server($license_key);

    // Cache result for 12 hours (success) or 1 hour (failure)
    $cache_duration = $validation_result ? (12 * HOUR_IN_SECONDS) : HOUR_IN_SECONDS;
    set_transient($cache_key, ($validation_result ? 'valid' : 'invalid'), $cache_duration);

    return $validation_result;
}

/**
 * Validate license key with getsentinel.dev server
 * This is a new helper function for the enhanced validation
 */
function sentinel_validate_license_with_server($license_key)
{
    // Never assume validity - return false if server is unreachable
    $fallback_valid = false;

    // Host server (getsentinel.dev) bypass: allow the license server to activate premium on itself
    // without self-pinging (avoids DB errors and rate limits). In wp-config on the live server add:
    // define( 'SENTINEL_SITE_LICENSE_KEY', 'your-key' );
    // Then in Sentinel → Settings enter the same key and save.
    if (defined('SENTINEL_SITE_LICENSE_KEY')) {
        $site_key = trim(constant('SENTINEL_SITE_LICENSE_KEY'));
        if ($site_key !== '') {
            $home = get_home_url();
            $current_host = parse_url($home, PHP_URL_HOST);
            if (empty($current_host) && $home) {
                $current_host = preg_replace('#^https?://#i', '', $home);
                $current_host = strtok($current_host, '/');
            }
            if (!empty($current_host)) {
                $current_host = strtolower($current_host);
                $is_license_server = (
                    $current_host === 'getsentinel.dev'
                    || $current_host === 'www.getsentinel.dev'
                    || substr($current_host, -strlen('.getsentinel.dev')) === '.getsentinel.dev'
                    || (defined('SENTINEL_SITE_URL') && strtolower((string) parse_url(SENTINEL_SITE_URL, PHP_URL_HOST)) === $current_host)
                );
                if ($is_license_server) {
                    $input_key = trim($license_key);
                    $input_key = str_replace(["\xe2\x80\x93", "\xe2\x80\x94", "\xc2\xad"], '-', $input_key);
                    $input_key = preg_replace('/\s+/', '', $input_key);
                    $config_key = preg_replace('/\s+/', '', $site_key);
                    $normalize = function ($k) {
                        $k = str_replace(['O', 'o'], '0', $k);
                        return str_replace(['I', 'l'], '1', $k);
                    };
                    $is_valid = ($normalize($input_key) === $normalize($config_key));
                    return $is_valid;
                }
            }
        }
    }

    // Determine validation endpoint based on environment
    $current_domain = get_home_url();
    $is_local = (
        strpos($current_domain, 'localhost') !== false ||
        strpos($current_domain, '.local') !== false ||
        strpos($current_domain, '127.0.0.1') !== false ||
        strpos($current_domain, '192.168.') !== false ||
        strpos($current_domain, '10.0.') !== false ||
        defined('WP_DEBUG') && WP_DEBUG
    );

    // Always use production endpoint for license validation
    // Local license validation is not needed - licenses must be validated against the license server
    $validation_url = 'https://getsentinel.dev/wp-json/sentinel/v1/validate-license';
    $request_body = array(
        'license_key' => sanitize_text_field($license_key),
        'domain' => get_home_url(),
        'version' => SENTINEL_VERSION
    );

    // Configure request settings for production license server
    $request_settings = array(
        'body' => $request_body,
        'headers' => array(
            'Content-Type' => 'application/x-www-form-urlencoded',
            'User-Agent' => 'Sentinel-Plugin/' . SENTINEL_VERSION . ' ' . get_home_url()
        ),
        'timeout' => 15, // Increased timeout for better reliability
        'sslverify' => true,
        'blocking' => true
    );

    // Make request with timeout and error handling
    $response = wp_remote_post($validation_url, $request_settings);

    // Handle request errors (network issues, timeouts, etc.)
    if (is_wp_error($response)) {
        error_log('[Sentinel] License validation failed - WP Error: ' . $response->get_error_message());
        return $fallback_valid;
    }

    // Check response code
    $response_code = wp_remote_retrieve_response_code($response);
    if ($response_code !== 200) {
        error_log('[Sentinel] License validation failed - HTTP ' . $response_code);
        return $fallback_valid;
    }

    // Parse response
    $response_body = wp_remote_retrieve_body($response);
    $data = json_decode($response_body, true);

    // Validate response structure
    if (!is_array($data) || !isset($data['valid'])) {
        error_log('[Sentinel] License validation failed - Invalid response structure');
        return $fallback_valid;
    }

    // Return server validation result
    $server_valid = (bool) $data['valid'];

    // Optional: Store additional license info if provided
    if ($server_valid && isset($data['license_info'])) {
        update_option('sentinel_license_info', $data['license_info']);
    }

    return $server_valid;
}

/**
 * Clear license validation cache when license key changes
 */
function sentinel_clear_license_cache()
{
    // Clear specific license cache for current license key and domain
    $license_key = get_option('sentinel_premium_license', '');
    if (!empty($license_key)) {
        $cache_key = 'sentinel_license_valid_' . md5($license_key . get_home_url());
        delete_transient($cache_key);

        // Clear timeout transient too
        delete_transient('_transient_timeout_' . $cache_key);
    }

    // Also clear all license validation caches as backup
    global $wpdb;
    $cache_prefix = 'sentinel_license_valid_';
    $wpdb->query($wpdb->prepare(
        "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
        '%' . $wpdb->esc_like('_transient_' . $cache_prefix) . '%'
    ));
}

// Clear cache whenever the license option is updated
add_action('update_option_sentinel_premium_license', 'sentinel_clear_license_cache');

/**
 * Get current plan name for display
 */
function sentinel_get_plan_name()
{
    return sentinel_is_premium() ? 'Premium' : 'Free';
}

/**
 * Get plan badge HTML for UI display
 */
function sentinel_get_plan_badge()
{
    $plan = sentinel_get_plan_name();
    $class = sentinel_is_premium() ? 'sentinel-badge-premium' : 'sentinel-badge-free';
    return sprintf('<span class="%s">%s</span>', $class, $plan);
}

/**
 * Total activity log entries recorded by Sentinel.
 *
 * @return int
 */
function sentinel_get_total_logs()
{
    return class_exists('Sentinel_Public_Stats') ? Sentinel_Public_Stats::get_total_logs() : 0;
}

/**
 * Activity log entries recorded today (site timezone).
 *
 * @return int
 */
function sentinel_get_today_logs()
{
    return class_exists('Sentinel_Public_Stats') ? Sentinel_Public_Stats::get_today_logs() : 0;
}

/**
 * Successful logins recorded today (user_login events).
 *
 * @return int
 */
function sentinel_get_logins_today()
{
    return class_exists('Sentinel_Public_Stats') ? Sentinel_Public_Stats::get_logins_today() : 0;
}

/**
 * All three public-safe summary counts.
 *
 * @return array{total_logs:int,today_logs:int,logins_today:int}
 */
function sentinel_get_public_stats()
{
    if (!class_exists('Sentinel_Public_Stats')) {
        return array(
            'total_logs'   => 0,
            'today_logs'   => 0,
            'logins_today' => 0,
        );
    }

    return Sentinel_Public_Stats::get_summary();
}

/**
 * Echo logins today for theme templates.
 *
 * @param array $args {
 *     @type string $before  Markup before the number.
 *     @type string $after   Markup after the number.
 *     @type bool   $echo    Echo when true; return string when false.
 * }
 * @return string|null
 */
function sentinel_display_logins_today($args = array())
{
    $args = wp_parse_args(
        $args,
        array(
            'before' => '',
            'after'  => '',
            'echo'   => true,
        )
    );

    $output = $args['before'] . (string) sentinel_get_logins_today() . $args['after'];

    if ($args['echo']) {
        echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- caller wraps markup.
        return null;
    }

    return $output;
}

// Simple debug function
function sentinel_debug($message)
{
    // Intentionally silent in production; hook back in during development if needed
}

/**
 * Authentication Counter Helper Functions (Premium)
 */

/**
 * Capture and process an authentication attempt
 *
 * @param array $ctx Context array with ip, username, result, source, etc.
 * @return void
 */
function sentinel_auth_capture_attempt($ctx)
{
    if (!sentinel_is_premium()) {
        return;
    }

    Sentinel_Auth_Counters::capture_attempt($ctx);
}

/**
 * Increment counters for an IP and return current snapshot
 *
 * @param string $ip IP address
 * @param string|null $username Username attempted (if any)
 * @param string $source Source of attempt (wp-login, xmlrpc, etc.)
 * @param bool $is_fail Whether this was a failed attempt
 * @return array Current counter snapshot
 */
function sentinel_auth_increment_counters($ip, $username = null, $source = 'wp-login', $is_fail = false)
{
    if (!sentinel_is_premium()) {
        return array();
    }

    return Sentinel_Auth_Counters::increment_counters($ip, $username, $source, $is_fail);
}

/**
 * Check if IP is allowlisted
 *
 * @param string $ip IP address
 * @return bool
 */
function sentinel_is_ip_allowlisted($ip)
{
    if (!sentinel_is_premium()) {
        return false;
    }

    return Sentinel_Auth_Counters::is_ip_allowlisted($ip);
}

/**
 * Check if IP is currently trusted
 *
 * @param string $ip IP address
 * @return bool
 */
function sentinel_is_ip_trusted($ip)
{
    if (!sentinel_is_premium()) {
        return false;
    }

    return Sentinel_Auth_Counters::is_ip_trusted($ip);
}

/**
 * Mark an IP as trusted for a duration
 *
 * @param string $ip IP address
 * @param int $duration Trust duration in seconds
 */
function sentinel_mark_ip_trusted($ip, $duration = 86400)
{
    if (!sentinel_is_premium()) {
        return;
    }

    Sentinel_Auth_Counters::mark_ip_trusted($ip, $duration);
}

/**
 * Security Incident Helper Functions (Premium)
 */

/**
 * Evaluate counter snapshot and handle any incidents
 *
 * @param array $snapshot Counter snapshot from auth counters
 * @param array $ctx Authentication context
 * @return array|null Incident data if triggered
 */
function sentinel_incident_evaluate($snapshot, $ctx)
{
    if (!sentinel_is_premium()) {
        return null;
    }

    return Sentinel_Incidents::evaluate_and_handle($snapshot, $ctx);
}

/**
 * Find open incident by IP and type
 *
 * @param string $ip IP address
 * @param string $type Incident type
 * @return array|null Incident data or null
 */
function sentinel_incident_find_open_by_ip($ip, $type)
{
    if (!sentinel_is_premium()) {
        return null;
    }

    return Sentinel_Incidents::find_open_incident($ip, $type);
}

/**
 * Auto-resolve stale incidents (called by cron)
 *
 * @return int Number of incidents resolved
 */
function sentinel_incident_autoresolve_due()
{
    if (!sentinel_is_premium()) {
        return 0;
    }

    return Sentinel_Incidents::auto_resolve_stale_incidents();
}

/**
 * Escalate incident confidence on success after burst
 *
 * @param string $ip IP address
 * @param string $username Username that logged in successfully
 * @return bool True if any incident was escalated
 */
function sentinel_confidence_escalate($ip, $username)
{
    if (!sentinel_is_premium()) {
        return false;
    }

    return Sentinel_Incidents::escalate_on_success_after_burst($ip, $username);
}

/**
 * Check if a user's role is excluded from logging
 *
 * @param int|null $user_id User ID to check (null for current user)
 * @return bool True if user role is excluded, false otherwise
 */
function sentinel_is_user_role_excluded($user_id = null)
{
    // Get excluded user roles from settings
    $settings = get_option('sentinel_log_management', array());
    $excluded_roles = isset($settings['excluded_user_roles']) ? $settings['excluded_user_roles'] : array();

    // If no roles are excluded, don't exclude anyone
    if (empty($excluded_roles)) {
        return false;
    }

    // If no user ID provided, use current user
    if ($user_id === null) {
        $user_id = get_current_user_id();
    }

    // If still no user ID (not logged in), don't exclude
    if (!$user_id) {
        return false;
    }

    // Get user data
    $user = get_userdata($user_id);
    if (!$user) {
        return false; // User doesn't exist, don't exclude
    }

    // Check if any of the user's roles are in the excluded list
    $user_roles = $user->roles;
    foreach ($user_roles as $role) {
        if (in_array($role, $excluded_roles)) {
            return true; // User has an excluded role
        }
    }

    return false; // User doesn't have any excluded roles
}

/**
 * Fallback logging when database logging fails
 * Uses WordPress error_log and admin notices as backup
 */
function sentinel_fallback_log($event_key, $data = array(), $user_id = null)
{
    // Log to WordPress error log
    $message = sprintf(
        '[SENTINEL FALLBACK] %s: %s (User: %d, URL: %s)',
        strtoupper($event_key),
        isset($data['error_message']) ? $data['error_message'] : json_encode($data),
        $user_id ?: get_current_user_id(),
        $_SERVER['REQUEST_URI'] ?? 'Unknown'
    );

    error_log($message);

    // Store in WordPress transient for admin display
    $fallback_logs = get_transient('sentinel_fallback_logs') ?: array();
    $fallback_logs[] = array(
        'event_key' => $event_key,
        'message' => $message,
        'timestamp' => current_time('mysql'),
        'data' => $data
    );

    // Keep only last 10 fallback logs
    $fallback_logs = array_slice($fallback_logs, -10);
    set_transient('sentinel_fallback_logs', $fallback_logs, HOUR_IN_SECONDS);

    // Store for database retry
    $retry_queue = get_transient('sentinel_retry_queue') ?: array();
    $retry_queue[] = array(
        'event_key' => $event_key,
        'data' => $data,
        'user_id' => $user_id,
        'timestamp' => time(),
        'attempts' => 0,
        'max_attempts' => 3
    );

    // Keep only last 50 retry entries — warn on overflow.
    if (count($retry_queue) > 50) {
        error_log('[Sentinel] Retry queue overflow: ' . count($retry_queue) . ' entries, capping to 50. Oldest entries discarded.');
        $retry_queue = array_slice($retry_queue, -50);
    }
    set_transient('sentinel_retry_queue', $retry_queue, 24 * HOUR_IN_SECONDS);

    // Add admin notice for critical errors
    if (is_admin() && in_array($event_key, array('wp_database_error', 'php_fatal_error'))) {
        add_action('admin_notices', function () use ($event_key, $data) {
            echo '<div class="notice notice-error"><p>';
            echo '<strong>Sentinel Alert:</strong> ';
            echo esc_html($event_key . ' - ' . (isset($data['error_message']) ? $data['error_message'] : 'Unknown error'));
            echo '</p></div>';
        });
    }
}

/**
 * Process database retry queue
 * Attempts to log failed entries to database when it's available again
 */
function sentinel_process_retry_queue()
{
    $retry_queue = get_transient('sentinel_retry_queue');
    if (empty($retry_queue)) {
        return 0;
    }

    $logger = new Sentinel_Logger();
    $processed = 0;
    $failed = array();

    foreach ($retry_queue as $entry) {
        if ($entry['attempts'] >= $entry['max_attempts']) {
            continue;
        }

        // Use direct database insert for php_fatal_error to bypass logger restrictions
        if ($entry['event_key'] === 'php_fatal_error') {
            $result = sentinel_retry_direct_database_insert($entry['event_key'], $entry['data'], $entry['user_id']);
        } else {
            $result = $logger->log($entry['event_key'], $entry['data'], $entry['user_id']);
        }

        if ($result) {
            $processed++;
        } else {
            $entry['attempts']++;
            $failed[] = $entry;
        }
    }

    // Update retry queue with only failed entries
    if (!empty($failed)) {
        set_transient('sentinel_retry_queue', $failed, 24 * HOUR_IN_SECONDS);
    } else {
        delete_transient('sentinel_retry_queue');
    }

    // Clear fallback logs if all entries were successfully processed
    if ($processed > 0 && empty($failed)) {
        delete_transient('sentinel_fallback_logs');
    }

    return $processed;
}

/**
 * Direct database insert for retry operations
 * Bypasses logger restrictions that prevent php_fatal_error logging
 */
function sentinel_retry_direct_database_insert($event_key, $data = array(), $user_id = null)
{
    global $wpdb;

    // Get event registry
    if (!class_exists('Sentinel_Events')) {
        return false;
    }

    $event_config = Sentinel_Events::get_event($event_key);
    if (!$event_config) {
        return false;
    }

    // Gather log info (similar to logger)
    $user_id = $user_id ?: get_current_user_id();
    $ip_address = isset($data['ip_address']) ? $data['ip_address'] : ($_SERVER['REMOTE_ADDR'] ?? '127.0.0.1');
    $user_agent = isset($data['user_agent']) ? $data['user_agent'] : ($_SERVER['HTTP_USER_AGENT'] ?? '');
    $url = isset($data['url']) ? $data['url'] : (is_admin() ? (admin_url($_SERVER['REQUEST_URI'] ?? '')) : ($_SERVER['REQUEST_URI'] ?? ''));
    $created_at = current_time('mysql');

    // Remove reserved keys from data
    $reserved = array('user_id', 'ip_address', 'user_agent', 'url', 'created_at');
    $event_data = array_diff_key($data, array_flip($reserved));

    // Insert directly into database
    $table = $wpdb->prefix . 'sentinel_logs';
    $result = $wpdb->insert(
        $table,
        array(
            'event_key'   => $event_key,
            'category'    => $event_config['category'],
            'priority'    => $event_config['priority'],
            'user_id'     => $user_id,
            'ip_address'  => $ip_address,
            'user_agent'  => $user_agent,
            'url'         => $url,
            'data'        => !empty($event_data) ? wp_json_encode($event_data) : '{}',
            'created_at'  => $created_at,
        ),
        array(
            '%s',
            '%s',
            '%s',
            '%d',
            '%s',
            '%s',
            '%s',
            '%s',
            '%s'
        )
    );

    return (bool) $result;
}

/**
 * Cron callback for database retry processing
 */
function sentinel_run_retry_cron()
{
    sentinel_process_retry_queue();
}

// Initialize the plugin
function sentinel_init()
{
    // Check if database needs updating
    Sentinel_Migration::check_version();

    // Seed onboarding state for upgraders where the activation hook
    // never fired (e.g. plugin files replaced on disk).
    if (class_exists('Sentinel_Onboarding')) {
        Sentinel_Onboarding::seed_on_load_if_missing();
    }

    // Initialize admin functionality using singleton pattern
    if (is_admin()) {
        $admin = Sentinel_Admin::get_instance();
        $admin->init();
    }

    // Initialize cleanup functionality
    if (class_exists('Sentinel_Cleanup')) {
        Sentinel_Cleanup::init();
    }

    // Initialize logger to set up batch logging cron hooks
    if (class_exists('Sentinel_Logger')) {
        new Sentinel_Logger();
    }

    // Initialize digest reports functionality
    if (class_exists('Sentinel_Digest_Reports')) {
        Sentinel_Digest_Reports::init();
    }

    // Initialize privacy functionality
    if (class_exists('Sentinel_Privacy')) {
        new Sentinel_Privacy();
    }

    // Initialize REST API functionality
    if (class_exists('Sentinel_Rest_API')) {
        Sentinel_Rest_API::init();
    }

    // Initialize file monitoring functionality
    if (class_exists('Sentinel_File_Monitor')) {
        Sentinel_File_Monitor::init();
    }

    // Initialize third‑party integrations through registry (keeps sentinel.php thin)
    if (class_exists('Sentinel_Integration_Registry')) {
        Sentinel_Integration_Registry::init();
    }

    // Ensure batch logging cron is scheduled if enabled
    $settings = get_option('sentinel_log_management', array());
    if (!empty($settings['batch_logging_enabled'])) {
        // Check if the cron job is scheduled, if not schedule it
        if (!wp_next_scheduled('sentinel_process_log_queue')) {
            sentinel_update_batch_logging_schedule();
        }
    }

    // Set up cleanup cron hooks
    add_action('sentinel_cleanup_logs', 'sentinel_run_cleanup_cron');
    add_action('sentinel_optimize_database', 'sentinel_run_optimization_cron');

    // Set up batch logging cron hook
    add_action('sentinel_process_log_queue', 'sentinel_run_batch_processing_cron');

    // Set up database retry cron hook
    add_action('sentinel_retry_database', 'sentinel_run_retry_cron');

    // Set up incident auto-resolution cron hook
    add_action('sentinel_autoresolve_incidents', 'sentinel_run_incident_autoresolve_cron');

    // Set up anonymization cron hook
    add_action('sentinel_anonymize_logs', 'sentinel_run_anonymize_cron');

    // Add custom cron interval for batch processing
    add_filter('cron_schedules', 'sentinel_add_batch_cron_interval');

    // Set up error logging
    sentinel_setup_php_error_handler();

    // Set up database error logging
    add_action('wp_loaded', 'sentinel_setup_db_error_logging');

    // Set up hooks for event logging
    add_action('wp_login', 'sentinel_log_user_login', 10, 2);
    add_action('wp_logout', 'sentinel_log_user_logout');
    add_action('user_register', 'sentinel_log_user_registered');
    add_action('profile_update', 'sentinel_log_profile_updated');
    add_action('transition_post_status', 'sentinel_log_post_published', 10, 3);
    add_action('before_delete_post', 'sentinel_log_post_deleted');
    add_action('comment_post', 'sentinel_log_comment_posted', 10, 2);
    add_action('comment_unapproved_to_approved', 'sentinel_log_comment_approved');
    add_action('delete_comment', 'sentinel_log_comment_deleted');
    add_action('activated_plugin', 'sentinel_log_plugin_activated');
    add_action('deactivated_plugin', 'sentinel_log_plugin_deactivated');
    add_action('upgrader_process_complete', 'sentinel_log_plugin_theme_updates', 10, 2);
    add_action('init', 'sentinel_check_maintenance_mode', 1);
    add_action('shutdown', 'sentinel_check_maintenance_mode_shutdown');
    add_action('wp_login_failed', 'sentinel_handle_login_failed');
    add_action('auth_cookie_bad', 'sentinel_log_bad_auth_cookie');
    add_action('wp_insert_post_data', 'sentinel_check_db_errors_on_post', 10, 2);
    add_action('wp_handle_upload_prefilter', 'sentinel_check_upload_errors');
    add_action('activated_plugin', 'sentinel_check_plugin_activation_errors');
    add_filter('wp_die_handler', 'sentinel_wp_die_handler');
    add_action('init', 'sentinel_check_http_errors');
    add_action('wp_cron_fail', 'sentinel_log_cron_errors');
    add_action('shutdown', 'sentinel_check_memory_usage');
}
add_action('plugins_loaded', 'sentinel_init');

// Load custom events first, then register default events
add_action('plugins_loaded', 'sentinel_load_custom_events', 15);
add_action('plugins_loaded', 'sentinel_register_default_events', 20);

/**
 * Canonical list of all Sentinel cron hook names.
 *
 * CRON RULE: When adding a new Sentinel cron hook, it must be:
 *  - Added to this function
 *  - Scheduled in activation (if applicable)
 *  - Cleared in deactivation
 *  - Removed from the cron option in uninstall.php (duplicate list there)
 *
 * @return array All Sentinel cron hook names (including legacy hooks for cleanup).
 */
function sentinel_get_cron_hook_names()
{
    return [
        'sentinel_cleanup_logs',
        'sentinel_optimize_database',
        'sentinel_process_log_queue',
        'sentinel_retry_database',
        'sentinel_autoresolve_incidents',
        'sentinel_anonymize_logs',
        'sentinel_daily_digest',
        'sentinel_weekly_digest',
        'sentinel_check_files',
        'sentinel_optimize_tables', // legacy — no longer scheduled, cleared for existing installs
    ];
}

// Activation hook
register_activation_hook(__FILE__, 'sentinel_activate');
function sentinel_activate()
{
    // Create database tables
    $migration = new Sentinel_Migration();
    $migration->create_tables();

    // Set default options
    Sentinel_Migration::set_default_options();

    // Schedule cleanup events (use time() — WP-Cron expects a Unix timestamp)
    if (!wp_next_scheduled('sentinel_cleanup_logs')) {
        wp_schedule_event(time(), 'daily', 'sentinel_cleanup_logs');
    }
    if (!wp_next_scheduled('sentinel_anonymize_logs')) {
        wp_schedule_event(time(), 'daily', 'sentinel_anonymize_logs');
    }
    if (!wp_next_scheduled('sentinel_daily_digest')) {
        wp_schedule_event(time(), 'daily', 'sentinel_daily_digest');
    }
    if (!wp_next_scheduled('sentinel_weekly_digest')) {
        wp_schedule_event(time(), 'weekly', 'sentinel_weekly_digest');
    }

    // Schedule batch logging if enabled
    sentinel_update_batch_logging_schedule();

    // Delay retry and incident crons by 5 minutes so activation does not
    // immediately process backlogs and replay stale notifications.
    if (!wp_next_scheduled('sentinel_retry_database')) {
        wp_schedule_event(time() + 300, 'hourly', 'sentinel_retry_database');
    }
    if (!wp_next_scheduled('sentinel_autoresolve_incidents')) {
        wp_schedule_event(time() + 300, 'hourly', 'sentinel_autoresolve_incidents');
    }
    if (!wp_next_scheduled('sentinel_optimize_database')) {
        wp_schedule_event(time(), 'weekly', 'sentinel_optimize_database');
    }
}

// Deactivation hook
register_deactivation_hook(__FILE__, 'sentinel_deactivate');
function sentinel_deactivate()
{
    // Clear every Sentinel cron hook (including legacy) from the canonical list.
    // Sentinel never schedules crons with arguments, so wp_clear_scheduled_hook()
    // is deterministic and removes all events for each hook name.
    foreach (sentinel_get_cron_hook_names() as $hook) {
        wp_clear_scheduled_hook($hook);
    }

    // Remove known Sentinel transients (by name only — no wildcard DB query).
    sentinel_clear_runtime_transients();
}

/**
 * Delete known Sentinel transients by explicit name and clear the license cache.
 * Called on deactivation. Does NOT run a wildcard DELETE on the options table.
 */
function sentinel_clear_runtime_transients()
{
    $transients = [
        'sentinel_fallback_logs',
        'sentinel_retry_queue',
        'sentinel_memory_alerts',
        'sentinel_performance_samples',
        'sentinel_maintenance_mode_state',
        'sentinel_plugin_activation_errors',
        'sentinel_file_path_validation',
        'sentinel_yoast_bulk_tracker',
        'sentinel_memory_leak_cooldown',
        'sentinel_memory_alert_cooldown_early_warning',
        'sentinel_memory_alert_cooldown_warning',
        'sentinel_memory_alert_cooldown_critical',
    ];

    foreach ($transients as $name) {
        delete_transient($name);
    }

    // Clear all license validation transients via the existing helper.
    if (function_exists('sentinel_clear_license_cache')) {
        sentinel_clear_license_cache();
    }
}

/**
 * Update cleanup schedule when settings change
 */
function sentinel_update_cleanup_schedule()
{
    if (!class_exists('Sentinel_Cleanup')) {
        return;
    }

    $cleanup = new Sentinel_Cleanup();
    $cleanup->unschedule_cleanup();
    $cleanup->schedule_cleanup();
}

/**
 * Update batch logging schedule when settings change
 */
function sentinel_update_batch_logging_schedule()
{
    $settings = get_option('sentinel_log_management', array());

    // Always clear existing batch logging schedule first
    wp_clear_scheduled_hook('sentinel_process_log_queue');

    // If batch logging is enabled, set up new schedule
    if (!empty($settings['batch_logging_enabled'])) {
        $frequency = !empty($settings['batch_frequency']) ? intval($settings['batch_frequency']) : 60;

        // Ensure we have a valid frequency
        if ($frequency < 30) {
            $frequency = 30; // Minimum 30 seconds
        }

        // WP-Cron uses UTC timestamps internally — use time() consistently.
        $schedule_time = time() + $frequency;
        $scheduled = wp_schedule_event($schedule_time, 'sentinel_batch_interval', 'sentinel_process_log_queue');

        $next_run = wp_next_scheduled('sentinel_process_log_queue');
        if ($next_run && $next_run <= time()) {
            wp_clear_scheduled_hook('sentinel_process_log_queue');
            wp_schedule_event(time() + 60, 'sentinel_batch_interval', 'sentinel_process_log_queue');
        }
    }
}

/**
 * Add custom cron interval for batch processing
 */
function sentinel_add_batch_cron_interval($schedules)
{
    $settings = get_option('sentinel_log_management', array());
    $frequency = !empty($settings['batch_frequency']) ? intval($settings['batch_frequency']) : 60;

    $schedules['sentinel_batch_interval'] = array(
        'interval' => $frequency,
        'display' => sprintf(__('Every %d seconds (Sentinel Batch)', 'sentinel'), $frequency)
    );

    return $schedules;
}

/**
 * Update digest report schedules based on settings
 */
function sentinel_update_digest_schedules()
{
    $settings = get_option('sentinel_log_management', array());

    // Check if any digest notifications are enabled
    $daily_digests = array('sentinel_email_daily_summary', 'sentinel_email_daily_error', 'sentinel_email_daily_user');
    $weekly_digests = array('sentinel_email_weekly_health', 'sentinel_email_weekly_performance', 'sentinel_email_weekly_security');

    $daily_enabled = false;
    $weekly_enabled = false;

    foreach ($daily_digests as $setting) {
        if (!empty($settings[$setting])) {
            $daily_enabled = true;
            break;
        }
    }

    foreach ($weekly_digests as $setting) {
        if (!empty($settings[$setting])) {
            $weekly_enabled = true;
            break;
        }
    }

    // Schedule daily digest if enabled
    if ($daily_enabled) {
        if (!wp_next_scheduled('sentinel_daily_digest')) {
            // Schedule for 9:00 AM tomorrow
            $tomorrow_9am = strtotime('tomorrow 9:00 AM');
            wp_schedule_event($tomorrow_9am, 'daily', 'sentinel_daily_digest');
        }
    } else {
        wp_clear_scheduled_hook('sentinel_daily_digest');
    }

    // Schedule weekly digest if enabled
    if ($weekly_enabled) {
        if (!wp_next_scheduled('sentinel_weekly_digest')) {
            // Schedule for 9:00 AM next Monday
            $next_monday_9am = strtotime('next monday 9:00 AM');
            wp_schedule_event($next_monday_9am, 'weekly', 'sentinel_weekly_digest');
        }
    } else {
        wp_clear_scheduled_hook('sentinel_weekly_digest');
    }
}

/**
 * Cron callback for log cleanup
 */
function sentinel_run_cleanup_cron()
{
    if (!class_exists('Sentinel_Cleanup')) {
        return;
    }

    $cleanup = new Sentinel_Cleanup();
    $cleanup->cleanup_logs();
}

/**
 * Cron callback for database optimization
 */
function sentinel_run_optimization_cron()
{
    if (!class_exists('Sentinel_Cleanup')) {
        return;
    }

    $cleanup = new Sentinel_Cleanup();
    $cleanup->optimize_database();
}

/**
 * Cron callback for batch processing
 */
function sentinel_run_batch_processing_cron()
{
    if (!class_exists('Sentinel_Logger')) {
        return;
    }

    $logger = new Sentinel_Logger();
    $processed = $logger->process_queue();

    // Reschedule only if the next run is missing (cleared or failed), not on every execution.
    if (!wp_next_scheduled('sentinel_process_log_queue')) {
        sentinel_update_batch_logging_schedule();
    }
}

/**
 * Cron callback for incident auto-resolution
 */
function sentinel_run_incident_autoresolve_cron()
{
    if (!sentinel_is_premium()) {
        return;
    }

    $resolved_count = sentinel_incident_autoresolve_due();

    // Log the auto-resolution activity
    if ($resolved_count > 0 && function_exists('sentinel_log_event')) {
        sentinel_log_event('security_incident_resolved', array(
            'resolved_count' => $resolved_count,
            'resolution_method' => 'auto_cron',
            'cron_timestamp' => current_time('mysql')
        ));
    }
}

/**
 * Cron callback for log anonymization
 */
function sentinel_run_anonymize_cron()
{
    if (!class_exists('Sentinel_Cleanup')) {
        return;
    }
    Sentinel_Cleanup::anonymize_old_logs();
}

// Public API function for logging events
if (!function_exists('sentinel_log_event')) {
    /**
     * Log an event using Sentinel
     *
     * @param string $event_key The event key (must be registered)
     * @param array $data       Additional event data (optional)
     * @param int $user_id      User ID (optional)
     * @return bool             True on success, false on failure
     */
    function sentinel_log_event($event_key, $data = array(), $user_id = null)
    {
        if (!class_exists('Sentinel_Logger')) {
            return false;
        }

        // Check if this event type is disabled
        $disabled_events = get_option('sentinel_disabled_events', array());
        if (in_array($event_key, $disabled_events)) {
            return false; // Event is disabled, don't log it
        }

        // Critical system events should ALWAYS be logged, regardless of role exclusion
        $critical_system_events = array(
            'php_fatal_error',
            'php_warning',
            'wp_database_error',
            'wp_memory_error',
            'wp_config_error',
            'security_incident_opened',
            'security_incident_updated'
        );

        // Check if user role is excluded from logging (but bypass for critical events)
        if (!in_array($event_key, $critical_system_events) && sentinel_is_user_role_excluded($user_id)) {
            return false; // User role is excluded, don't log it
        }


        // Try database logging first
        $logger = new Sentinel_Logger();
        $result = $logger->log($event_key, $data, $user_id);

        // If database logging fails, use fallback logging for critical events
        if (!$result && in_array($event_key, array('wp_database_error', 'php_fatal_error', 'wp_config_error'))) {
            sentinel_fallback_log($event_key, $data, $user_id);
        }

        // --- BEGIN: Security Email Notification ---
        // Mutual exclusion: only ONE email pathway fires per event call.
        // Pathway A (Security Alert) takes priority. If it sends, Pathway B is skipped.
        // v1.1.2: Both pathways are now rate-limited to prevent email floods.
        $sentinel_email_sent_this_event = false;
        $settings = get_option('sentinel_log_management', array());
        if (!empty($settings['admin_notifications'])) {
            if (class_exists('Sentinel_Events')) {
                $event_config = Sentinel_Events::get_event($event_key);
                if ($event_config) {
                    $is_security = ($event_config['category'] === 'security');
                    $is_critical = ($event_config['priority'] === 'critical');
                    $is_system = ($event_config['category'] === 'system');
                    if (($is_security || $is_critical || $is_system) && sentinel_email_rate_limiter($event_key)) {
                        // Compose security alert email
                        $to = isset($settings['notification_email']) ? $settings['notification_email'] : get_option('admin_email');
                        $subject = '[Sentinel Security Alert] ' . ($event_config['label'] ?? $event_key);

                        // Plain text version
                        $body_plain = "A security/critical/system event was logged by Sentinel:\n\n";
                        $body_plain .= "Event: " . ($event_config['label'] ?? $event_key) . "\n";
                        $body_plain .= "Category: " . $event_config['category'] . "\n";
                        $body_plain .= "Priority: " . $event_config['priority'] . "\n";
                        $body_plain .= "User ID: " . ($user_id ?: 'N/A') . "\n";
                        $body_plain .= "Data: " . print_r($data, true) . "\n";
                        $body_plain .= "Time: " . current_time('mysql') . "\n";
                        $body_plain .= "URL: " . ($_SERVER['REQUEST_URI'] ?? 'Unknown') . "\n";

                        // --- Use external HTML template for email body ---
                        $is_notification = false; // Security/system/critical
                        // Set up all required variables for the template
                        // $event_config, $data, $to, $subject, $is_notification, $event_key
                        include SENTINEL_PLUGIN_DIR . 'includes/email-template.php';
                        // $body_html is now available

                        // Send the actual email
                        $headers = array(
                            'Content-Type: text/html; charset=UTF-8',
                            'From: Sentinel <' . get_option('admin_email') . '>'
                        );

                        $sent = wp_mail($to, $subject, $body_html, $headers);
                        $sentinel_email_sent_this_event = (bool) $sent;

                        if (!$sent) {
                            error_log('[Sentinel] Failed to send security alert email to ' . $to);
                        }
                    }
                }
            }
        }
        // --- END: Security Email Notification ---

        // --- BEGIN: Granular Email Notification ---
        // Mutual exclusion: skip if Pathway A already sent an email for this event.
        if (!$sentinel_email_sent_this_event && function_exists('sentinel_should_send_granular_email_notification') && class_exists('Sentinel_Events')) {
            $event_config = Sentinel_Events::get_event($event_key);
            if ($event_config && sentinel_should_send_granular_email_notification($event_key, $event_config, $data, $user_id) && sentinel_email_rate_limiter($event_key)) {
                // Compose granular notification email
                $to = isset($settings['notification_email']) ? $settings['notification_email'] : get_option('admin_email');
                $subject = '[Sentinel Notification] ' . ($event_config['label'] ?? $event_key);
                // Use the same $body_plain and $body_html logic as above
                // (Copy the template code here for consistency)
                $priority_color = '';
                $priority_bg = '';
                switch ($event_config['priority']) {
                    case 'critical':
                        $priority_color = '#dc2626';
                        $priority_bg = '#fef2f2';
                        break;
                    case 'high':
                        $priority_color = '#ea580c';
                        $priority_bg = '#fff7ed';
                        break;
                    case 'medium':
                        $priority_color = '#ca8a04';
                        $priority_bg = '#fefce8';
                        break;
                    default:
                        $priority_color = '#2563eb';
                        $priority_bg = '#eff6ff';
                }
                $category_color = '';
                switch ($event_config['category']) {
                    case 'security':
                        $category_color = '#dc2626';
                        break;
                    case 'system':
                        $category_color = '#059669';
                        break;
                    default:
                        $category_color = '#6b7280';
                }
                $body_plain = "A Sentinel event notification was triggered:\n\n";
                $body_plain .= "Event: " . ($event_config['label'] ?? $event_key) . "\n";
                $body_plain .= "Category: " . $event_config['category'] . "\n";
                $body_plain .= "Priority: " . $event_config['priority'] . "\n";
                $body_plain .= "User ID: " . (get_current_user_id() ?: 'N/A') . "\n";
                $body_plain .= "Data: " . print_r($data, true) . "\n";
                $body_plain .= "Time: " . current_time('mysql') . "\n";
                // --- Use external HTML template for email body (granular) ---
                $is_notification = true; // Granular notification
                // $event_config, $data, $to, $subject, $is_notification, $event_key
                include SENTINEL_PLUGIN_DIR . 'includes/email-template.php';
                // $body_html is now available

                // Send the actual email
                $headers = array(
                    'Content-Type: text/html; charset=UTF-8',
                    'From: Sentinel <' . get_option('admin_email') . '>'
                );

                $sent = wp_mail($to, $subject, $body_html, $headers);

                if (!$sent) {
                    error_log('[Sentinel] Failed to send granular notification email to ' . $to);
                }
            }
        }
        // --- END: Granular Email Notification ---

        return $result;
    }
}

// Public API function for registering events
if (!function_exists('sentinel_register_event')) {
    /**
     * Register a custom event type (Sentinel+ feature for custom events)
     *
     * @param string $event_key The event key
     * @param array $config     Event configuration
     * @return bool|WP_Error    True on success, false/WP_Error on failure
     */
    function sentinel_register_event($event_key, $config = array())
    {
        if (!class_exists('Sentinel_Events')) {
            return false;
        }

        // Check if this is a default system event (always allowed)
        $is_system_event = sentinel_is_default_event($event_key);

        // For custom events, require premium
        if (!$is_system_event && !sentinel_is_premium()) {
            return new WP_Error('premium_required', 'Custom event registration requires Sentinel Premium');
        }

        // Register the event
        $result = Sentinel_Events::register_event($event_key, $config);

        // If successful and it's a custom event, save to database for persistence
        if ($result && !$is_system_event) {
            sentinel_save_custom_event($event_key, $config);
        }

        return $result;
    }
}

/**
 * Check if an event key is a default system event
 *
 * @param string $event_key The event key to check
 * @return bool True if it's a default system event
 */
function sentinel_is_default_event($event_key)
{
    // Load event definitions if not already loaded
    if (!function_exists('sentinel_get_default_event_keys')) {
        require_once plugin_dir_path(__FILE__) . 'includes/event-definitions.php';
    }

    $default_events = sentinel_get_default_event_keys();
    return in_array($event_key, $default_events);
}

/**
 * Save custom event to database for persistence
 *
 * @param string $event_key The event key
 * @param array $config Event configuration
 * @return bool True on success
 */
function sentinel_save_custom_event($event_key, $config)
{
    $custom_events = get_option('sentinel_custom_events', array());
    $custom_events[$event_key] = $config;
    return update_option('sentinel_custom_events', $custom_events);
}

/**
 * Register a custom event (wrapper function for template setup)
 *
 * @param string $event_key The event key
 * @param array $config Event configuration
 * @return bool|WP_Error True on success, WP_Error on failure
 */
function sentinel_register_event($event_key, $config)
{
    // Validate input
    if (empty($event_key) || !is_array($config)) {
        return new WP_Error('invalid_input', 'Event key and config are required');
    }

    // Check if it's a system event
    if (sentinel_is_default_event($event_key)) {
        return new WP_Error('system_event', 'Cannot override system events');
    }

    // Register with the Events class if available
    if (class_exists('Sentinel_Events')) {
        Sentinel_Events::register_event($event_key, $config);
    }

    // Save to database for persistence
    return sentinel_save_custom_event($event_key, $config);
}

/**
 * Load custom events from database and register them
 */
function sentinel_load_custom_events()
{
    $custom_events = get_option('sentinel_custom_events', array());

    foreach ($custom_events as $event_key => $config) {
        // Plugin integration events are available to everyone
        // Only restrict user-created custom events to premium
        $is_plugin_integration = (
            strpos($event_key, 'woo_') === 0 ||
            strpos($event_key, 'cf7_') === 0 ||
            strpos($event_key, 'wpf_') === 0 ||
            strpos($event_key, 'gf_') === 0 ||
            strpos($event_key, 'yoast_') === 0
        );

        // Allow plugin integrations for everyone, restrict custom events to premium
        if ($is_plugin_integration || sentinel_is_premium()) {
            if (class_exists('Sentinel_Events')) {
                Sentinel_Events::register_event($event_key, $config);
            }
        }
    }
}

/**
 * Remove a custom event
 *
 * @param string $event_key The event key to remove
 * @return bool True on success
 */
function sentinel_remove_custom_event($event_key)
{
    if (!sentinel_is_premium()) {
        return false;
    }

    // Don't allow removal of default system events
    if (sentinel_is_default_event($event_key)) {
        return false;
    }

    $custom_events = get_option('sentinel_custom_events', array());
    if (isset($custom_events[$event_key])) {
        unset($custom_events[$event_key]);
        return update_option('sentinel_custom_events', $custom_events);
    }

    return false;
}

/**
 * Get all custom events for display
 *
 * @return array Array of custom events
 */
function sentinel_get_custom_events()
{
    $custom_events = get_option('sentinel_custom_events', array());

    // If not premium, only return plugin integration events
    if (!sentinel_is_premium()) {
        $plugin_events = array();
        foreach ($custom_events as $event_key => $config) {
            $is_plugin_integration = (
                strpos($event_key, 'woo_') === 0 ||
                strpos($event_key, 'cf7_') === 0 ||
                strpos($event_key, 'wpf_') === 0 ||
                strpos($event_key, 'gf_') === 0
            );

            if ($is_plugin_integration) {
                $plugin_events[$event_key] = $config;
            }
        }
        return $plugin_events;
    }

    return $custom_events;
}

// AJAX Handlers for Custom Events Management
add_action('wp_ajax_sentinel_add_custom_event', 'sentinel_ajax_add_custom_event');
add_action('wp_ajax_sentinel_remove_custom_event', 'sentinel_ajax_remove_custom_event');
add_action('wp_ajax_sentinel_get_custom_events', 'sentinel_ajax_get_custom_events');
add_action('wp_ajax_sentinel_toggle_custom_event', 'sentinel_ajax_toggle_custom_event');
add_action('wp_ajax_sentinel_toggle_system_event', 'sentinel_ajax_toggle_system_event');
add_action('wp_ajax_sentinel_setup_template', 'sentinel_ajax_setup_template');
add_action('wp_ajax_sentinel_test_custom_event', 'sentinel_ajax_test_custom_event');

// AJAX Handler for API Key Management
add_action('wp_ajax_sentinel_save_api_key', 'sentinel_ajax_save_api_key');

/**
 * AJAX handler: Add custom event
 */
function sentinel_ajax_add_custom_event()
{
    // Security checks
    check_ajax_referer('sentinel_ajax_nonce', 'nonce');

    if (!current_user_can('manage_options')) {
        wp_die(json_encode(array('success' => false, 'error' => 'Permission denied')));
    }

    if (!sentinel_is_premium()) {
        wp_die(json_encode(array('success' => false, 'error' => 'Sentinel+ feature required')));
    }

    // Get and validate input
    $event_key = sanitize_text_field($_POST['event_key'] ?? '');
    $label = sanitize_text_field($_POST['label'] ?? '');
    $category = sanitize_text_field($_POST['category'] ?? 'general');
    $priority = sanitize_text_field($_POST['priority'] ?? 'medium');
    $description = sanitize_textarea_field($_POST['description'] ?? '');

    // Validation
    if (empty($event_key) || empty($label)) {
        wp_die(json_encode(array('success' => false, 'error' => 'Event key and label are required')));
    }

    // Validate event key format (letters, numbers, underscores only)
    if (!preg_match('/^[a-zA-Z0-9_]+$/', $event_key)) {
        wp_die(json_encode(array('success' => false, 'error' => 'Event key can only contain letters, numbers, and underscores')));
    }

    // Check if it's a system event
    if (sentinel_is_default_event($event_key)) {
        wp_die(json_encode(array('success' => false, 'error' => 'Cannot override system events')));
    }

    // Check if event already exists
    $existing_events = get_option('sentinel_custom_events', array());
    if (isset($existing_events[$event_key])) {
        wp_die(json_encode(array('success' => false, 'error' => 'Event key already exists')));
    }

    // Create event config
    $config = array(
        'label' => $label,
        'category' => $category,
        'priority' => $priority,
        'description' => $description,
        'enabled' => true
    );

    // Register and save the event
    $result = sentinel_register_event($event_key, $config);

    if (is_wp_error($result)) {
        wp_die(json_encode(array('success' => false, 'error' => $result->get_error_message())));
    }

    if ($result) {
        wp_die(json_encode(array(
            'success' => true,
            'message' => 'Custom event added successfully',
            'event' => array(
                'key' => $event_key,
                'config' => $config
            )
        )));
    } else {
        wp_die(json_encode(array('success' => false, 'error' => 'Failed to add custom event')));
    }
}

/**
 * AJAX handler: Remove custom event
 */
function sentinel_ajax_remove_custom_event()
{
    // Security checks
    check_ajax_referer('sentinel_ajax_nonce', 'nonce');

    if (!current_user_can('manage_options')) {
        wp_die(json_encode(array('success' => false, 'error' => 'Permission denied')));
    }

    if (!sentinel_is_premium()) {
        wp_die(json_encode(array('success' => false, 'error' => 'Sentinel+ feature required')));
    }

    $event_key = sanitize_text_field($_POST['event_key'] ?? '');

    if (empty($event_key)) {
        wp_die(json_encode(array('success' => false, 'error' => 'Event key is required')));
    }

    $result = sentinel_remove_custom_event($event_key);

    if ($result) {
        wp_die(json_encode(array('success' => true, 'message' => 'Custom event removed successfully')));
    } else {
        wp_die(json_encode(array('success' => false, 'error' => 'Failed to remove custom event')));
    }
}

/**
 * AJAX handler: Get all custom events
 */
function sentinel_ajax_get_custom_events()
{
    // Security checks
    check_ajax_referer('sentinel_ajax_nonce', 'nonce');

    if (!current_user_can('manage_options')) {
        wp_die(json_encode(array('success' => false, 'error' => 'Permission denied')));
    }

    // Plugin integrations available to everyone, custom events require premium
    // We'll filter appropriately in the sentinel_get_custom_events function

    $custom_events = sentinel_get_custom_events();
    $disabled_events = get_option('sentinel_disabled_events', array());

    // Filter out ALL plugin-specific events from custom events section
    // Plugin events should only appear in the "3rd Party Plugin Integrations" section
    $filtered_events = array();
    foreach ($custom_events as $event_key => $config) {
        // Skip all plugin-specific events - they belong in the integrations section only
        if (
            strpos($event_key, 'woo_') === 0 ||
            strpos($event_key, 'cf7_') === 0 ||
            strpos($event_key, 'wpf_') === 0 ||
            strpos($event_key, 'gf_') === 0 ||
            strpos($event_key, 'yoast_') === 0
        ) {
            continue; // Skip all plugin events regardless of plugin status
        }

        // Only include truly custom events (not plugin templates)
        $config['enabled'] = !in_array($event_key, $disabled_events);
        $filtered_events[$event_key] = $config;
    }

    wp_die(json_encode(array('success' => true, 'events' => $filtered_events)));
}

/**
 * AJAX handler: Toggle custom event status
 */
function sentinel_ajax_toggle_custom_event()
{
    // Security checks
    check_ajax_referer('sentinel_ajax_nonce', 'nonce');

    if (!current_user_can('manage_options')) {
        wp_die(json_encode(array('success' => false, 'error' => 'Permission denied')));
    }

    if (!sentinel_is_premium()) {
        wp_die(json_encode(array('success' => false, 'error' => 'Sentinel+ feature required')));
    }

    // Get and validate input
    $event_key = sanitize_text_field($_POST['event_key'] ?? '');
    $enabled = intval($_POST['enabled'] ?? 1);

    if (empty($event_key)) {
        wp_die(json_encode(array('success' => false, 'error' => 'Event key is required')));
    }

    // Check if this is a custom event
    $custom_events = sentinel_get_custom_events();
    if (!isset($custom_events[$event_key])) {
        wp_die(json_encode(array('success' => false, 'error' => 'Custom event not found')));
    }

    // Get current disabled events list
    $disabled_events = get_option('sentinel_disabled_events', array());

    if ($enabled) {
        // Enable the event (remove from disabled list)
        $disabled_events = array_diff($disabled_events, array($event_key));
    } else {
        // Disable the event (add to disabled list)
        if (!in_array($event_key, $disabled_events)) {
            $disabled_events[] = $event_key;
        }
    }

    // Update the disabled events option
    update_option('sentinel_disabled_events', $disabled_events);

    wp_die(json_encode(array('success' => true, 'message' => 'Custom event status updated')));
}

/**
 * AJAX handler: Toggle system event status
 */
function sentinel_ajax_toggle_system_event()
{
    // Security checks
    check_ajax_referer('sentinel_ajax_nonce', 'nonce');

    if (!current_user_can('manage_options')) {
        wp_die(json_encode(array('success' => false, 'error' => 'Permission denied')));
    }

    // Get and validate input
    $event_key = sanitize_text_field($_POST['event_key'] ?? '');
    $enabled = intval($_POST['enabled'] ?? 1);

    if (empty($event_key)) {
        wp_die(json_encode(array('success' => false, 'error' => 'Event key is required')));
    }

    // Get current disabled events list
    $disabled_events = get_option('sentinel_disabled_events', array());

    if ($enabled) {
        // Enable the event (remove from disabled list)
        $disabled_events = array_diff($disabled_events, array($event_key));
    } else {
        // Disable the event (add to disabled list)
        if (!in_array($event_key, $disabled_events)) {
            $disabled_events[] = $event_key;
        }
    }

    // Update the disabled events option
    update_option('sentinel_disabled_events', $disabled_events);

    wp_die(json_encode(array('success' => true, 'message' => 'System event status updated')));
}

/**
 * AJAX handler: Setup plugin template
 */
function sentinel_ajax_setup_template()
{
    // Security checks
    check_ajax_referer('sentinel_ajax_nonce', 'nonce');

    if (!current_user_can('manage_options')) {
        wp_die(json_encode(array('success' => false, 'error' => 'Permission denied')));
    }

    // Get and validate input
    $template = sanitize_text_field($_POST['template'] ?? '');

    if (empty($template)) {
        wp_die(json_encode(array('success' => false, 'error' => 'Template is required')));
    }

    // Get template configurations
    $templates = sentinel_get_plugin_templates();

    if (!isset($templates[$template])) {
        wp_die(json_encode(array('success' => false, 'error' => 'Invalid template')));
    }

    $template_config = $templates[$template];

    // Check if plugin is active
    if (!$template_config['plugin_active']) {
        wp_die(json_encode(array('success' => false, 'error' => 'Required plugin is not active')));
    }

    $events_created = 0;
    $existing_events = get_option('sentinel_custom_events', array());
    $errors = array();

    // Create each event in the template
    foreach ($template_config['events'] as $event_key => $event_config) {
        // Skip if event already exists
        if (isset($existing_events[$event_key])) {
            continue;
        }

        // Register and save the custom event
        $result = sentinel_register_event($event_key, $event_config);

        if (is_wp_error($result)) {
            $errors[] = "Failed to create {$event_key}: " . $result->get_error_message();
        } elseif ($result) {
            $events_created++;
        }
    }

    if ($events_created > 0) {
        wp_die(json_encode(array(
            'success' => true,
            'events_created' => $events_created,
            'message' => "Successfully created {$events_created} custom events"
        )));
    } else {
        if (!empty($errors)) {
            wp_die(json_encode(array('success' => false, 'error' => implode(', ', $errors))));
        } else {
            wp_die(json_encode(array('success' => false, 'error' => 'All events already exist or failed to create')));
        }
    }
}

/**
 * Get plugin template configurations
 */
function sentinel_get_plugin_templates()
{
    if (class_exists('Sentinel_Integration_Registry')) {
        return Sentinel_Integration_Registry::get_templates();
    }
    return array();
}

/**
 * AJAX handler: Test custom event
 */
function sentinel_ajax_test_custom_event()
{
    // Security checks
    check_ajax_referer('sentinel_ajax_nonce', 'nonce');

    if (!current_user_can('manage_options')) {
        wp_die(json_encode(array('success' => false, 'error' => 'Permission denied')));
    }

    if (!sentinel_is_premium()) {
        wp_die(json_encode(array('success' => false, 'error' => 'Sentinel+ feature required')));
    }

    $event_key = sanitize_text_field($_POST['event_key'] ?? '');

    if (empty($event_key)) {
        wp_die(json_encode(array('success' => false, 'error' => 'Event key is required')));
    }

    // Create sample test data based on event type
    $test_data = array();
    switch ($event_key) {
        case 'new_order':
            $test_data = array(
                'order_id' => 'TEST-' . time(),
                'customer_email' => 'test@example.com',
                'total_amount' => 99.99,
                'currency' => 'USD',
                'items_count' => 3,
                'payment_method' => 'credit_card',
                'test_mode' => true
            );
            break;
        default:
            $test_data = array(
                'test_event' => true,
                'timestamp' => current_time('mysql'),
                'user_id' => get_current_user_id(),
                'source' => 'admin_test'
            );
            break;
    }

    // Attempt to log the custom event
    $result = sentinel_log_event($event_key, $test_data);

    // Check for WP_Error first
    if (is_wp_error($result) && $result instanceof WP_Error) {
        wp_die(json_encode(array('success' => false, 'error' => $result->get_error_message())));
    } elseif ($result === false) {
        wp_die(json_encode(array('success' => false, 'error' => 'Event not registered or logging failed')));
    } else {
        wp_die(json_encode(array(
            'success' => true,
            'message' => "Test event '{$event_key}' logged successfully! Check your logs to see the entry."
        )));
    }
}

/**
 * AJAX handler: Save API key
 */
function sentinel_ajax_save_api_key()
{
    // Verify nonce
    if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'sentinel_settings')) {
        wp_send_json_error('Invalid nonce');
    }

    // Check permissions
    if (!current_user_can('manage_options')) {
        wp_send_json_error('Insufficient permissions');
    }

    // Validate API key
    $api_key = sanitize_text_field($_POST['api_key'] ?? '');
    if (empty($api_key) || !preg_match('/^sent_[A-Za-z0-9]{32}$/', $api_key)) {
        wp_send_json_error('Invalid API key format');
    }

    // Get current settings
    $log_settings = get_option('sentinel_log_management', array());

    // Update API key
    $log_settings['api_key'] = $api_key;

    // Save settings
    $result = update_option('sentinel_log_management', $log_settings);

    if ($result) {
        // Log the API key generation
        sentinel_log_event('api_key_generated', array(
            'key_prefix' => substr($api_key, 0, 8) . '...',
            'generated_by' => get_current_user_id(),
            'timestamp' => current_time('mysql')
        ));

        wp_send_json_success('API key saved successfully');
    } else {
        wp_send_json_error('Failed to save API key');
    }
}


/**
 * One-time cleanup: Remove system events from custom events option
 * This fixes the issue where system events were incorrectly saved as custom events
 */
function sentinel_cleanup_system_events_from_custom()
{
    // Only run if we haven't cleaned up yet (v2 after fixing default events list)
    if (get_option('sentinel_custom_events_cleaned_v2', false)) {
        return;
    }

    $custom_events = get_option('sentinel_custom_events', array());
    if (empty($custom_events)) {
        update_option('sentinel_custom_events_cleaned_v2', true);
        return;
    }

    // Remove all system events from the custom events list
    $cleaned_events = array();
    foreach ($custom_events as $event_key => $config) {
        if (!sentinel_is_default_event($event_key)) {
            $cleaned_events[$event_key] = $config;
        }
    }

    // Update the option with only truly custom events
    update_option('sentinel_custom_events', $cleaned_events);
    update_option('sentinel_custom_events_cleaned_v2', true);

    // Log the cleanup for debugging
    $removed_count = count($custom_events) - count($cleaned_events);
}

// Clear incorrectly saved system events from custom events option (one-time cleanup)
add_action('init', 'sentinel_cleanup_system_events_from_custom', 1);

// Custom event loading moved to plugins_loaded hook for better timing
function sentinel_register_default_events()
{
    // Check if Sentinel_Events class exists
    if (!class_exists('Sentinel_Events')) {
        return;
    }

    // Load event definitions if not already loaded
    if (!function_exists('sentinel_get_default_event_definitions')) {
        require_once plugin_dir_path(__FILE__) . 'includes/event-definitions.php';
    }

    // Get all default event definitions
    $default_events = sentinel_get_default_event_definitions();

    // Register each event
    foreach ($default_events as $event_key => $config) {
        // Convert config format to match Sentinel_Events::register_event expectations
        $register_config = array(
            'label' => ucwords(str_replace('_', ' ', $event_key)),
            'category' => $config['category'],
            'priority' => $config['priority'],
            'description' => $config['description']
        );

        Sentinel_Events::register_event($event_key, $register_config);
    }
}

// Hook into WordPress events for automatic logging
/**
 * Log user login events - Integrated with Auth Counters
 */
function sentinel_log_user_login($user_login, $user)
{
    $username = $user->user_login ?? $user_login;
    $user_id = $user->ID ?? 0;

    // Build context for auth counter system
    $context = array(
        'ip' => $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1',
        'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown',
        'username' => $username,
        'user_id' => $user_id,
        'result' => 'success',
        'source' => 'wp-login',
        'timestamp' => time()
    );

    // Log the basic event
    sentinel_log_event('user_login', array(
        'username' => $username,
        'user_id' => $user_id,
        'login_method' => 'standard',
        'user_agent' => $context['user_agent'],
        'ip' => $context['ip'],
        'success' => true
    ), $user_id);

    // Premium: Use auth counter system for success-after-burst detection
    if (sentinel_is_premium() && function_exists('sentinel_auth_capture_attempt')) {
        sentinel_auth_capture_attempt($context);

        // Check for admin role trust
        if (user_can($user, 'administrator')) {
            $trust_admin_ips = get_option('sentinel_security_settings', array())['trust_admin_ips'] ?? true;
            if ($trust_admin_ips && function_exists('sentinel_mark_ip_trusted')) {
                sentinel_mark_ip_trusted($context['ip'], 24 * HOUR_IN_SECONDS);
            }
        }
    }
}

/**
 * Handle XML-RPC authentication attempts
 */
add_filter('xmlrpc_login_error', 'sentinel_handle_xmlrpc_auth', 10, 2);
function sentinel_handle_xmlrpc_auth($error, $user)
{
    // Determine if this was a success or failure
    $is_success = !is_wp_error($error) && !is_wp_error($user);
    $username = '';

    if ($is_success && $user instanceof WP_User) {
        $username = $user->user_login;
        $user_id = $user->ID;
    } elseif (is_wp_error($user)) {
        // Try to extract username from error data
        $username = isset($_POST['username']) ? sanitize_user($_POST['username']) : 'unknown';
        $user_id = null;
    }

    // Build context for auth counter system
    $context = array(
        'ip' => $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1',
        'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown',
        'username' => $username,
        'user_id' => $user_id ?? null,
        'result' => $is_success ? 'success' : 'fail',
        'source' => 'xmlrpc',
        'timestamp' => time(),
        'error_code' => is_wp_error($error) ? $error->get_error_code() : ''
    );

    // Log the event
    $event_type = $is_success ? 'auth_success' : 'auth_fail';
    sentinel_log_event($event_type, array(
        'username' => $username,
        'user_agent' => $context['user_agent'],
        'ip' => $context['ip'],
        'source' => 'xmlrpc',
        'success' => $is_success,
        'error_code' => $context['error_code']
    ), $user_id ?? 0);

    // Premium: Use auth counter system for incident detection
    if (sentinel_is_premium() && function_exists('sentinel_auth_capture_attempt')) {
        sentinel_auth_capture_attempt($context);
    }

    return $error;
}

/**
 * Handle WooCommerce authentication attempts
 */
add_action('plugins_loaded', 'sentinel_setup_woocommerce_hooks');
function sentinel_setup_woocommerce_hooks()
{
    if (class_exists('WooCommerce')) {
        // WooCommerce login failed
        add_action('wp_login_failed', 'sentinel_handle_woo_login_failed');

        // WooCommerce successful login (uses same wp_login hook but we can detect WooCommerce context)
        add_action('woocommerce_login_redirect', 'sentinel_handle_woo_login_success', 10, 2);
    }
}

function sentinel_handle_woo_login_failed($username)
{
    // Only handle if this is a WooCommerce request
    if (!class_exists('WooCommerce')) {
        return;
    }

    $is_woo_request = false;

    // Check for WooCommerce context using multiple methods
    if (
        isset($_POST['woocommerce-login-nonce']) ||
        (isset($_REQUEST['wc-ajax']) && !empty($_REQUEST['wc-ajax'])) ||
        (strpos($_SERVER['REQUEST_URI'] ?? '', '/my-account') !== false)
    ) {
        $is_woo_request = true;
    }

    if (!$is_woo_request) {
        return;
    }

    // Build context for auth counter system
    $context = array(
        'ip' => $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1',
        'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown',
        'username' => $username,
        'user_id' => null,
        'result' => 'fail',
        'source' => 'woocommerce',
        'timestamp' => time()
    );

    // Log the event
    sentinel_log_event('auth_fail', array(
        'username' => $username,
        'user_agent' => $context['user_agent'],
        'ip' => $context['ip'],
        'source' => 'woocommerce',
        'success' => false
    ));

    // Premium: Use auth counter system for incident detection
    if (sentinel_is_premium() && function_exists('sentinel_auth_capture_attempt')) {
        sentinel_auth_capture_attempt($context);
    }
}

function sentinel_handle_woo_login_success($redirect, $user)
{
    if (!$user instanceof WP_User) {
        return $redirect;
    }

    // Build context for auth counter system
    $context = array(
        'ip' => $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1',
        'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown',
        'username' => $user->user_login,
        'user_id' => $user->ID,
        'result' => 'success',
        'source' => 'woocommerce',
        'timestamp' => time()
    );

    // Log the event
    sentinel_log_event('auth_success', array(
        'username' => $user->user_login,
        'user_agent' => $context['user_agent'],
        'ip' => $context['ip'],
        'source' => 'woocommerce',
        'success' => true
    ), $user->ID);

    // Premium: Use auth counter system for success-after-burst detection
    if (sentinel_is_premium() && function_exists('sentinel_auth_capture_attempt')) {
        sentinel_auth_capture_attempt($context);
    }

    return $redirect;
}

/**
 * Handle REST API authentication attempts
 */
add_action('init', 'sentinel_setup_rest_auth_hooks');
function sentinel_setup_rest_auth_hooks()
{
    // Hook into REST API authentication
    add_filter('rest_authentication_errors', 'sentinel_handle_rest_auth', 999);
}

function sentinel_handle_rest_auth($result)
{
    // Only track if this is actually a REST request
    if (!defined('REST_REQUEST') || !REST_REQUEST) {
        return $result;
    }

    // Get current user (if authenticated)
    $user = wp_get_current_user();
    $username = '';
    $user_id = null;

    if ($user && $user->ID) {
        $username = $user->user_login;
        $user_id = $user->ID;
        $is_success = true;
    } else {
        // Try to get username from request
        $username = $_REQUEST['username'] ?? $_SERVER['PHP_AUTH_USER'] ?? 'unknown';
        $is_success = false;
    }

    // Only log if we have authentication context
    if (empty($username) || $username === 'unknown') {
        return $result;
    }

    // Build context for auth counter system
    $context = array(
        'ip' => $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1',
        'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown',
        'username' => $username,
        'user_id' => $user_id,
        'result' => $is_success ? 'success' : 'fail',
        'source' => 'rest-api',
        'timestamp' => time(),
        'endpoint' => $_SERVER['REQUEST_URI'] ?? ''
    );

    // Log the event
    $event_type = $is_success ? 'auth_success' : 'auth_fail';
    sentinel_log_event($event_type, array(
        'username' => $username,
        'user_agent' => $context['user_agent'],
        'ip' => $context['ip'],
        'source' => 'rest-api',
        'endpoint' => $context['endpoint'],
        'success' => $is_success
    ), $user_id ?? 0);

    // Premium: Use auth counter system for incident detection
    if (sentinel_is_premium() && function_exists('sentinel_auth_capture_attempt')) {
        sentinel_auth_capture_attempt($context);
    }

    return $result;
}

/**
 * Log user logout events
 */
function sentinel_log_user_logout()
{
    $current_user = wp_get_current_user();

    if ($current_user && $current_user->ID) {
        sentinel_log_event('user_logout', array(
            'username' => $current_user->user_login,
            'user_id' => $current_user->ID,
            'logout_method' => 'standard',
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? ''
        ), $current_user->ID);
    }
}

// Store user ID before logout
add_action('init', function () {
    if (is_user_logged_in()) {
        $GLOBALS['sentinel_current_user_id'] = get_current_user_id();
    }
});

// Log logout event with correct user ID
add_action('wp_logout', function () {
    $user_id = isset($GLOBALS['sentinel_current_user_id']) ? $GLOBALS['sentinel_current_user_id'] : 0;
    if ($user_id) {
        sentinel_log_event('user_logout', array(), $user_id);
    }
});

add_action('user_register', 'sentinel_log_user_registered');
function sentinel_log_user_registered($user_id)
{
    $user = get_userdata($user_id);
    sentinel_log_event('user_registered', array(
        'username' => $user->user_login,
        'email' => $user->user_email
    ), $user_id);
}

add_action('profile_update', 'sentinel_log_profile_updated');
function sentinel_log_profile_updated($user_id)
{
    sentinel_log_event('profile_updated', array(), $user_id);
}

// Third-party plugin hooks are now registered via integration classes in
// includes/class-sentinel-integrations.php (WooCommerce, CF7, WPForms, Gravity Forms).

// =====================================
// PLUGIN DEACTIVATION CLEANUP
// =====================================

/**
 * Clean up plugin-specific events when plugins are deactivated
 */
add_action('deactivated_plugin', 'sentinel_cleanup_plugin_events_on_deactivation', 10, 2);
function sentinel_cleanup_plugin_events_on_deactivation($plugin, $network_deactivating)
{
    // Map plugin files to their event prefixes via registry
    $plugin_event_map = array();
    if (class_exists('Sentinel_Integration_Registry')) {
        $plugin_event_map = Sentinel_Integration_Registry::get_cleanup_map();
    }

    // Check if this is a plugin we track
    if (!isset($plugin_event_map[$plugin])) {
        return;
    }

    $event_prefix = $plugin_event_map[$plugin];
    $plugin_name = '';

    // Get friendly plugin names
    switch ($event_prefix) {
        case 'woo_':
            $plugin_name = 'WooCommerce';
            break;
        case 'cf7_':
            $plugin_name = 'Contact Form 7';
            break;
        case 'wpf_':
            $plugin_name = 'WPForms';
            break;
        case 'gf_':
            $plugin_name = 'Gravity Forms';
            break;
        case 'yoast_':
            $plugin_name = 'Yoast SEO';
            break;
    }

    // Get existing custom events
    $custom_events = get_option('sentinel_custom_events', array());
    $disabled_events = get_option('sentinel_disabled_events', array());
    $events_removed = 0;

    // Remove all events with this prefix
    foreach ($custom_events as $event_key => $config) {
        if (strpos($event_key, $event_prefix) === 0) {
            unset($custom_events[$event_key]);
            $events_removed++;

            // Also remove from disabled events list
            $disabled_events = array_diff($disabled_events, array($event_key));
        }
    }

    // Update the options
    if ($events_removed > 0) {
        update_option('sentinel_custom_events', $custom_events);
        update_option('sentinel_disabled_events', $disabled_events);

        // Log the cleanup action
        sentinel_log_event('plugin_events_cleaned', array(
            'plugin_name' => $plugin_name,
            'plugin_file' => $plugin,
            'events_removed' => $events_removed,
            'event_prefix' => $event_prefix,
            'cleanup_reason' => 'plugin_deactivated'
        ));
    }
}

/**
 * Clean up plugin-specific events when plugins are deleted
 */
add_action('deleted_plugin', 'sentinel_cleanup_plugin_events_on_deletion', 10, 2);
function sentinel_cleanup_plugin_events_on_deletion($plugin, $deleted)
{
    // Only proceed if deletion was successful
    if (!$deleted) {
        return;
    }

    // Use the same cleanup logic as deactivation
    sentinel_cleanup_plugin_events_on_deactivation($plugin, false);
}

add_action('wp_insert_post', 'sentinel_log_post_published', 10, 3);
function sentinel_log_post_published($post_id, $post, $update)
{
    // Ensure we have a valid post object
    if (!is_object($post) || !isset($post->post_status)) {
        $post = get_post($post_id);
        if (!$post) {
            return; // Invalid post, skip logging
        }
    }

    if ($post->post_status === 'publish' && !$update) {
        sentinel_log_event('post_published', array(
            'post_id' => $post_id,
            'post_type' => $post->post_type,
            'post_title' => $post->post_title
        ), $post->post_author);
    } elseif ($update) {
        sentinel_log_event('post_updated', array(
            'post_id' => $post_id,
            'post_type' => $post->post_type,
            'post_title' => $post->post_title
        ), $post->post_author);
    }
}

add_action('before_delete_post', 'sentinel_log_post_deleted');
function sentinel_log_post_deleted($post_id)
{
    $post = get_post($post_id);
    if ($post && is_object($post) && isset($post->post_type)) {
        sentinel_log_event('post_deleted', array(
            'post_id' => $post_id,
            'post_type' => $post->post_type,
            'post_title' => $post->post_title
        ), $post->post_author);
    }
}

add_action('wp_insert_comment', 'sentinel_log_comment_posted', 10, 2);
function sentinel_log_comment_posted($comment_id, $comment)
{
    // Ensure we have a valid comment object
    if (!is_object($comment) || !isset($comment->comment_post_ID)) {
        $comment = get_comment($comment_id);
        if (!$comment) {
            return; // Invalid comment, skip logging
        }
    }

    sentinel_log_event('comment_posted', array(
        'comment_id' => $comment_id,
        'post_id' => $comment->comment_post_ID
    ), $comment->user_id);
}

add_action('comment_unapproved_to_approved', 'sentinel_log_comment_approved');
function sentinel_log_comment_approved($comment)
{
    // Ensure we have a valid comment object
    if (!is_object($comment) || !isset($comment->comment_ID)) {
        return; // Invalid comment, skip logging
    }

    sentinel_log_event('comment_approved', array(
        'comment_id' => $comment->comment_ID,
        'post_id' => $comment->comment_post_ID
    ), $comment->user_id);
}

add_action('delete_comment', 'sentinel_log_comment_deleted');
function sentinel_log_comment_deleted($comment_id)
{
    $comment = get_comment($comment_id);
    if ($comment && is_object($comment) && isset($comment->comment_post_ID)) {
        sentinel_log_event('comment_deleted', array(
            'comment_id' => $comment_id,
            'post_id' => $comment->comment_post_ID
        ), $comment->user_id);
    }
}

add_action('activated_plugin', 'sentinel_log_plugin_activated');
function sentinel_log_plugin_activated($plugin)
{
    // During self-activation, plugins_loaded has already fired so the event
    // registry is empty. Bootstrap it so the logger accepts the event.
    if (class_exists('Sentinel_Events') && empty(Sentinel_Events::get_all_events())
        && function_exists('sentinel_register_default_events')) {
        sentinel_register_default_events();
    }
    sentinel_log_event('plugin_activated', array(
        'plugin' => $plugin
    ));
}

add_action('deactivated_plugin', 'sentinel_log_plugin_deactivated');
function sentinel_log_plugin_deactivated($plugin)
{
    sentinel_log_event('plugin_deactivated', array(
        'plugin' => $plugin
    ));
}

/**
 * Log plugin and theme updates
 */
function sentinel_log_plugin_theme_updates($upgrader, $hook_extra)
{
    // Skip if not a plugin or theme update
    if (!isset($hook_extra['type'])) {
        return;
    }

    $type = $hook_extra['type'];

    if ($type === 'plugin') {
        // Handle plugin updates
        if (isset($hook_extra['plugins']) && is_array($hook_extra['plugins'])) {
            foreach ($hook_extra['plugins'] as $plugin_file) {
                // Get plugin data
                if (!function_exists('get_plugin_data')) {
                    require_once(ABSPATH . 'wp-admin/includes/plugin.php');
                }

                $plugin_data = get_plugin_data(WP_PLUGIN_DIR . '/' . $plugin_file, false, false);

                sentinel_log_event('plugin_updated', array(
                    'plugin_file' => $plugin_file,
                    'plugin_name' => $plugin_data['Name'] ?? basename($plugin_file),
                    'new_version' => $plugin_data['Version'] ?? 'Unknown',
                    'plugin_uri' => $plugin_data['PluginURI'] ?? '',
                    'author' => $plugin_data['Author'] ?? '',
                    'bulk_update' => count($hook_extra['plugins']) > 1
                ));
            }
        }
    } elseif ($type === 'theme') {
        // Handle theme updates
        if (isset($hook_extra['themes']) && is_array($hook_extra['themes'])) {
            foreach ($hook_extra['themes'] as $theme_slug) {
                $theme = wp_get_theme($theme_slug);

                if ($theme->exists()) {
                    sentinel_log_event('theme_updated', array(
                        'theme_slug' => $theme_slug,
                        'theme_name' => $theme->get('Name'),
                        'new_version' => $theme->get('Version'),
                        'theme_uri' => $theme->get('ThemeURI'),
                        'author' => $theme->get('Author'),
                        'bulk_update' => count($hook_extra['themes']) > 1
                    ));
                }
            }
        }
    }
}

add_action('switch_theme', 'sentinel_log_theme_switched');
function sentinel_log_theme_switched($new_name, $new_theme = null)
{
    // Get theme details for logging
    $theme_data = array(
        'theme_name' => $new_name,
        'theme_version' => ''
    );

    // If theme object is available, get additional details
    if ($new_theme && is_object($new_theme)) {
        $theme_data['theme_version'] = $new_theme->get('Version');
        $theme_data['theme_author'] = $new_theme->get('Author');
    }

    sentinel_log_event('theme_switched', $theme_data);
}

/**
 * Check maintenance mode status on init
 */
function sentinel_check_maintenance_mode()
{
    $maintenance_file = ABSPATH . '.maintenance';
    $current_state = file_exists($maintenance_file);

    // Get stored state from previous check
    $transient_key = 'sentinel_maintenance_mode_state';
    $previous_state_raw = get_transient($transient_key);

    // Check if transient actually exists (false could mean "doesn't exist" or "value is false")
    $transient_exists = get_option('_transient_' . $transient_key) !== false;

    // Convert to boolean to ensure type consistency
    $previous_state = $transient_exists ? (bool)$previous_state_raw : false;

    // If this is the first check or transient expired, just store the current state
    if (!$transient_exists) {
        set_transient($transient_key, $current_state, HOUR_IN_SECONDS);
        return;
    }

    // Check for state changes
    if ($current_state !== $previous_state) {
        if ($current_state) {
            // Maintenance mode was enabled
            $maintenance_data = array(
                'maintenance_file_path' => $maintenance_file,
                'detected_at' => current_time('mysql'),
                'file_size' => file_exists($maintenance_file) ? filesize($maintenance_file) : 0
            );

            // Try to read maintenance file contents for additional info
            if (is_readable($maintenance_file)) {
                $contents = file_get_contents($maintenance_file);
                if ($contents !== false) {
                    $maintenance_data['file_contents'] = substr($contents, 0, 500); // Limit to 500 chars

                    // Try to extract timestamp if it's in standard WordPress format
                    if (preg_match('/(\d+)/', $contents, $matches)) {
                        $maintenance_data['maintenance_start_time'] = date('Y-m-d H:i:s', $matches[1]);
                    }
                }
            }

            sentinel_log_event('maintenance_mode_enabled', $maintenance_data);
        } else {
            // Maintenance mode was disabled
            $maintenance_data = array(
                'maintenance_file_path' => $maintenance_file,
                'detected_at' => current_time('mysql'),
                'previous_check' => $previous_state ? 'enabled' : 'disabled'
            );

            sentinel_log_event('maintenance_mode_disabled', $maintenance_data);
        }

        // Update stored state
        set_transient('sentinel_maintenance_mode_state', $current_state, HOUR_IN_SECONDS);
    }
}

/**
 * Check maintenance mode status on shutdown (catches changes during request)
 */
function sentinel_check_maintenance_mode_shutdown()
{
    // Only run this check occasionally to avoid performance impact
    if (wp_rand(1, 10) === 1) {
        sentinel_check_maintenance_mode();
    }
}

add_action('upgrader_process_complete', 'sentinel_log_core_upgraded', 10, 2);
function sentinel_log_core_upgraded($upgrader, $hook_extra)
{
    // Only log WordPress core upgrades, not plugin/theme upgrades
    if (isset($hook_extra['type']) && $hook_extra['type'] === 'core') {
        // Get WordPress version information
        global $wp_version;
        $upgrade_data = array(
            'wp_version' => $wp_version,
            'upgrade_type' => $hook_extra['action'] ?? 'upgrade'
        );

        sentinel_log_event('core_upgraded', $upgrade_data);
    }
}

add_action('updated_option', 'sentinel_log_option_updated', 10, 3);
function sentinel_log_option_updated($option_name, $old_value, $value)
{
    // Skip logging certain internal WordPress options to avoid spam
    $skip_options = array(
        '_transient_',
        '_site_transient_',
        'cron',
        'recovery_mode_email_last_sent',
        'auto_core_update_notified',
        'recovery_mode_email_last_sent',
        'theme_switched',
        'stylesheet_switched',
        'template_switched',
        'sentinel_memory_analysis_history',
        'sentinel_performance_samples',
        'sentinel_disabled_events'
    );

    // Check if this option should be skipped
    foreach ($skip_options as $skip_pattern) {
        if (strpos($option_name, $skip_pattern) !== false) {
            return;
        }
    }

    // Prepare option data for logging with better array handling
    $format_value_preview = function ($val) {
        if (is_string($val)) {
            return substr($val, 0, 100);
        } elseif (is_array($val)) {
            $count = count($val);
            if ($count === 0) {
                return 'Empty array';
            } elseif ($count === 1) {
                return 'Array with 1 item';
            } else {
                return "Array with {$count} items";
            }
        } elseif (is_bool($val)) {
            return $val ? 'true' : 'false';
        } elseif (is_numeric($val)) {
            return (string)$val;
        } else {
            return gettype($val);
        }
    };

    $option_data = array(
        'option_name' => $option_name,
        'old_value_preview' => $format_value_preview($old_value),
        'new_value_preview' => $format_value_preview($value),
        'value_type' => gettype($value)
    );

    sentinel_log_event('option_updated', $option_data);
}

// =====================================
// ERROR HANDLING HOOKS
// =====================================

// WordPress Login Failures - Integrated with Auth Counters
add_action('wp_login_failed', 'sentinel_handle_login_failed');
function sentinel_handle_login_failed($username, $error = null)
{
    // Prevent duplicate logging within the same request
    static $logged_failures = array();
    $failure_key = md5($username . $_SERVER['REMOTE_ADDR'] . $_SERVER['REQUEST_TIME']);

    if (isset($logged_failures[$failure_key])) {
        return; // Already logged this failure
    }

    $logged_failures[$failure_key] = true;

    // Build context for auth counter system
    $context = array(
        'ip' => $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1',
        'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown',
        'username' => $username,
        'user_id' => null,
        'result' => 'fail',
        'source' => 'wp-login',
        'timestamp' => time(),
        'error_code' => is_wp_error($error) ? $error->get_error_code() : 'unknown'
    );

    // Log the basic event
    sentinel_log_event('login_failed', array(
        'username' => $username,
        'user_agent' => $context['user_agent'],
        'ip' => $context['ip'],
        'error_code' => $context['error_code']
    ));

    // Premium: Use auth counter system for incident detection
    if (sentinel_is_premium() && function_exists('sentinel_auth_capture_attempt')) {
        sentinel_auth_capture_attempt($context);
    }
}

// Bad Authentication Cookies
add_action('auth_cookie_bad_username', 'sentinel_log_bad_auth_cookie');
add_action('auth_cookie_bad_hash', 'sentinel_log_bad_auth_cookie');
function sentinel_log_bad_auth_cookie($cookie_elements)
{
    sentinel_log_event('auth_cookie_bad', array(
        'cookie_data' => is_array($cookie_elements) ? json_encode($cookie_elements) : $cookie_elements
    ));
}

// WordPress Database Errors
add_action('wp_insert_post_data', 'sentinel_check_db_errors_on_post', 10, 2);
function sentinel_check_db_errors_on_post($data, $postarr)
{
    global $wpdb;
    if (!empty($wpdb->last_error)) {
        // Prevent logging database errors during Sentinel's own database operations
        static $db_error_logging = false;
        if ($db_error_logging) {
            return $data; // Prevent recursive database error logging
        }

        // Throttle database error logging to prevent feedback loops
        static $last_db_error_time = 0;
        $current_time = time();
        if ($current_time - $last_db_error_time < 5) {
            return $data; // Only log database errors every 5 seconds at most
        }
        $last_db_error_time = $current_time;

        // Check if this is a Sentinel-related query to prevent feedback loop
        if (strpos($wpdb->last_query, 'sentinel_logs') !== false) {
            return $data;
        }

        $db_error_logging = true;
        sentinel_log_event('wp_database_error', array(
            'error_message' => $wpdb->last_error,
            'last_query' => $wpdb->last_query,
            'context' => 'post_insert'
        ));
        $db_error_logging = false;
    }
    return $data;
}

/**
 * Set up database error logging
 */
function sentinel_setup_db_error_logging()
{
    global $wpdb;

    // Only set up if not already done
    if (!isset($wpdb->sentinel_error_handler_set)) {
        $wpdb->sentinel_error_handler_set = true;

        // Hook into WordPress database error handling
        add_action('wp_error_added', 'sentinel_log_wp_error', 10, 4);

        // Check for database errors on shutdown
        add_action('shutdown', 'sentinel_check_db_errors');
    }
}

/**
 * Log WordPress errors
 */
function sentinel_log_wp_error($code, $message, $data, $wp_error)
{
    // Skip if this is a database error (we handle those separately)
    if (strpos($code, 'db_') === 0) {
        return;
    }

    sentinel_log_event('wp_error', array(
        'error_code' => $code,
        'message' => $message,
        'data' => $data
    ));
}

/**
 * Check for database errors on shutdown
 */
function sentinel_check_db_errors()
{
    global $wpdb;

    if (!empty($wpdb->last_error)) {
        // Prevent logging database errors during Sentinel's own database operations
        static $db_error_logging = false;
        if ($db_error_logging) {
            return; // Prevent recursive database error logging
        }

        // Throttle database error logging to prevent feedback loops
        static $last_db_error_time = 0;
        $current_time = time();
        if ($current_time - $last_db_error_time < 5) {
            return; // Only log database errors every 5 seconds at most
        }
        $last_db_error_time = $current_time;

        // Check if this is a Sentinel-related query to prevent feedback loop
        if (strpos($wpdb->last_query, 'sentinel_logs') !== false) {
            return;
        }

        $db_error_logging = true;
        sentinel_log_event('wp_database_error', array(
            'error_message' => $wpdb->last_error,
            'last_query' => $wpdb->last_query,
            'url' => $_SERVER['REQUEST_URI'] ?? 'unknown'
        ));
        $db_error_logging = false;
    }
}

// PHP Error Handler
add_action('init', 'sentinel_setup_php_error_handler');
function sentinel_setup_php_error_handler()
{
    // Only set up if not already handling errors
    if (!defined('SENTINEL_ERROR_HANDLING')) {
        define('SENTINEL_ERROR_HANDLING', true);

        // Set custom error handler
        set_error_handler('sentinel_php_error_handler');

        // Register shutdown function to catch fatal errors
        register_shutdown_function('sentinel_php_fatal_error_handler');
    }
}

function sentinel_php_error_handler($errno, $errstr, $errfile, $errline)
{
    // Don't log errors if error reporting is turned off
    if (!(error_reporting() & $errno)) {
        return false;
    }

    $event_key = 'php_notice';
    $priority = 'info';

    switch ($errno) {
        case E_ERROR:
        case E_CORE_ERROR:
        case E_COMPILE_ERROR:
        case E_USER_ERROR:
            $event_key = 'php_fatal_error';
            $priority = 'critical';
            break;
        case E_WARNING:
        case E_CORE_WARNING:
        case E_COMPILE_WARNING:
        case E_USER_WARNING:
            $event_key = 'php_warning';
            $priority = 'high';
            break;
        case E_NOTICE:
        case E_USER_NOTICE:
            $event_key = 'php_notice';
            $priority = 'low';
            break;
        case E_DEPRECATED:
        case E_USER_DEPRECATED:
            $event_key = 'php_deprecated';
            $priority = 'high';
            break;
    }

    sentinel_log_event($event_key, array(
        'error_message' => $errstr,
        'error_file' => $errfile,
        'error_line' => $errline,
        'error_type' => $errno,
        'url' => $_SERVER['REQUEST_URI'] ?? 'Unknown'
    ));

    // Don't interfere with WordPress error handling
    return false;
}

function sentinel_php_fatal_error_handler()
{
    $error = error_get_last();
    if ($error !== null && in_array($error['type'], [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR])) {
        sentinel_log_event('php_fatal_error', array(
            'error_message' => $error['message'],
            'error_file' => $error['file'],
            'error_line' => $error['line'],
            'error_type' => $error['type'],
            'url' => $_SERVER['REQUEST_URI'] ?? 'Unknown'
        ));
    }

    // Log performance metrics for weekly digest
    sentinel_log_performance_metrics();
}

// File Upload Errors
add_filter('wp_handle_upload_prefilter', 'sentinel_check_upload_errors');
function sentinel_check_upload_errors($file)
{
    if (isset($file['error']) && $file['error'] != UPLOAD_ERR_OK) {
        $error_messages = array(
            UPLOAD_ERR_INI_SIZE => 'File exceeds upload_max_filesize',
            UPLOAD_ERR_FORM_SIZE => 'File exceeds MAX_FILE_SIZE',
            UPLOAD_ERR_PARTIAL => 'File was only partially uploaded',
            UPLOAD_ERR_NO_FILE => 'No file was uploaded',
            UPLOAD_ERR_NO_TMP_DIR => 'Missing temporary folder',
            UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk',
            UPLOAD_ERR_EXTENSION => 'File upload stopped by extension'
        );

        sentinel_log_event('file_upload_error', array(
            'error_code' => $file['error'],
            'error_message' => isset($error_messages[$file['error']]) ? $error_messages[$file['error']] : 'Unknown upload error',
            'filename' => $file['name'] ?? 'Unknown',
            'filesize' => $file['size'] ?? 0
        ));
    }
    return $file;
}

// Plugin Activation Errors
add_action('activated_plugin', 'sentinel_check_plugin_activation_errors');
function sentinel_check_plugin_activation_errors($plugin)
{
    // Check if there were any errors during activation
    $errors = get_transient('sentinel_plugin_activation_errors');
    if ($errors) {
        sentinel_log_event('plugin_activation_error', array(
            'plugin' => $plugin,
            'error_message' => $errors
        ));
        delete_transient('sentinel_plugin_activation_errors');
    }
}

// Catch WordPress die() calls that might indicate errors
add_action('wp_die_handler', 'sentinel_wp_die_handler');
function sentinel_wp_die_handler($handler)
{
    // Store the original handler
    $original_handler = $handler;

    // Return a custom handler that logs before dying
    return function ($message, $title = '', $args = array()) use ($original_handler) {
        // Log the wp_die event
        sentinel_log_event('wp_config_error', array(
            'error_message' => is_string($message) ? $message : 'WordPress died',
            'error_title' => $title,
            'context' => 'wp_die',
            'url' => $_SERVER['REQUEST_URI'] ?? 'Unknown'
        ));

        // Call the original handler
        return call_user_func($original_handler, $message, $title, $args);
    };
}

// HTTP Error Handling
add_action('wp', 'sentinel_check_http_errors');
function sentinel_check_http_errors()
{
    // Check if this is a 404 error
    if (is_404()) {
        sentinel_log_event('http_404_error', array(
            'requested_url' => $_SERVER['REQUEST_URI'] ?? 'Unknown',
            'referer' => $_SERVER['HTTP_REFERER'] ?? 'Direct',
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown'
        ));
    }
}

// WordPress Remote Request Errors
add_action('http_api_debug', 'sentinel_log_remote_request_errors', 10, 5);
function sentinel_log_remote_request_errors($response, $context, $class, $args, $url)
{
    if (is_wp_error($response)) {
        sentinel_log_event('wp_remote_error', array(
            'error_message' => $response->get_error_message(),
            'error_code' => $response->get_error_code(),
            'url' => $url,
            'context' => $context
        ));
    }
}

// WordPress Cron Errors
add_action('wp_cron_api_error', 'sentinel_log_cron_errors');
function sentinel_log_cron_errors($error)
{
    if (is_wp_error($error)) {
        sentinel_log_event('wp_cron_error', array(
            'error_message' => $error->get_error_message(),
            'error_code' => $error->get_error_code()
        ));
    }
}

// Include comprehensive memory monitoring
require_once plugin_dir_path(__FILE__) . 'includes/class-sentinel-memory-monitor.php';

// Legacy memory check (kept for backward compatibility)
add_action('init', 'sentinel_check_memory_usage');
function sentinel_check_memory_usage()
{
    // Note: This is now handled by the comprehensive Sentinel_Memory_Monitor class
    // This function is kept for backward compatibility only

    $settings = get_option('sentinel_log_management', array());
    if (empty($settings['memory_monitoring_enabled'])) {
        return; // Memory monitoring disabled
    }

    $memory_limit = ini_get('memory_limit');
    $memory_usage = memory_get_usage(true);

    // Convert memory limit to bytes
    $limit_bytes = sentinel_convert_to_bytes($memory_limit);

    // If using more than 90% of memory limit, log a warning (legacy behavior)
    if ($memory_usage > ($limit_bytes * 0.9)) {
        sentinel_log_event('wp_memory_error', array(
            'memory_usage' => $memory_usage,
            'memory_limit' => $memory_limit,
            'percentage_used' => round(($memory_usage / $limit_bytes) * 100, 2),
            'url' => $_SERVER['REQUEST_URI'] ?? 'Unknown',
            'note' => 'Legacy memory check - comprehensive monitoring active'
        ));
    }
}

/**
 * Log performance metrics for weekly digest reports
 * This function is called at shutdown to capture real performance data
 * Stores samples in a transient instead of the activity log
 */
function sentinel_log_performance_metrics()
{
    $settings = get_option('sentinel_log_management', array());
    if (empty($settings['memory_monitoring_enabled'])) {
        return; // Performance monitoring disabled
    }

    // Implement sampling to reduce storage - only log 1 in 10 requests
    $sample_rate = 10; // Only log every 10th request
    $request_hash = md5($_SERVER['REQUEST_URI'] ?? 'unknown' . time());
    $should_log = (ord($request_hash[0]) % $sample_rate) === 0;
    if (!$should_log) {
        return; // Skip this request
    }
    // Gather metrics
    $peak_memory = memory_get_peak_usage(true);
    global $wpdb;
    $db_queries = $wpdb->num_queries;
    $response_time_ms = 0;
    if (isset($_SERVER['REQUEST_TIME_FLOAT'])) {
        $response_time_ms = round((microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000, 2);
    }
    $sample = array(
        'memory_bytes' => $peak_memory,
        'db_queries' => $db_queries,
        'response_time_ms' => $response_time_ms,
        'timestamp' => time(),
        'url' => $_SERVER['REQUEST_URI'] ?? 'Unknown',
    );
    // Store in transient (array of samples) with hard cap to prevent unbounded growth.
    $samples = get_transient('sentinel_performance_samples');
    if (!is_array($samples)) {
        $samples = array();
    }
    $samples[] = $sample;

    $max_samples = 2000;
    if (count($samples) > $max_samples) {
        $samples = array_slice($samples, -$max_samples);
    }

    set_transient('sentinel_performance_samples', $samples, 691200);
}

// Helper function to convert memory limit to bytes
function sentinel_convert_to_bytes($value)
{
    $value = trim($value);
    $last = strtolower($value[strlen($value) - 1]);
    $value = (int) $value;

    switch ($last) {
        case 'g':
            $value *= 1024;
        case 'm':
            $value *= 1024;
        case 'k':
            $value *= 1024;
    }

    return $value;
}

// Helper function to format activity messages for display
if (!function_exists('sentinel_format_activity_message')) {
    /**
     * Format activity message for display
     *
     * @param object $activity   The activity object from database
     * @param array $usernames   Array of user IDs to usernames
     * @return string            Formatted message
     */
    function sentinel_format_activity_message($activity, $usernames)
    {
        $username = isset($usernames[$activity->user_id]) ? $usernames[$activity->user_id] : 'Unknown User';
        $data = (!empty($activity->data) && is_string($activity->data)) ? json_decode($activity->data, true) : array();

        switch ($activity->event_key) {
            case 'user_login':
                $login_method = isset($data['login_method']) ? $data['login_method'] : '';
                $method_text = $login_method === 'standard' ? '' : ' (' . ucfirst($login_method) . ')';
                return sprintf('%s logged in%s', $username, $method_text);

            case 'user_logout':
                return sprintf('%s logged out', $username);

            case 'user_registered':
                $user_email = isset($data['user_email']) ? $data['user_email'] : '';
                $email_text = $user_email ? ' (' . $user_email . ')' : '';
                return sprintf('New user %s registered%s', $username, $email_text);

            case 'profile_updated':
                return sprintf('%s updated their profile', $username);

            case 'post_published':
                $post_title = isset($data['post_title']) ? $data['post_title'] : 'Unknown Post';
                $post_type = isset($data['post_type']) ? $data['post_type'] : 'post';
                return sprintf('%s published "%s" (%s)', $username, $post_title, $post_type);

            case 'post_updated':
                $post_title = isset($data['post_title']) ? $data['post_title'] : 'Unknown Post';
                $post_type = isset($data['post_type']) ? $data['post_type'] : 'post';
                return sprintf('%s updated "%s" (%s)', $username, $post_title, $post_type);

            case 'post_deleted':
                $post_title = isset($data['post_title']) ? $data['post_title'] : 'Unknown Post';
                $post_type = isset($data['post_type']) ? $data['post_type'] : 'post';
                return sprintf('%s deleted "%s" (%s)', $username, $post_title, $post_type);

            case 'comment_posted':
                $post_title = isset($data['post_title']) ? $data['post_title'] : 'Unknown Post';
                return sprintf('%s posted a comment on "%s"', $username, $post_title);

            case 'comment_approved':
                $post_title = isset($data['post_title']) ? $data['post_title'] : 'Unknown Post';
                return sprintf('Comment by %s on "%s" was approved', $username, $post_title);

            case 'comment_deleted':
                $post_title = isset($data['post_title']) ? $data['post_title'] : 'Unknown Post';
                return sprintf('Comment by %s on "%s" was deleted', $username, $post_title);

            case 'plugin_activated':
                $plugin = isset($data['plugin']) ? $data['plugin'] : 'Unknown Plugin';
                return sprintf('Plugin %s was activated', $plugin);

            case 'plugin_deactivated':
                $plugin = isset($data['plugin']) ? $data['plugin'] : 'Unknown Plugin';
                return sprintf('Plugin %s was deactivated', $plugin);

            case 'plugin_updated':
                $plugin_name = isset($data['plugin_name']) ? $data['plugin_name'] : 'Unknown Plugin';
                $new_version = isset($data['new_version']) ? $data['new_version'] : 'Unknown Version';
                $bulk_update = isset($data['bulk_update']) && $data['bulk_update'];
                $bulk_text = $bulk_update ? ' (bulk update)' : '';
                return sprintf('Plugin %s updated to version %s%s', $plugin_name, $new_version, $bulk_text);

            case 'theme_updated':
                $theme_name = isset($data['theme_name']) ? $data['theme_name'] : 'Unknown Theme';
                $new_version = isset($data['new_version']) ? $data['new_version'] : 'Unknown Version';
                $bulk_update = isset($data['bulk_update']) && $data['bulk_update'];
                $bulk_text = $bulk_update ? ' (bulk update)' : '';
                return sprintf('Theme %s updated to version %s%s', $theme_name, $new_version, $bulk_text);

            case 'maintenance_mode_enabled':
                $file_size = isset($data['file_size']) ? $data['file_size'] : 0;
                return sprintf('Site entered maintenance mode (%d bytes)', $file_size);

            case 'maintenance_mode_disabled':
                return 'Site exited maintenance mode';

            case 'theme_switched':
                $theme_name = isset($data['theme_name']) ? $data['theme_name'] : 'Unknown Theme';
                $theme_version = isset($data['theme_version']) ? $data['theme_version'] : '';
                $version_text = $theme_version ? ' (v' . $theme_version . ')' : '';
                return sprintf('Theme switched to "%s"%s', $theme_name, $version_text);

            case 'core_upgraded':
                $wp_version = isset($data['wp_version']) ? $data['wp_version'] : 'Unknown Version';
                $upgrade_type = isset($data['upgrade_type']) ? $data['upgrade_type'] : 'upgrade';
                return sprintf('WordPress core %s to version %s', $upgrade_type, $wp_version);

            case 'option_updated':
                $option_name = isset($data['option_name']) ? $data['option_name'] : 'Unknown Option';
                $old_preview = isset($data['old_value_preview']) ? $data['old_value_preview'] : '';
                $new_preview = isset($data['new_value_preview']) ? $data['new_value_preview'] : '';

                // Create a more readable message
                if ($old_preview && $new_preview && $old_preview !== $new_preview) {
                    return sprintf('Option "%s" changed from "%s" to "%s"', $option_name, $old_preview, $new_preview);
                } else {
                    return sprintf('Option "%s" was updated', $option_name);
                }

            case 'logs_cleaned':
                $deleted_count = isset($data['deleted_count']) ? $data['deleted_count'] : 0;
                return sprintf('System cleaned up %d old log entries', $deleted_count);

            case 'tables_optimized':
                $tables_count = isset($data['tables_optimized']) ? $data['tables_optimized'] : 0;
                return sprintf('System optimized %d database tables', $tables_count);

                // PHP Error Events
            case 'php_fatal_error':
                return 'PHP Fatal Error';

            case 'php_warning':
                return 'PHP Warning';

            case 'php_notice':
                return 'PHP Notice';

            case 'php_deprecated':
                return 'PHP Deprecated';

                // WordPress Core Errors
            case 'wp_database_error':
                return 'Database Error';

            case 'wp_memory_error':
                return 'Memory Limit Exceeded';

            case 'wp_permission_error':
                return 'Permission Error';

            case 'wp_config_error':
                return 'Configuration Error';

                // Security & Authentication Errors
            case 'login_failed':
                $attempted_user = isset($data['username']) ? $data['username'] : 'Unknown User';
                return sprintf('Failed login attempt for user "%s"', $attempted_user);

            case 'auth_cookie_bad':
                return 'Bad authentication cookie detected';

            case 'nonce_verification_failed':
                $action = isset($data['action']) ? $data['action'] : 'unknown action';
                return sprintf('Nonce verification failed for "%s"', $action);

                // Premium: Action Engine Events
            case 'security_action_triggered':
                $action_mode = isset($data['action_mode']) ? ucfirst($data['action_mode']) : 'Unknown';
                $incident_type = isset($data['incident_type']) ? str_replace('security.', '', $data['incident_type']) : 'threat';
                return sprintf('Security action "%s" triggered for %s detection', $action_mode, $incident_type);

            case 'security_throttle_applied':
                $delay = isset($data['delay_seconds']) ? $data['delay_seconds'] : 'unknown';
                return sprintf('Request throttled by %s seconds due to suspicious activity', $delay);

            case 'security_ip_blocked':
                $duration = isset($data['duration_hours']) ? $data['duration_hours'] : 'unknown';
                $incident_type = isset($data['incident_type']) ? str_replace('security.', '', $data['incident_type']) : 'threat';
                return sprintf('IP temporarily blocked for %s hours due to %s detection', $duration, $incident_type);

            case 'security_blocked_attempt':
                $attempted_user = isset($data['username']) ? $data['username'] : 'Unknown User';
                return sprintf('Blocked access attempt from restricted IP for user "%s"', $attempted_user);

            case 'permission_denied':
                $action = isset($data['action']) ? $data['action'] : 'unknown action';
                return sprintf('Access denied for "%s"', $action);

                // HTTP & API Errors
            case 'http_404_error':
                $url = isset($data['url']) ? $data['url'] : 'Unknown URL';
                return sprintf('404 Error for "%s"', $url);

            case 'http_500_error':
                $url = isset($data['url']) ? $data['url'] : 'Unknown URL';
                return sprintf('Internal Server Error for "%s"', $url);

            case 'wp_remote_error':
                $error_msg = isset($data['error_message']) ? substr($data['error_message'], 0, 100) : 'Unknown remote error';
                return sprintf('Remote Request Error: %s', $error_msg);

            case 'wp_cron_error':
                $error_msg = isset($data['error_message']) ? substr($data['error_message'], 0, 100) : 'Unknown cron error';
                return sprintf('Cron Job Error: %s', $error_msg);

                // Plugin & Theme Errors
            case 'plugin_error':
                $plugin_name = isset($data['plugin']) ? $data['plugin'] : 'unknown plugin';
                return sprintf('Plugin Error: %s', $plugin_name);

            case 'theme_error':
                $theme_name = isset($data['theme']) ? $data['theme'] : 'unknown theme';
                return sprintf('Theme Error: %s', $theme_name);

            case 'plugin_activation_error':
                $plugin_name = isset($data['plugin']) ? $data['plugin'] : 'unknown plugin';
                return sprintf('Plugin Activation Error: %s', $plugin_name);

                // File & Upload Errors
            case 'file_upload_error':
                return 'File Upload Error';

            case 'file_permission_error':
                return sprintf('File Permission Error: %s', isset($data['error_message']) ? $data['error_message'] : 'file permission denied');

                // Rate Limiting Events
            case 'rate_limit_warning':
                $severity = isset($data['severity']) ? strtoupper($data['severity']) : 'WARNING';
                $percentage = isset($data['percentage']) ? $data['percentage'] : 'N/A';
                $time_window = isset($data['time_window']) ? $data['time_window'] : 'unknown';
                return sprintf('%s: Rate limit approaching (%s%% of %s limit)', $severity, $percentage, $time_window);

            case 'rate_limit_exceeded':
                $time_window = isset($data['time_window']) ? $data['time_window'] : 'unknown';
                $count = isset($data['current_count']) ? $data['current_count'] : 'N/A';
                $limit = isset($data['limit']) ? $data['limit'] : 'N/A';
                $behavior_desc = isset($data['behavior_description']) ? $data['behavior_description'] : 'Rate limiting active';
                return sprintf('Rate limit exceeded (%s/%s %s) - %s', $count, $limit, $time_window, $behavior_desc);

                // Audit log access
            case 'log_accessed':
                return sprintf('%s accessed the logs', $username);

                // File monitoring events
            case 'file_modified':
                $file_name = isset($data['file_name']) ? $data['file_name'] : 'Unknown file';
                $size_change_text = isset($data['size_change_text']) ? $data['size_change_text'] : '';
                $change_summary = isset($data['change_summary']) ? $data['change_summary'] : '';

                // Base message (no inline button)
                return sprintf(
                    'File monitoring detected %s was modified%s%s',
                    $file_name,
                    $size_change_text ? ' (' . $size_change_text . ')' : '',
                    $change_summary ? ' - ' . $change_summary : ''
                );

                // Performance / Memory monitor events
            case 'memory_early_warning':
                $pct = isset($data['usage_percent']) ? $data['usage_percent'] : null;
                $used = isset($data['memory_usage_formatted']) ? $data['memory_usage_formatted'] : '';
                $limit = isset($data['memory_limit_formatted']) ? $data['memory_limit_formatted'] : '';
                return sprintf(
                    'Memory early warning%s%s',
                    $pct !== null ? ' (' . round($pct, 1) . '%)' : '',
                    ($used && $limit) ? ' - ' . $used . ' / ' . $limit : ''
                );

            case 'memory_warning':
                $pct = isset($data['usage_percent']) ? $data['usage_percent'] : null;
                $used = isset($data['memory_usage_formatted']) ? $data['memory_usage_formatted'] : '';
                $limit = isset($data['memory_limit_formatted']) ? $data['memory_limit_formatted'] : '';
                return sprintf(
                    'Memory warning%s%s',
                    $pct !== null ? ' (' . round($pct, 1) . '%)' : '',
                    ($used && $limit) ? ' - ' . $used . ' / ' . $limit : ''
                );

            case 'memory_critical':
                $pct = isset($data['usage_percent']) ? $data['usage_percent'] : null;
                $used = isset($data['memory_usage_formatted']) ? $data['memory_usage_formatted'] : '';
                $limit = isset($data['memory_limit_formatted']) ? $data['memory_limit_formatted'] : '';
                return sprintf(
                    'Memory critical%s%s',
                    $pct !== null ? ' (' . round($pct, 1) . '%)' : '',
                    ($used && $limit) ? ' - ' . $used . ' / ' . $limit : ''
                );

            case 'memory_auto_optimization':
                return 'Memory auto optimization executed';

            case 'memory_leak_warning':
                $confidence = isset($data['confidence_score']) ? $data['confidence_score'] : 0;
                $avg_increase = isset($data['average_increase_formatted']) ? $data['average_increase_formatted'] : '';
                return sprintf(
                    'Potential memory leak detected%s%s',
                    $confidence ? ' (' . $confidence . '% confidence)' : '',
                    $avg_increase ? ' - ' . $avg_increase . ' average increase' : ''
                );

                // WordPress Error Events
            case 'wp_error':
                $error_code = isset($data['error_code']) ? $data['error_code'] : 'unknown';
                $error_message = isset($data['message']) ? $data['message'] : 'General WordPress error';
                return sprintf('WordPress error occurred: %s (%s)', $error_message, $error_code);

                // Privacy & GDPR Events
            case 'user_data_anonymized':
                $anonymized_count = isset($data['anonymized_count']) ? $data['anonymized_count'] : 0;
                $force_all = isset($data['force_all']) ? $data['force_all'] : false;
                $mode = $force_all ? 'Manual anonymization' : 'Scheduled anonymization';
                return sprintf('%s completed: %d logs anonymized for privacy compliance', $mode, $anonymized_count);

                // Contact Form 7 Events
            case 'cf7_form_submitted':
                $form_title = isset($data['form_title']) ? $data['form_title'] : 'Contact Form';
                $form_id = isset($data['form_id']) ? ' (ID: ' . $data['form_id'] . ')' : '';
                return sprintf('Contact form "%s"%s was successfully submitted', $form_title, $form_id);

            case 'cf7_form_failed':
                $form_title = isset($data['form_title']) ? $data['form_title'] : 'Contact Form';
                $form_id = isset($data['form_id']) ? ' (ID: ' . $data['form_id'] . ')' : '';
                $failure_reason = isset($data['failure_reason']) ? $data['failure_reason'] : 'submission failed';
                return sprintf('Contact form "%s"%s submission failed (%s)', $form_title, $form_id, $failure_reason);

                // WPForms Events
            case 'wpf_form_submitted':
                $form_title = isset($data['form_title']) ? $data['form_title'] : 'WPForm';
                $form_id = isset($data['form_id']) ? ' (ID: ' . $data['form_id'] . ')' : '';
                return sprintf('WPForms form "%s"%s was successfully submitted', $form_title, $form_id);

            case 'wpf_payment_completed':
                $form_title = isset($data['form_title']) ? $data['form_title'] : 'WPForm';
                $form_id = isset($data['form_id']) ? ' (ID: ' . $data['form_id'] . ')' : '';
                $payment_id = isset($data['payment_id']) ? ' (Payment ID: ' . $data['payment_id'] . ')' : '';
                return sprintf('WPForms payment completed for form "%s"%s%s', $form_title, $form_id, $payment_id);

                // Plugin Management Events
            case 'plugin_events_cleaned':
                $plugin_name = isset($data['plugin_name']) ? $data['plugin_name'] : 'Unknown Plugin';
                $events_count = isset($data['events_removed']) ? $data['events_removed'] : 0;
                $reason = isset($data['cleanup_reason']) && $data['cleanup_reason'] === 'plugin_deactivated' ? 'deactivated' : 'deleted';
                return sprintf('%s %s - automatically removed %d tracking events', $plugin_name, $reason, $events_count);

                // Authentication Events
            case 'auth_success':
                $username = isset($data['username']) ? $data['username'] : $username;
                $source = isset($data['source']) ? $data['source'] : 'standard login';
                $source_text = $source === 'standard' ? '' : ' (' . ucfirst($source) . ')';
                return sprintf('%s successfully authenticated%s', $username, $source_text);

            case 'auth_fail':
                $attempted_user = isset($data['username']) ? $data['username'] : 'Unknown User';
                $source = isset($data['source']) ? $data['source'] : 'standard login';
                $source_text = $source === 'standard' ? '' : ' (' . ucfirst($source) . ')';
                return sprintf('Authentication failed for "%s"%s', $attempted_user, $source_text);

                // Yoast SEO Events
            case 'yoast_meta_updated':
                $post_title = isset($data['post_title']) ? $data['post_title'] : 'Unknown Post';
                $meta_key = isset($data['meta_key']) ? $data['meta_key'] : '';

                // Map Yoast meta keys to friendly names
                $friendly_names = array(
                    '_yoast_wpseo_title' => 'SEO Title',
                    '_yoast_wpseo_metadesc' => 'Meta Description',
                    '_yoast_wpseo_focuskw' => 'Focus Keyword',
                    '_yoast_wpseo_canonical' => 'Canonical URL',
                    '_yoast_wpseo_opengraph-title' => 'Open Graph Title',
                    '_yoast_wpseo_opengraph-description' => 'Open Graph Description',
                    '_yoast_wpseo_twitter-title' => 'Twitter Title',
                    '_yoast_wpseo_twitter-description' => 'Twitter Description'
                );

                $friendly_name = isset($friendly_names[$meta_key]) ? $friendly_names[$meta_key] : 'SEO Meta';
                return sprintf('%s updated %s for "%s"', $username, $friendly_name, $post_title);

            case 'yoast_score_changed':
                $post_title = isset($data['post_title']) ? $data['post_title'] : 'Unknown Post';
                $score_type = isset($data['score_type']) ? $data['score_type'] : 'SEO score';
                return sprintf('%s changed %s for "%s"', $username, $score_type, $post_title);

            case 'yoast_schema_updated':
                $post_title = isset($data['post_title']) ? $data['post_title'] : 'Unknown Post';
                return sprintf('%s updated schema markup for "%s"', $username, $post_title);

            case 'yoast_bulk_action':
                $action = isset($data['action']) ? $data['action'] : 'bulk SEO action';
                $count = isset($data['count']) ? $data['count'] : 'multiple';
                return sprintf('%s performed %s on %s items', $username, $action, $count);

                // Custom events will fall through to default case

            default:
                return sprintf('%s performed %s', $username, $activity->event_key);
        }
    }
}

// Helper function to get activity icon for event type
if (!function_exists('sentinel_get_activity_icon')) {
    /**
     * Get activity icon for event type
     *
     * @param string $event_key The event key
     * @return string           Icon name
     */
    function sentinel_get_activity_icon($event_key)
    {
        $icons = array(
            'user_login' => 'admin-users',
            'user_logout' => 'exit',
            'user_registered' => 'admin-users',
            'profile_updated' => 'admin-users',
            'post_published' => 'admin-post',
            'post_updated' => 'edit',
            'post_deleted' => 'trash',
            'comment_posted' => 'admin-comments',
            'comment_approved' => 'yes',
            'comment_deleted' => 'trash',
            'plugin_activated' => 'admin-plugins',
            'plugin_deactivated' => 'admin-plugins',
            'logs_cleaned' => 'database',
            'tables_optimized' => 'performance',

            // PHP Error Icons
            'php_fatal_error' => 'warning',
            'php_warning' => 'warning',
            'php_notice' => 'info',
            'php_deprecated' => 'warning',

            // WordPress Core Error Icons
            'wp_database_error' => 'database-remove',
            'wp_memory_error' => 'warning',
            'wp_permission_error' => 'lock',
            'wp_config_error' => 'admin-tools',
            'wp_error' => 'warning',

            // Security & Authentication Error Icons
            'login_failed' => 'shield-alt',
            'auth_cookie_bad' => 'shield-alt',
            'nonce_verification_failed' => 'shield-alt',
            'permission_denied' => 'lock',

            // HTTP & API Error Icons
            'http_404_error' => 'search',
            'http_500_error' => 'warning',
            'wp_remote_error' => 'cloud',
            'wp_cron_error' => 'clock',

            // Plugin & Theme Error Icons
            'plugin_error' => 'admin-plugins',
            'theme_error' => 'admin-appearance',
            'plugin_activation_error' => 'admin-plugins',

            // File & Upload Error Icons
            'file_upload_error' => 'upload',
            'file_permission_error' => 'lock',

            // Rate Limiting Icons
            'rate_limit_warning' => 'warning',
            'rate_limit_exceeded' => 'dashboard',

            // Privacy & GDPR Icons
            'user_data_anonymized' => 'privacy',

            // Contact Form 7 Icons
            'cf7_form_submitted' => 'email-alt',
            'cf7_form_failed' => 'email-alt2',

            // WPForms Icons
            'wpf_form_submitted' => 'forms',
            'wpf_payment_completed' => 'money-alt',

            // Plugin Management Icons
            'plugin_events_cleaned' => 'admin-plugins',

            // Authentication Icons
            'auth_success' => 'yes-alt',
            'auth_fail' => 'no-alt',

            // Yoast SEO Icons
            'yoast_meta_updated' => 'search',
            'yoast_score_changed' => 'chart-line',
            'yoast_schema_updated' => 'code-standards',
            'yoast_bulk_action' => 'admin-tools',

            // Memory monitor icons
            'memory_early_warning' => 'performance',
            'memory_warning' => 'performance',
            'memory_critical' => 'warning',
            'memory_auto_optimization' => 'performance',
            'memory_leak_warning' => 'warning'
        );

        return isset($icons[$event_key]) ? $icons[$event_key] : 'admin-generic';
    }
}

// Memory monitor functionality is active through hooks and settings page

// Handle manual anonymization action in settings page
add_action('admin_init', function () {
    // Smart retry: Process retry queue on every admin page load
    if (function_exists('sentinel_process_retry_queue')) {
        sentinel_process_retry_queue();
    }

    if (is_admin() && isset($_GET['page']) && $_GET['page'] === 'sentinel-settings' && isset($_GET['action'])) {
        if ($_GET['action'] === 'anonymize_logs') {
            if (class_exists('Sentinel_Cleanup')) {
                Sentinel_Cleanup::anonymize_old_logs(true);
                wp_redirect(admin_url('admin.php?page=sentinel-settings&message=anonymize_success&tab=log-management'));
                exit;
            }
        } elseif ($_GET['action'] === 'retry_failed_logs') {
            if (function_exists('sentinel_process_retry_queue')) {
                $processed = sentinel_process_retry_queue();
                $message = $processed > 0 ? "retry_success&count=$processed" : 'retry_no_entries';
                wp_redirect(admin_url('admin.php?page=sentinel-settings&message=' . $message . '&tab=database'));
                exit;
            }
        }
    }
});

// --- BEGIN: Granular Email Notification Logic ---
if (!function_exists('sentinel_should_send_granular_email_notification')) {
    /**
     * Determine if a granular email notification should be sent for this event
     * @param string $event_key
     * @param array $event_config
     * @param array $data
     * @param int|null $user_id
     * @return bool
     */
    function sentinel_should_send_granular_email_notification($event_key, $event_config, $data, $user_id = null)
    {
        $settings = get_option('sentinel_log_management', array());
        if (empty($settings['sentinel_email_notifications_enabled'])) {
            return false;
        }
        $priority = $event_config['priority'] ?? '';
        $category = $event_config['category'] ?? '';
        // Real-time: critical, security, error, high
        if (!empty($settings['sentinel_email_realtime_critical']) && $priority === 'critical') {
            return true;
        }
        if (!empty($settings['sentinel_email_realtime_security']) && $category === 'security') {
            return true;
        }
        if (!empty($settings['sentinel_email_realtime_error']) && $category === 'errors') {
            return true;
        }
        if (!empty($settings['sentinel_email_realtime_high']) && $priority === 'high') {
            return true;
        }
        // Categories
        if (!empty($settings['sentinel_email_cat_authentication']) && $category === 'authentication') {
            return true;
        }
        if (!empty($settings['sentinel_email_cat_content']) && $category === 'content') {
            return true;
        }
        if (!empty($settings['sentinel_email_cat_system']) && $category === 'system') {
            return true;
        }
        if (!empty($settings['sentinel_email_cat_error']) && $category === 'errors') {
            return true;
        }
        if (!empty($settings['sentinel_email_cat_security']) && $category === 'security') {
            return true;
        }
        if (!empty($settings['sentinel_email_cat_user']) && $category === 'user') {
            return true;
        }
        // Priorities
        if (!empty($settings['sentinel_email_priority_high']) && $priority === 'high') {
            return true;
        }
        if (!empty($settings['sentinel_email_priority_medium']) && $priority === 'medium') {
            return true;
        }
        if (!empty($settings['sentinel_email_priority_low']) && $priority === 'low') {
            return true;
        }
        return false;
    }
}
// --- END: Granular Email Notification Logic ---

// --- BEGIN: Email Rate Limiter (v1.1.2) ---
if (!function_exists('sentinel_email_rate_limiter')) {
    /**
     * Rate-limit notification emails to prevent flooding.
     *
     * Enforces two independent limits:
     *  1. Per-event-key cooldown  -- max 1 email per event type per 30 min
     *  2. Global hourly cap       -- max 5 notification emails per hour
     *
     * Uses lightweight WordPress transients so it works on any hosting
     * environment without extra dependencies.
     *
     * @param string $event_key The event key being notified.
     * @return bool True if the email may be sent, false if throttled.
     */
    function sentinel_email_rate_limiter($event_key)
    {
        $settings = get_option('sentinel_security_settings', array());
        $per_event_cooldown = isset($settings['email_per_event_cooldown']) ? (int) $settings['email_per_event_cooldown'] : 1800;
        $hourly_cap         = isset($settings['email_hourly_cap']) ? (int) $settings['email_hourly_cap'] : 5;

        // --- 1. Per-event-key cooldown ---
        $event_transient = 'sentinel_email_cd_' . md5($event_key);
        if (get_transient($event_transient) !== false) {
            return false;
        }

        // --- 2. Global hourly cap ---
        $global_transient = 'sentinel_email_hourly';
        $sent_count = (int) get_transient($global_transient);
        if ($sent_count >= $hourly_cap) {
            return false;
        }

        // All checks passed -- record the send and allow it.
        set_transient($event_transient, 1, $per_event_cooldown);
        set_transient($global_transient, $sent_count + 1, HOUR_IN_SECONDS);

        return true;
    }
}
// --- END: Email Rate Limiter (v1.1.2) ---
