<?php
/**
 * Guard Dog Activity Log List Table class.
 *
 * @package GuardDog
 * @since 1.6.0
 */

if (!defined('ABSPATH')) exit; // Exit if accessed directly

// Ensure WP_List_Table class is loaded
if (!class_exists('WP_List_Table')) {
    require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
}

class Guard_Dog_Activity_Log_List_Table extends WP_List_Table {

    /**
     * Constructor.
     *
     * @since 1.6.0
     */
    public function __construct() {
        parent::__construct([
            'singular' => __('Log Entry', 'guard-dog'),   // Singular name of the listed records
            'plural'   => 'guard_dog_log_entries_plural', // Plural name of the listed records - using a simple string
            'ajax'     => false                         // Does this table support ajax?
        ]);
    }

    /**
     * Get a list of columns. The format is:
     * 'internal-name' => 'Title'
     *
     * @since 1.6.0
     * @return array
     */
    public function get_columns() {
        $columns = [
            'cb'       => '<input type="checkbox" />', // Checkbox for bulk actions
            'id'         => __('ID', 'guard-dog'),
            'timestamp'  => __('Timestamp', 'guard-dog'),
            'event_type' => __('Event Type', 'guard-dog'),
            'username'   => __('Username', 'guard-dog'),
            'user_id'    => __('User ID', 'guard-dog'),
            'ip_address' => __('IP Address', 'guard-dog'),
            'details'    => __('Details', 'guard-dog'),
        ];
        return $columns;
    }

    /**
     * Default column rendering.
     *
     * @since 1.6.0
     * @param array  $item        A singular item (one log row).
     * @param string $column_name The name/slug of the column to be displayed.
     * @return string Text or HTML to be displayed.
     */
    protected function column_default($item, $column_name) {
        switch ($column_name) {
            case 'id':
            case 'username':
            case 'user_id':
            case 'ip_address':
                return esc_html($item[$column_name]);
            case 'event_type':
                return esc_html( Guard_Dog_Activity_Log::get_event_label( $item['event_type'] ?? '' ) );
            default:
                // Return serialized data for debugging purposes instead of print_r
                return '<pre>' . esc_html(wp_json_encode($item, JSON_PRETTY_PRINT)) . '</pre>';
        }
    }

    /**
     * Render the checkbox column for bulk actions.
     *
     * @since 1.6.0
     * @param array $item A singular item (one log row).
     * @return string The checkbox HTML.
     */
    protected function column_cb($item) {
        return sprintf('<input type="checkbox" name="log_ids[]" value="%s" />', (int) $item['id']);
    }

    /**
     * Render the timestamp column.
     *
     * @since 1.6.0
     * @param array $item A singular item (one log row).
     * @return string Formatted timestamp.
     */
    protected function column_timestamp($item) {
        // Format timestamp from MySQL DATETIME to a more readable format
        // Adjust format as desired, e.g., using WordPress date/time settings
        $timestamp = strtotime($item['timestamp']);
        if ($timestamp) {
            return date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $timestamp);
        }
        return esc_html($item['timestamp']); // Fallback
    }

    /**
     * Render the details column.
     *
     * @since 1.6.0
     * @param array $item A singular item (one log row).
     * @return string Formatted details.
     */
    protected function column_details($item) {
        $details_data = json_decode($item['details'], true);
        if (json_last_error() === JSON_ERROR_NONE && is_array($details_data)) {
            // Use wp_json_encode for consistent formatting instead of print_r
            return '<pre>' . esc_html(wp_json_encode($details_data, JSON_PRETTY_PRINT)) . '</pre>';
        }
        return esc_html($item['details']); // Fallback if not valid JSON or not an array
    }

    /**
     * Displays the table navigation above and below the table.
     *
     * @since 1.6.0
     * @param string $which The location of the bulk actions: 'top' or 'bottom'.
     */
    protected function extra_tablenav($which) {
        if ($which == "top") {
            // Get current filter values with fallbacks
            $current_filter_category = $this->get_filter_value('filter_category', 'all');
            $current_date_start = $this->get_filter_value('filter_date_start', '');
            $current_date_end = $this->get_filter_value('filter_date_end', '');
            ?>
            <div class="alignleft actions bulkactions">
                <label for="filter-by-category" class="screen-reader-text"><?php esc_html_e('Filter by category', 'guard-dog'); ?></label>
                <select name="filter_category" id="filter-by-category">
                    <option value="all" <?php selected($current_filter_category, 'all'); ?>><?php esc_html_e('All Events', 'guard-dog'); ?></option>
                    <option value="security" <?php selected($current_filter_category, 'security'); ?>><?php esc_html_e('Security Events', 'guard-dog'); ?></option>
                    <option value="users" <?php selected($current_filter_category, 'users'); ?>><?php esc_html_e('User Events', 'guard-dog'); ?></option>
                    <option value="content" <?php selected($current_filter_category, 'content'); ?>><?php esc_html_e('Content Events', 'guard-dog'); ?></option>
                    <option value="system" <?php selected($current_filter_category, 'system'); ?>><?php esc_html_e('System Events', 'guard-dog'); ?></option>
                </select>

                <label for="filter-by-date-start" class="screen-reader-text"><?php esc_html_e('Start date', 'guard-dog'); ?></label>
                <input type="date" name="filter_date_start" id="filter-by-date-start" value="<?php echo esc_attr($current_date_start); ?>" placeholder="<?php esc_attr_e('Start Date', 'guard-dog'); ?>" />

                <label for="filter-by-date-end" class="screen-reader-text"><?php esc_html_e('End date', 'guard-dog'); ?></label>
                <input type="date" name="filter_date_end" id="filter-by-date-end" value="<?php echo esc_attr($current_date_end); ?>" placeholder="<?php esc_attr_e('End Date', 'guard-dog'); ?>"/>

                <?php submit_button(__('Filter', 'guard-dog'), 'secondary', 'do_filter_logs', false, ['id' => 'post-query-submit']); ?>
            </div>
            <div class="alignleft actions">
                 <?php $this->search_box(esc_html__('Search Logs', 'guard-dog'), 'guard-dog-log-search'); ?>
            </div>
            <?php
        }
    }

    /**
     * Safely get filter value from request with proper validation.
     *
     * @since 1.6.0
     * @param string $key The filter key to retrieve.
     * @param string $default Default value if not found or invalid.
     * @return string Sanitized filter value.
     */
    private function get_filter_value($key, $default = '') {
        // Only process GET data in admin context for list table operations
        if (!is_admin() || !current_user_can('manage_options')) {
            return $default;
        }

        // phpcs:disable WordPress.Security.NonceVerification.Recommended
        // Nonce verification is not required in this context:
        // 1. This is a WordPress admin list table that reads GET parameters for filtering/sorting
        // 2. WordPress list tables use GET parameters for filtering without nonces (standard WordPress pattern)
        // 3. These are read-only operations that only affect display, not data processing
        // 4. All input is properly sanitized and validated against whitelists before use
        // 5. Access is restricted to admin users with manage_options capability
        // 6. No privileged actions or data modifications occur based on these GET parameters
        if (!isset($_GET[$key])) {
            return $default;
        }
        
        $value = sanitize_text_field(wp_unslash($_GET[$key]));

        switch ($key) {
            case 'filter_category':
                $valid_categories = ['all', 'security', 'users', 'content', 'system'];
                return in_array($value, $valid_categories, true) ? $value : $default;
            
            case 'filter_date_start':
            case 'filter_date_end':
                return preg_match('/^\d{4}-\d{2}-\d{2}$/', $value) ? $value : $default;
            
            default:
                return $value;
        }
        // phpcs:enable WordPress.Security.NonceVerification.Recommended
    }

    /**
     * Safely get sorting parameters with proper validation.
     *
     * @since 1.6.0
     * @return array Array with 'orderby' and 'order' keys.
     */
    private function get_sorting_params() {
        // Only process GET data in admin context for list table operations
        if (!is_admin() || !current_user_can('manage_options')) {
            return ['orderby' => 'timestamp', 'order' => 'DESC'];
        }

        // phpcs:disable WordPress.Security.NonceVerification.Recommended
        // Nonce verification is not required in this context:
        // 1. This is a WordPress admin list table that reads GET parameters for sorting
        // 2. WordPress list tables use GET parameters for sorting without nonces (standard WordPress pattern)
        // 3. These are read-only operations that only affect display order, not data processing
        // 4. All input is properly sanitized and validated against whitelists before use
        // 5. Access is restricted to admin users with manage_options capability
        // 6. No privileged actions or data modifications occur based on these GET parameters
        $orderby_input = isset($_GET['orderby']) ? sanitize_text_field(wp_unslash($_GET['orderby'])) : '';
        $orderby = (!empty($orderby_input) && array_key_exists($orderby_input, $this->get_sortable_columns())) ? sanitize_sql_orderby($orderby_input) : 'timestamp';
        
        $order_input = isset($_GET['order']) ? sanitize_text_field(wp_unslash($_GET['order'])) : '';
        $order = (!empty($order_input) && in_array(strtoupper($order_input), ['ASC', 'DESC'], true)) ? strtoupper($order_input) : 'DESC';
        // phpcs:enable WordPress.Security.NonceVerification.Recommended
        
        return ['orderby' => $orderby, 'order' => $order];
    }

    /**
     * Prepare the items for the table to process.
     *
     * @since 1.6.0
     */
    public function prepare_items() {
        global $wpdb;

        $this->_column_headers = [$this->get_columns(), [], $this->get_sortable_columns()];

        // Get filtered data with proper pagination
        $filter_data = $this->build_query_data();
        
        // Set pagination
        $this->set_pagination_args([
            'total_items' => $filter_data['total_items'],
            'per_page'    => $filter_data['per_page'],
            'total_pages' => ceil($filter_data['total_items'] / $filter_data['per_page'])
        ]);

        // Set items
        $this->items = $filter_data['items'];
    }

    /**
     * Build query data with proper WordPress compliance.
     *
     * @since 1.6.0
     * @return array Query results and pagination data.
     */
    private function build_query_data() {
        global $wpdb;
        
        // Get filter parameters
        $filter_category = $this->get_filter_value('filter_category', 'all');
        $filter_date_start = $this->get_filter_value('filter_date_start', '');
        $filter_date_end = $this->get_filter_value('filter_date_end', '');
        $search_term = $this->get_filter_value('s', '');
        
        // Get sorting parameters
        $sorting_params = $this->get_sorting_params();
        $orderby = $sorting_params['orderby'];
        $order = $sorting_params['order'];
        
        // Pagination
        $per_page = $this->get_items_per_page('guard_dog_logs_per_page', 20);
        $current_page = $this->get_pagenum();
        $offset = ($current_page - 1) * $per_page;

        // Get total count for pagination - activity logs are real-time data that shouldn't be cached
        // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $total_items = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}guard_dog_logs");
        // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $items = $this->get_unfiltered_items($orderby, $order, $per_page, $offset);

        return [
            'items' => $items,
            'total_items' => (int)$total_items,
            'per_page' => $per_page
        ];
    }



    /**
     * Get unfiltered items with proper WordPress compliance.
     *
     * @since 1.6.0
     * @param string $orderby The column to order by.
     * @param string $order The order direction.
     * @param int $per_page Items per page.
     * @param int $offset The offset.
     * @return array The unfiltered items.
     */
    private function get_unfiltered_items($orderby, $order, $per_page, $offset) {
        global $wpdb;
        
        // Activity logs are real-time security data that should not be cached
        // Direct database queries are appropriate for admin-only activity logging
        // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        
        // Validate orderby to prevent SQL injection
        $valid_columns = ['id', 'timestamp', 'event_type', 'username', 'ip_address'];
        if (!in_array($orderby, $valid_columns, true)) {
            $orderby = 'timestamp';
        }
        
        // Validate order direction
        $order = strtoupper($order) === 'ASC' ? 'ASC' : 'DESC';
        
        // Execute query with conditional static SQL to avoid interpolation issues
        // Note: ORDER BY columns cannot be prepared, they must be validated and escaped separately
        // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
        // ORDER BY clauses require column name interpolation which cannot be prepared
        // Column names are validated against a whitelist to prevent SQL injection
        if ($orderby === 'id' && $order === 'ASC') {
            return $wpdb->get_results(
                $wpdb->prepare(
                    "SELECT * FROM {$wpdb->prefix}guard_dog_logs ORDER BY id ASC LIMIT %d OFFSET %d",
                    $per_page,
                    $offset
                ),
                ARRAY_A
            );
        } elseif ($orderby === 'id' && $order === 'DESC') {
            return $wpdb->get_results(
                $wpdb->prepare(
                    "SELECT * FROM {$wpdb->prefix}guard_dog_logs ORDER BY id DESC LIMIT %d OFFSET %d",
                    $per_page,
                    $offset
                ),
                ARRAY_A
            );
        } elseif ($orderby === 'timestamp' && $order === 'ASC') {
            return $wpdb->get_results(
                $wpdb->prepare(
                    "SELECT * FROM {$wpdb->prefix}guard_dog_logs ORDER BY timestamp ASC LIMIT %d OFFSET %d",
                    $per_page,
                    $offset
                ),
                ARRAY_A
            );
        } elseif ($orderby === 'timestamp' && $order === 'DESC') {
            return $wpdb->get_results(
                $wpdb->prepare(
                    "SELECT * FROM {$wpdb->prefix}guard_dog_logs ORDER BY timestamp DESC LIMIT %d OFFSET %d",
                    $per_page,
                    $offset
                ),
                ARRAY_A
            );
        } elseif ($orderby === 'event_type' && $order === 'ASC') {
            return $wpdb->get_results(
                $wpdb->prepare(
                    "SELECT * FROM {$wpdb->prefix}guard_dog_logs ORDER BY event_type ASC LIMIT %d OFFSET %d",
                    $per_page,
                    $offset
                ),
                ARRAY_A
            );
        } elseif ($orderby === 'event_type' && $order === 'DESC') {
            return $wpdb->get_results(
                $wpdb->prepare(
                    "SELECT * FROM {$wpdb->prefix}guard_dog_logs ORDER BY event_type DESC LIMIT %d OFFSET %d",
                    $per_page,
                    $offset
                ),
                ARRAY_A
            );
        } elseif ($orderby === 'username' && $order === 'ASC') {
            return $wpdb->get_results(
                $wpdb->prepare(
                    "SELECT * FROM {$wpdb->prefix}guard_dog_logs ORDER BY username ASC LIMIT %d OFFSET %d",
                    $per_page,
                    $offset
                ),
                ARRAY_A
            );
        } elseif ($orderby === 'username' && $order === 'DESC') {
            return $wpdb->get_results(
                $wpdb->prepare(
                    "SELECT * FROM {$wpdb->prefix}guard_dog_logs ORDER BY username DESC LIMIT %d OFFSET %d",
                    $per_page,
                    $offset
                ),
                ARRAY_A
            );
        } elseif ($orderby === 'ip_address' && $order === 'ASC') {
            return $wpdb->get_results(
                $wpdb->prepare(
                    "SELECT * FROM {$wpdb->prefix}guard_dog_logs ORDER BY ip_address ASC LIMIT %d OFFSET %d",
                    $per_page,
                    $offset
                ),
                ARRAY_A
            );
        } elseif ($orderby === 'ip_address' && $order === 'DESC') {
            return $wpdb->get_results(
                $wpdb->prepare(
                    "SELECT * FROM {$wpdb->prefix}guard_dog_logs ORDER BY ip_address DESC LIMIT %d OFFSET %d",
                    $per_page,
                    $offset
                ),
                ARRAY_A
            );
        } else {
            // Default fallback to timestamp DESC
            return $wpdb->get_results(
                $wpdb->prepare(
                    "SELECT * FROM {$wpdb->prefix}guard_dog_logs ORDER BY timestamp DESC LIMIT %d OFFSET %d",
                    $per_page,
                    $offset
                ),
                ARRAY_A
            );
        }
        // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
        // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    }

    /**
     * Get a list of sortable columns.
     *
     * @since 1.6.0
     * @return array
     */
    protected function get_sortable_columns() {
        $sortable_columns = [
            'id'         => ['id', false], // true for default sorted
            'timestamp'  => ['timestamp', true], // Default sort by timestamp
            'event_type' => ['event_type', false],
            'username'   => ['username', false],
            'ip_address' => ['ip_address', false],
        ];
        return $sortable_columns;
    }

    /**
     * Define bulk actions.
     *
     * @since 1.6.0
     * @return array An associative array of bulk actions: slug => Pabel.
     */
    protected function get_bulk_actions() {
        $actions = [
            'bulk_delete' => __('Delete', 'guard-dog'),
        ];
        return $actions;
    }

} // End class Guard_Dog_Activity_Log_List_Table 