<?php
/**
 * Name: Activity Log
 * Description: Track and log user activities including logins, logouts, post modifications, plugin activations, theme changes, and settings updates for security auditing and monitoring
 * Category: Security
 * Icon: -
 * UseCases: [
 *   {
 *     "title": "Security Auditing",
 *     "description": "Track who logged in, what posts were changed, and which plugins were activated to investigate suspicious activity after breaches."
 *   },
 *   {
 *     "title": "Compliance Requirements",
 *     "description": "Maintain detailed activity logs for GDPR, SOC2, or industry compliance showing all user actions and data changes."
 *   },
 *   {
 *     "title": "Team Accountability",
 *     "description": "Monitor multi-user sites with 10+ editors to see who published/deleted content or changed critical settings."
 *   }
 * ]
 * WhereToFind: {
 *   "location": "Tools → Activity Log",
 *   "url": "admin.php?page=switchboard-activity-log",
 *   "instructions": "View all tracked user activities and security events in the admin dashboard",
 *   "newTab": false
 * }
 * Added: 2025-08-05
 * Tags: activity, log, audit, security, tracking, monitoring
 */

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

class SwitchboardActivityLog {

    private $table_name;
    private $settings;

    /**
     * Severity mapping for actions
     * Maps action types to severity levels for filtering
     */
    private static $severity_map = [
        'critical' => ['login_failed', 'user_deleted', 'post_deleted'],
        'warning'  => ['post_trash', 'plugin_deactivated'],
        'success'  => ['user_login', 'post_published', 'user_registered'],
        'info'     => ['user_logout', 'post_created', 'post_draft', 'post_pending', 'plugin_activated', 'theme_switched', 'profile_updated', 'option_updated'],
    ];

    /**
     * Get actions for a given severity level
     *
     * @param string $severity The severity level (critical, warning, success, info)
     * @return array List of actions for that severity
     */
    public static function get_actions_by_severity($severity) {
        return isset(self::$severity_map[$severity]) ? self::$severity_map[$severity] : [];
    }

    /**
     * Get severity for a given action
     *
     * @param string $action The action type
     * @return string The severity level
     */
    public static function get_severity_for_action($action) {
        foreach (self::$severity_map as $severity => $actions) {
            if (in_array($action, $actions, true)) {
                return $severity;
            }
        }
        return 'info'; // Default severity
    }

    /**
     * Get all severity levels with their labels
     *
     * @return array Severity levels with labels
     */
    public static function get_severity_levels() {
        return [
            'critical' => __('Critical', 'wpswitchboard'),
            'warning'  => __('Warning', 'wpswitchboard'),
            'success'  => __('Success', 'wpswitchboard'),
            'info'     => __('Info', 'wpswitchboard'),
        ];
    }

    public function __construct() {
        global $wpdb;
        $this->table_name = $wpdb->prefix . 'switchboard_activity_log';
        $this->settings = get_option('switchboard_activity-log_settings', []);

        // Create table if it doesn't exist (checked on admin_init)
        add_action('admin_init', [$this, 'maybe_create_table']);

        // Register admin page
        add_action('admin_menu', [$this, 'register_admin_page']);

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

        // AJAX handlers
        add_action('wp_ajax_activity_log_get_logs', [$this, 'ajax_get_logs']);
        add_action('wp_ajax_activity_log_get_stats', [$this, 'ajax_get_stats']);
        add_action('wp_ajax_activity_log_delete', [$this, 'ajax_delete']);
        add_action('wp_ajax_activity_log_delete_bulk', [$this, 'ajax_delete_bulk']);
        add_action('wp_ajax_activity_log_delete_all', [$this, 'ajax_delete_all']);
        add_action('wp_ajax_activity_log_export', [$this, 'ajax_export']);

        // Track activities
        $this->setup_tracking();

        // Setup cron for cleanup
        add_action('switchboard_cleanup_activity_log', [$this, 'cleanup_old_logs']);
    }

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

        $from = $date_range['from'];
        $to = $date_range['to'];

        // Total events
        $total_events = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$this->table_name} WHERE DATE(created_at) BETWEEN %s AND %s",
            $from,
            $to
        ));

        // User logins
        $user_logins = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$this->table_name} WHERE action = 'user_login' AND DATE(created_at) BETWEEN %s AND %s",
            $from,
            $to
        ));

        // Failed logins
        $failed_logins = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$this->table_name} WHERE action = 'login_failed' AND DATE(created_at) BETWEEN %s AND %s",
            $from,
            $to
        ));

        // Content changes (posts published, updated, deleted)
        $content_changes = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$this->table_name} WHERE action IN ('post_published', 'post_updated', 'post_deleted', 'post_trashed') AND DATE(created_at) BETWEEN %s AND %s",
            $from,
            $to
        ));

        // Critical events
        $critical_events = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM {$this->table_name}
             WHERE action IN ('login_failed', 'user_deleted', 'plugin_deactivated', 'plugin_deleted', 'theme_switched')
             AND DATE(created_at) BETWEEN %s AND %s
             ORDER BY created_at DESC LIMIT 5",
            $from,
            $to
        ), ARRAY_A);

        // Recent events
        $recent_events = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM {$this->table_name}
             WHERE DATE(created_at) BETWEEN %s AND %s
             ORDER BY created_at DESC LIMIT 10",
            $from,
            $to
        ), ARRAY_A);

        return [
            'total_events'    => intval($total_events),
            'user_logins'     => intval($user_logins),
            'failed_logins'   => intval($failed_logins),
            'content_changes' => intval($content_changes),
            'critical_events' => $critical_events,
            'recent_events'   => $recent_events,
        ];
    }

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

    /**
     * Called when module is deactivated
     * Clears all scheduled cron events
     */
    public function deactivate() {
        wp_clear_scheduled_hook('switchboard_cleanup_activity_log');
    }

    public function maybe_create_table() {
        global $wpdb;
        if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $this->table_name)) != $this->table_name) {
            $this->create_table();
        }
    }

    public function create_table() {
        global $wpdb;

        $charset_collate = $wpdb->get_charset_collate();

        $sql = "CREATE TABLE IF NOT EXISTS {$this->table_name} (
            id bigint(20) NOT NULL AUTO_INCREMENT,
            user_id bigint(20) NOT NULL,
            user_name varchar(255) NOT NULL,
            user_role varchar(100) NOT NULL,
            action varchar(100) NOT NULL,
            object_type varchar(100) DEFAULT NULL,
            object_id bigint(20) DEFAULT NULL,
            object_name varchar(255) DEFAULT NULL,
            ip_address varchar(45) NOT NULL,
            user_agent text DEFAULT NULL,
            created_at datetime NOT NULL,
            PRIMARY KEY  (id),
            KEY user_id (user_id),
            KEY action (action),
            KEY created_at (created_at),
            KEY user_role (user_role),
            KEY ip_address (ip_address)
        ) $charset_collate;";

        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        dbDelta($sql);
    }

    /**
     * Register admin page under Tools menu
     */
    public function register_admin_page() {
        add_submenu_page(
            'switchboard',
            __('Activity Log', 'wpswitchboard'),
            __('Activity Log', 'wpswitchboard'),
            'manage_options',
            'switchboard-activity-log',
            [$this, 'render_admin_page']
        );
    }

    /**
     * Render the admin page
     */
    public function render_admin_page() {
        $admin_template = plugin_dir_path(__FILE__) . '../../assets/activity-log/admin.php';
        if (file_exists($admin_template)) {
            include $admin_template;
        }
    }

    /**
     * Enqueue admin assets
     */
    public function enqueue_admin_assets($hook) {
        // Check if we're on the activity log page
        $is_activity_log_page = (
            (isset($_GET['page']) && $_GET['page'] === 'switchboard-activity-log') ||
            strpos($hook, 'switchboard-activity-log') !== false
        );

        if (!$is_activity_log_page) {
            return;
        }

        // Enqueue the main Switchboard admin CSS
        if (defined('SWITCHBOARD_URL') && defined('SWITCHBOARD_VERSION')) {
            wp_enqueue_style(
                'switchboard-admin',
                SWITCHBOARD_URL . 'switchboard-admin.css',
                [],
                SWITCHBOARD_VERSION
            );
        }

        // Enqueue activity log specific CSS
        wp_enqueue_style(
            'activity-log-admin',
            plugins_url('../../assets/activity-log/admin.css', __FILE__),
            [],
            '1.0.0'
        );

        // Enqueue dashicons
        wp_enqueue_style('dashicons');

        // Ensure jQuery is loaded
        wp_enqueue_script('jquery');

        // Enqueue activity log JS
        wp_enqueue_script(
            'activity-log-admin',
            plugins_url('../../assets/activity-log/admin.js', __FILE__),
            ['jquery'],
            '1.0.0',
            true
        );

        wp_localize_script('activity-log-admin', 'activityLogAdmin', [
            'ajaxUrl' => admin_url('admin-ajax.php'),
            'nonce' => wp_create_nonce('activity_log_admin'),
            'siteUrl' => home_url()
        ]);
    }

    /**
     * AJAX: Get filtered logs
     */
    public function ajax_get_logs() {
        check_ajax_referer('activity_log_admin', 'nonce');

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

        global $wpdb;

        // Get and sanitize parameters
        $orderby = isset($_POST['orderby']) ? sanitize_text_field($_POST['orderby']) : 'created_at';
        $order = isset($_POST['order']) ? sanitize_text_field($_POST['order']) : 'DESC';
        $offset = isset($_POST['offset']) ? intval($_POST['offset']) : 0;
        $limit = isset($_POST['limit']) ? intval($_POST['limit']) : 50;

        // Validate orderby
        $allowed_orderby = ['created_at', 'user_name', 'action', 'user_role', 'ip_address'];
        if (!in_array($orderby, $allowed_orderby, true)) {
            $orderby = 'created_at';
        }

        // Validate order
        $order = strtoupper($order) === 'ASC' ? 'ASC' : 'DESC';

        // Validate limits
        $limit = max(1, min(100, $limit));
        $offset = max(0, $offset);

        // Build WHERE clause
        $where_clauses = [];
        $where_values = [];

        // Date filters
        $date_from = isset($_POST['date_from']) ? sanitize_text_field($_POST['date_from']) : '';
        $date_to = isset($_POST['date_to']) ? sanitize_text_field($_POST['date_to']) : '';

        if (!empty($date_from)) {
            $where_clauses[] = "DATE(created_at) >= %s";
            $where_values[] = $date_from;
        }

        if (!empty($date_to)) {
            $where_clauses[] = "DATE(created_at) <= %s";
            $where_values[] = $date_to;
        }

        // User filter
        $user_id = isset($_POST['user_id']) ? $_POST['user_id'] : '';
        if ($user_id !== '') {
            $where_clauses[] = "user_id = %d";
            $where_values[] = intval($user_id);
        }

        // Action filter (using action_type to avoid conflict with WordPress AJAX action)
        $action_type = isset($_POST['action_type']) ? sanitize_text_field($_POST['action_type']) : '';
        if (!empty($action_type)) {
            $where_clauses[] = "action = %s";
            $where_values[] = $action_type;
        }

        // Severity filter - maps to multiple actions (only applies if action_type not set)
        $severity = isset($_POST['severity']) ? sanitize_text_field($_POST['severity']) : '';
        if (!empty($severity) && empty($action_type)) {
            $severity_actions = self::get_actions_by_severity($severity);
            if (!empty($severity_actions)) {
                $action_placeholders = implode(',', array_fill(0, count($severity_actions), '%s'));
                $where_clauses[] = "action IN ($action_placeholders)";
                $where_values = array_merge($where_values, $severity_actions);
            }
        }

        // Role filter
        $role = isset($_POST['role']) ? sanitize_text_field($_POST['role']) : '';
        if (!empty($role)) {
            $where_clauses[] = "user_role = %s";
            $where_values[] = $role;
        }

        // IP address filter
        $ip_address = isset($_POST['ip_address']) ? sanitize_text_field($_POST['ip_address']) : '';
        if (!empty($ip_address)) {
            $where_clauses[] = "ip_address LIKE %s";
            $where_values[] = '%' . $wpdb->esc_like($ip_address) . '%';
        }

        // Search filter
        $search = isset($_POST['search']) ? sanitize_text_field($_POST['search']) : '';
        if (!empty($search)) {
            $search_like = '%' . $wpdb->esc_like($search) . '%';
            $where_clauses[] = "(user_name LIKE %s OR object_name LIKE %s OR ip_address LIKE %s)";
            $where_values[] = $search_like;
            $where_values[] = $search_like;
            $where_values[] = $search_like;
        }

        // Exclude internal WordPress post types from results
        $ignored_types = [
            'wp_font_face', 'wp_font_family', 'wp_global_styles', 'wp_navigation',
            'wp_template', 'wp_template_part', 'wp_block', 'oembed_cache',
            'user_request', 'customize_changeset', 'custom_css', 'revision', 'nav_menu_item'
        ];
        $type_placeholders = implode(',', array_fill(0, count($ignored_types), '%s'));
        $where_clauses[] = "(object_type IS NULL OR object_type NOT IN ($type_placeholders))";
        $where_values = array_merge($where_values, $ignored_types);

        // Build WHERE string
        $where = '';
        if (!empty($where_clauses)) {
            $where = 'WHERE ' . implode(' AND ', $where_clauses);
        }

        // Get total count
        $count_query = "SELECT COUNT(*) FROM {$this->table_name} {$where}";
        if (!empty($where_values)) {
            $count_query = $wpdb->prepare($count_query, $where_values);
        }
        $total = $wpdb->get_var($count_query);

        // Get logs
        $query = "SELECT * FROM {$this->table_name} {$where} ORDER BY {$orderby} {$order} LIMIT %d OFFSET %d";
        $query_values = array_merge($where_values, [$limit, $offset]);
        $logs = $wpdb->get_results($wpdb->prepare($query, $query_values), ARRAY_A);

        wp_send_json_success([
            'logs' => $logs,
            'total' => intval($total),
            'offset' => $offset,
            'limit' => $limit
        ]);
    }

    /**
     * AJAX: Get stats
     */
    public function ajax_get_stats() {
        check_ajax_referer('activity_log_admin', 'nonce');

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

        global $wpdb;

        // Ignored types filter for consistent stats
        $ignored_types = [
            'wp_font_face', 'wp_font_family', 'wp_global_styles', 'wp_navigation',
            'wp_template', 'wp_template_part', 'wp_block', 'oembed_cache',
            'user_request', 'customize_changeset', 'custom_css', 'revision', 'nav_menu_item'
        ];
        $type_placeholders = implode(',', array_fill(0, count($ignored_types), '%s'));
        $ignore_clause = "(object_type IS NULL OR object_type NOT IN ($type_placeholders))";

        // Total events (excluding ignored types)
        $total_query = "SELECT COUNT(*) FROM {$this->table_name} WHERE $ignore_clause";
        $total_events = $wpdb->get_var($wpdb->prepare($total_query, $ignored_types));

        // Events today (excluding ignored types)
        $today_query = "SELECT COUNT(*) FROM {$this->table_name} WHERE DATE(created_at) = CURDATE() AND $ignore_clause";
        $today_events = $wpdb->get_var($wpdb->prepare($today_query, $ignored_types));

        // Failed logins (last 30 days) - these don't have object_type so no filter needed
        $failed_logins = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$this->table_name} WHERE action = %s AND created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)",
            'login_failed'
        ));

        // Unique users (excluding ignored types)
        $users_query = "SELECT COUNT(DISTINCT user_id) FROM {$this->table_name} WHERE user_id > 0 AND $ignore_clause";
        $unique_users = $wpdb->get_var($wpdb->prepare($users_query, $ignored_types));

        wp_send_json_success([
            'total_events' => intval($total_events),
            'today_events' => intval($today_events),
            'failed_logins' => intval($failed_logins),
            'unique_users' => intval($unique_users)
        ]);
    }

    /**
     * AJAX: Delete single entry
     */
    public function ajax_delete() {
        check_ajax_referer('activity_log_admin', 'nonce');

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

        $id = isset($_POST['id']) ? intval($_POST['id']) : 0;

        if (!$id) {
            wp_send_json_error(esc_html__('Invalid ID', 'wpswitchboard'));
        }

        global $wpdb;
        $deleted = $wpdb->delete($this->table_name, ['id' => $id], ['%d']);

        if ($deleted) {
            wp_send_json_success();
        } else {
            wp_send_json_error(esc_html__('Failed to delete', 'wpswitchboard'));
        }
    }

    /**
     * AJAX: Bulk delete entries
     */
    public function ajax_delete_bulk() {
        check_ajax_referer('activity_log_admin', 'nonce');

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

        $ids = isset($_POST['ids']) ? array_map('intval', (array) $_POST['ids']) : [];

        if (empty($ids)) {
            wp_send_json_error(esc_html__('No entries selected', 'wpswitchboard'));
        }

        global $wpdb;

        // Use placeholders for the IN clause
        $placeholders = implode(',', array_fill(0, count($ids), '%d'));
        $deleted = $wpdb->query($wpdb->prepare(
            "DELETE FROM {$this->table_name} WHERE id IN ($placeholders)",
            $ids
        ));

        if ($deleted !== false) {
            wp_send_json_success(['deleted' => $deleted]);
        } else {
            wp_send_json_error(esc_html__('Failed to delete', 'wpswitchboard'));
        }
    }

    /**
     * AJAX: Delete all entries
     */
    public function ajax_delete_all() {
        check_ajax_referer('activity_log_admin', 'nonce');

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

        global $wpdb;
        $deleted = $wpdb->query("TRUNCATE TABLE {$this->table_name}");

        if ($deleted !== false) {
            wp_send_json_success();
        } else {
            wp_send_json_error(esc_html__('Failed to delete all logs', 'wpswitchboard'));
        }
    }

    /**
     * AJAX: Export to CSV
     */
    public function ajax_export() {
        // Verify nonce from GET parameter
        if (!isset($_GET['nonce']) || !wp_verify_nonce($_GET['nonce'], 'activity_log_admin')) {
            wp_die(esc_html__('Security check failed', 'wpswitchboard'));
        }

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

        global $wpdb;

        // Build WHERE clause from GET parameters
        $where_clauses = [];
        $where_values = [];

        // Date filters
        $date_from = isset($_GET['date_from']) ? sanitize_text_field($_GET['date_from']) : '';
        $date_to = isset($_GET['date_to']) ? sanitize_text_field($_GET['date_to']) : '';

        if (!empty($date_from)) {
            $where_clauses[] = "DATE(created_at) >= %s";
            $where_values[] = $date_from;
        }

        if (!empty($date_to)) {
            $where_clauses[] = "DATE(created_at) <= %s";
            $where_values[] = $date_to;
        }

        // User filter
        $user_id = isset($_GET['user_id']) ? $_GET['user_id'] : '';
        if ($user_id !== '') {
            $where_clauses[] = "user_id = %d";
            $where_values[] = intval($user_id);
        }

        // Action filter
        $action = isset($_GET['action_type']) ? sanitize_text_field($_GET['action_type']) : '';
        if (!empty($action)) {
            $where_clauses[] = "action = %s";
            $where_values[] = $action;
        }

        // Severity filter - maps to multiple actions (only applies if action_type not set)
        $severity = isset($_GET['severity']) ? sanitize_text_field($_GET['severity']) : '';
        if (!empty($severity) && empty($action)) {
            $severity_actions = self::get_actions_by_severity($severity);
            if (!empty($severity_actions)) {
                $action_placeholders = implode(',', array_fill(0, count($severity_actions), '%s'));
                $where_clauses[] = "action IN ($action_placeholders)";
                $where_values = array_merge($where_values, $severity_actions);
            }
        }

        // Role filter
        $role = isset($_GET['role']) ? sanitize_text_field($_GET['role']) : '';
        if (!empty($role)) {
            $where_clauses[] = "user_role = %s";
            $where_values[] = $role;
        }

        // IP address filter
        $ip_address = isset($_GET['ip_address']) ? sanitize_text_field($_GET['ip_address']) : '';
        if (!empty($ip_address)) {
            $where_clauses[] = "ip_address LIKE %s";
            $where_values[] = '%' . $wpdb->esc_like($ip_address) . '%';
        }

        // Search filter
        $search = isset($_GET['search']) ? sanitize_text_field($_GET['search']) : '';
        if (!empty($search)) {
            $search_like = '%' . $wpdb->esc_like($search) . '%';
            $where_clauses[] = "(user_name LIKE %s OR object_name LIKE %s OR ip_address LIKE %s)";
            $where_values[] = $search_like;
            $where_values[] = $search_like;
            $where_values[] = $search_like;
        }

        // Exclude internal WordPress post types from export
        $ignored_types = [
            'wp_font_face', 'wp_font_family', 'wp_global_styles', 'wp_navigation',
            'wp_template', 'wp_template_part', 'wp_block', 'oembed_cache',
            'user_request', 'customize_changeset', 'custom_css', 'revision', 'nav_menu_item'
        ];
        $type_placeholders = implode(',', array_fill(0, count($ignored_types), '%s'));
        $where_clauses[] = "(object_type IS NULL OR object_type NOT IN ($type_placeholders))";
        $where_values = array_merge($where_values, $ignored_types);

        // Build WHERE string
        $where = '';
        if (!empty($where_clauses)) {
            $where = 'WHERE ' . implode(' AND ', $where_clauses);
        }

        // Get all matching logs
        $query = "SELECT created_at, user_name, user_role, action, object_type, object_name, ip_address, user_agent FROM {$this->table_name} {$where} ORDER BY created_at DESC";
        if (!empty($where_values)) {
            $query = $wpdb->prepare($query, $where_values);
        }
        $logs = $wpdb->get_results($query, ARRAY_A);

        // Output CSV
        $filename = 'activity-log-export-' . gmdate('Y-m-d') . '.csv';

        header('Content-Type: text/csv; charset=utf-8');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('Pragma: no-cache');
        header('Expires: 0');

        $output = fopen('php://output', 'w');

        // UTF-8 BOM for Excel compatibility
        fprintf($output, chr(0xEF).chr(0xBB).chr(0xBF));

        // Header row
        fputcsv($output, [
            'Date',
            'User',
            'Role',
            'Action',
            'Object Type',
            'Object Name',
            'IP Address',
            'User Agent'
        ]);

        // Data rows
        foreach ($logs as $log) {
            // Prevent CSV injection
            $safe_log = array_map(function($value) {
                if ($value === null) {
                    return '';
                }
                $value = (string) $value;
                // Prefix dangerous characters with single quote
                if (preg_match('/^[=+\-@\t\r]/', $value)) {
                    return "'" . $value;
                }
                return $value;
            }, $log);

            fputcsv($output, $safe_log);
        }

        fclose($output);
        exit;
    }


    private function setup_tracking() {
        // User login/logout
        add_action('wp_login', [$this, 'log_user_login'], 10, 2);
        add_action('wp_logout', [$this, 'log_user_logout']);
        add_action('wp_login_failed', [$this, 'log_failed_login']);

        // Post/Page actions
        add_action('transition_post_status', [$this, 'log_post_status_change'], 10, 3);
        add_action('delete_post', [$this, 'log_post_delete'], 10, 2);

        // Plugin actions
        add_action('activated_plugin', [$this, 'log_plugin_activation'], 10, 2);
        add_action('deactivated_plugin', [$this, 'log_plugin_deactivation'], 10, 2);

        // Theme actions
        add_action('switch_theme', [$this, 'log_theme_switch'], 10, 3);

        // User actions
        add_action('user_register', [$this, 'log_user_register']);
        add_action('delete_user', [$this, 'log_user_delete']);
        add_action('profile_update', [$this, 'log_profile_update'], 10, 2);

        // Settings changes
        add_action('update_option', [$this, 'log_option_update'], 10, 3);
    }

    private function log_activity($action, $object_type = null, $object_id = null, $object_name = null, $user_id = null) {
        global $wpdb;

        if ($user_id === null) {
            $user_id = get_current_user_id();
        }

        if (!$user_id) {
            $user_id = 0;
            $user_name = 'Guest';
            $user_role = 'none';
        } else {
            $user = get_userdata($user_id);
            if (!$user || !is_object($user)) {
                $user_name = 'Unknown';
                $user_role = 'none';
            } else {
                $user_name = $user->user_login;
                $user_role = !empty($user->roles) && is_array($user->roles) ? $user->roles[0] : 'none';
            }
        }

        $ip_address = $this->get_ip_address();
        $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field(substr($_SERVER['HTTP_USER_AGENT'], 0, 255)) : '';

        $wpdb->insert(
            $this->table_name,
            [
                'user_id' => $user_id,
                'user_name' => $user_name,
                'user_role' => $user_role,
                'action' => $action,
                'object_type' => $object_type,
                'object_id' => $object_id,
                'object_name' => $object_name,
                'ip_address' => $ip_address,
                'user_agent' => $user_agent,
                'created_at' => current_time('mysql'),
            ],
            ['%d', '%s', '%s', '%s', '%s', '%d', '%s', '%s', '%s', '%s']
        );
    }

    public function log_user_login($user_login, $user) {
        $this->log_activity('user_login', 'user', $user->ID, $user_login, $user->ID);
    }

    public function log_user_logout() {
        $user_id = get_current_user_id();
        $user = get_userdata($user_id);
        $this->log_activity('user_logout', 'user', $user_id, $user && is_object($user) ? $user->user_login : 'Unknown');
    }

    public function log_failed_login($username) {
        $this->log_activity('login_failed', 'user', 0, $username, 0);
    }

    /**
     * Check if post type should be ignored for logging
     */
    private function should_ignore_post_type($post_type) {
        // Internal/system post types that shouldn't be logged
        $ignored_post_types = [
            'wp_font_face',
            'wp_font_family',
            'wp_global_styles',
            'wp_navigation',
            'wp_template',
            'wp_template_part',
            'wp_block',
            'oembed_cache',
            'user_request',
            'customize_changeset',
            'custom_css',
            'revision',
            'nav_menu_item',
        ];

        return in_array($post_type, $ignored_post_types, true);
    }

    public function log_post_status_change($new_status, $old_status, $post) {
        if ($new_status === $old_status) return;
        if (wp_is_post_revision($post->ID)) return;
        if ($this->should_ignore_post_type($post->post_type)) return;

        $action = "post_{$new_status}";
        if ($old_status === 'auto-draft' && $new_status === 'draft') {
            $action = 'post_created';
        }

        $this->log_activity($action, $post->post_type, $post->ID, $post->post_title);
    }

    public function log_post_delete($post_id, $post) {
        if (wp_is_post_revision($post_id)) return;
        if ($this->should_ignore_post_type($post->post_type)) return;
        $this->log_activity('post_deleted', $post->post_type, $post_id, $post->post_title);
    }

    public function log_plugin_activation($plugin, $network_wide) {
        $plugin_file = WP_PLUGIN_DIR . '/' . $plugin;
        if (!file_exists($plugin_file)) {
            $this->log_activity('plugin_activated', 'plugin', 0, $plugin);
            return;
        }
        $plugin_data = get_plugin_data($plugin_file);
        $this->log_activity('plugin_activated', 'plugin', 0, isset($plugin_data['Name']) ? $plugin_data['Name'] : $plugin);
    }

    public function log_plugin_deactivation($plugin, $network_wide) {
        $plugin_file = WP_PLUGIN_DIR . '/' . $plugin;
        if (!file_exists($plugin_file)) {
            $this->log_activity('plugin_deactivated', 'plugin', 0, $plugin);
            return;
        }
        $plugin_data = get_plugin_data($plugin_file);
        $this->log_activity('plugin_deactivated', 'plugin', 0, isset($plugin_data['Name']) ? $plugin_data['Name'] : $plugin);
    }

    public function log_theme_switch($new_name, $new_theme, $old_theme) {
        $this->log_activity('theme_switched', 'theme', 0, $new_name);
    }

    public function log_user_register($user_id) {
        $user = get_userdata($user_id);
        $this->log_activity('user_registered', 'user', $user_id, $user && is_object($user) ? $user->user_login : 'Unknown');
    }

    public function log_user_delete($user_id) {
        $user = get_userdata($user_id);
        $this->log_activity('user_deleted', 'user', $user_id, $user && is_object($user) ? $user->user_login : 'Unknown');
    }

    public function log_profile_update($user_id, $old_user_data) {
        $this->log_activity('profile_updated', 'user', $user_id, $old_user_data->user_login);
    }

    public function log_option_update($option, $old_value, $value) {
        // Only log important options
        $tracked_options = ['blogname', 'blogdescription', 'siteurl', 'home', 'admin_email'];
        if (in_array($option, $tracked_options)) {
            $this->log_activity('option_updated', 'option', 0, $option);
        }
    }

    private function get_ip_address() {
        // Only trust REMOTE_ADDR for security logging - other headers can be spoofed
        $ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0';

        // Validate IP address
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {
            // If it's a private/reserved IP, still validate it's a proper IP format
            if (filter_var($ip, FILTER_VALIDATE_IP) === false) {
                return '0.0.0.0';
            }
        }

        return $ip;
    }

    public function cleanup_old_logs() {
        global $wpdb;

        $days = isset($this->settings['retention_days']) ? intval($this->settings['retention_days']) : 30;

        $wpdb->query($wpdb->prepare(
            "DELETE FROM {$this->table_name} WHERE created_at < DATE_SUB(NOW(), INTERVAL %d DAY)",
            $days
        ));
    }

    /**
     * Note: Settings panel removed - this module uses a dedicated admin page at Switchboard → Activity Log
     * The render_settings() method was removed to redirect clicks on the settings icon to the dedicated page.
     * See render_admin_page() method and /assets/activity-log/admin.php for the full interface.
     */

    /**
     * Register data locations for cleanup system
     *
     * @return array Data storage locations
     */
    public function register_data_locations() {
        return [
            'options' => [
                'switchboard_activity-log_settings',
            ],
            'option_patterns' => [],
            'tables' => [
                'switchboard_activity_log',
            ],
            'post_meta' => [],
            'user_meta' => [],
        ];
    }
}

$switchboard_instance = new SwitchboardActivityLog();
Switchboard_Data_Registry::get_instance()->auto_register($switchboard_instance, __FILE__);
