<?php

namespace WPUltimateSecurity\UltimateSecurity\Modules\Monitoring;

use WPUltimateSecurity\UltimateSecurity\Modules\Base\AbstractModule;
use WPUltimateSecurity\UltimateSecurity\Support\Helpers\Utils;

/**
 * Simple Event Logger
 *
 * Provides basic audit logging for security events in the free plugin.
 * Logs core security events like login attempts, 2FA verification, and lockouts.
 *
 * @package WPUltimateSecurity\UltimateSecurity\Modules\Monitoring
 * @since 2.1.0
 */
class SimpleEventLogger extends AbstractModule
{
    /**
     * Module version
     */
    protected const VERSION = '2.1.0';

    /**
     * Get unique module identifier.
     *
     * @return string
     */
    public static function getId(): string
    {
        return 'audit_logs';
    }

    /**
     * Get human-readable module name.
     *
     * @return string
     */
    public static function getName(): string
    {
        return __('Audit Logs', 'ultimate-security');
    }

    /**
     * Get module domain.
     *
     * @return string
     */
    public static function getDomain(): string
    {
        return 'monitoring';
    }

    /**
     * Check if module is enabled.
     *
     * @return bool
     */
    public static function isEnabled(): bool
    {
        $options = Utils::ultimate_security_get_option();
        return isset($options['audit_logs']['enable']) &&
            $options['audit_logs']['enable'] === '1';
    }

    /**
     * Boot the module - register hooks.
     *
     * @return void
     */
    public static function boot(): void
    {
        // Hook into WordPress events
        add_action('wp_login', [self::class, 'log_login_success'], 10, 2);
        add_action('wp_login_failed', [self::class, 'log_login_failed'], 10, 2);
        add_action('wp_logout', [self::class, 'log_logout'], 10, 1);
        add_action('password_reset', [self::class, 'log_password_reset'], 10, 2);
        add_action('profile_update', [self::class, 'log_password_changed'], 10, 3);

        // Hook into Ultimate Security events
        add_action('ultimate_security_2fa_method_enabled', [self::class, 'log_2fa_enabled'], 10, 3);
        add_action('ultimate_security_2fa_method_disabled', [self::class, 'log_2fa_disabled'], 10, 3);
        add_action('ultimate_security_2fa_verified', [self::class, 'log_2fa_success'], 10, 2);
        add_action('ultimate_security_2fa_failed', [self::class, 'log_2fa_failed'], 10, 2);
        add_action('ultimate_security_user_locked_out', [self::class, 'log_lockout'], 10, 3);
        add_action('ultimate_security_device_trusted', [self::class, 'log_device_trusted'], 10, 2);
        add_action('ultimate_security_device_revoked', [self::class, 'log_device_revoked'], 10, 2);

        // Register REST API endpoint for viewing logs
        add_action('rest_api_init', [self::class, 'register_rest_routes']);

        // Schedule cleanup cron
        if (!wp_next_scheduled('ultimate_security_cleanup_audit_logs')) {
            wp_schedule_event(time(), 'daily', 'ultimate_security_cleanup_audit_logs');
        }
        add_action('ultimate_security_cleanup_audit_logs', [self::class, 'cleanup_old_logs']);
    }
    /**
     * Event types
     */
    const EVENT_LOGIN_SUCCESS = 'login_success';
    const EVENT_LOGIN_FAILED = 'login_failed';
    const EVENT_LOGOUT = 'logout';
    const EVENT_LOCKOUT = 'user_lockout';
    const EVENT_2FA_SUCCESS = '2fa_success';
    const EVENT_2FA_FAILED = '2fa_failed';
    const EVENT_2FA_ENABLED = '2fa_enabled';
    const EVENT_2FA_DISABLED = '2fa_disabled';
    const EVENT_PASSWORD_RESET = 'password_reset';
    const EVENT_PASSWORD_CHANGED = 'password_changed';
    const EVENT_SUSPICIOUS_ACTIVITY = 'suspicious_activity';
    const EVENT_DEVICE_TRUSTED = 'device_trusted';
    const EVENT_DEVICE_REVOKED = 'device_revoked';

    /**
     * Severity levels
     */
    const SEVERITY_LOW = 'low';
    const SEVERITY_MEDIUM = 'medium';
    const SEVERITY_HIGH = 'high';
    const SEVERITY_CRITICAL = 'critical';

    /**
     * Table name
     *
     * @var string
     */
    private static $table_name;



    /**
     * Get table name
     *
     * @return string
     */
    private static function get_table_name()
    {
        if (self::$table_name === null) {
            global $wpdb;
            self::$table_name = $wpdb->prefix . 'ultimate_security_audit_logs';
        }
        return self::$table_name;
    }


    /**
     * Log an event
     *
     * @param string $event_type Event type constant
     * @param string $severity Severity level
     * @param string $message Human-readable message
     * @param array $context Additional context data
     * @param int|null $user_id User ID (defaults to current user)
     * @return int|false Inserted row ID or false on failure
     */
    public static function log($event_type, $severity, $message, $context = [], $user_id = null)
    {
        if (!self::isEnabled()) {
            return false;
        }

        global $wpdb;

        // Get user info
        if ($user_id === null) {
            $user_id = get_current_user_id();
        }

        $username = null;
        if ($user_id) {
            $user = get_user_by('id', $user_id);
            $username = $user ? $user->user_login : null;
        }

        // Get request info
        $ip_address = self::get_client_ip();
        $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? substr(sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])), 0, 500) : null;

        // Prepare data
        $data = [
            'event_type' => sanitize_key($event_type),
            'severity' => in_array($severity, [self::SEVERITY_LOW, self::SEVERITY_MEDIUM, self::SEVERITY_HIGH, self::SEVERITY_CRITICAL]) ? $severity : self::SEVERITY_LOW,
            'user_id' => $user_id ?: null,
            'username' => $username,
            'ip_address' => $ip_address,
            'user_agent' => $user_agent,
            'message' => sanitize_text_field($message),
            'context' => !empty($context) ? wp_json_encode($context) : null,
            'created_at' => current_time('mysql'),
        ];

        $result = $wpdb->insert(
            self::get_table_name(),
            $data,
            ['%s', '%s', '%d', '%s', '%s', '%s', '%s', '%s', '%s']
        );

        if ($result === false) {
            return false;
        }

        /**
         * Fires after an audit log event is recorded
         *
         * @param int $log_id The inserted log ID
         * @param string $event_type The event type
         * @param string $severity The severity level
         * @param array $data The full log data
         */
        do_action('ultimate_security_audit_log_recorded', $wpdb->insert_id, $event_type, $severity, $data);

        return $wpdb->insert_id;
    }

    /**
     * Get client IP address
     *
     * @return string
     */
    private static function get_client_ip()
    {
        $ip_keys = ['HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'REMOTE_ADDR'];

        foreach ($ip_keys as $key) {
            if (!empty($_SERVER[$key])) {
                $ip = sanitize_text_field(wp_unslash($_SERVER[$key]));
                // Handle comma-separated IPs (X-Forwarded-For)
                if (strpos($ip, ',') !== false) {
                    $ip = trim(explode(',', $ip)[0]);
                }
                if (filter_var($ip, FILTER_VALIDATE_IP)) {
                    return $ip;
                }
            }
        }

        return '0.0.0.0';
    }

    /**
     * Log successful login
     *
     * @param string $user_login Username
     * @param \WP_User $user User object
     */
    public static function log_login_success($user_login, $user)
    {
        self::log(
            self::EVENT_LOGIN_SUCCESS,
            self::SEVERITY_LOW,
            sprintf(__('User %s logged in successfully', 'ultimate-security'), $user_login),
            ['login_method' => 'standard'],
            $user->ID
        );
    }

    /**
     * Log failed login attempt
     *
     * @param string $username Username attempted
     * @param \WP_Error $error Error object
     */
    public static function log_login_failed($username, $error = null)
    {
        $user = get_user_by('login', $username);
        $user_id = $user ? $user->ID : null;

        $error_code = $error instanceof \WP_Error ? $error->get_error_code() : 'unknown';

        self::log(
            self::EVENT_LOGIN_FAILED,
            self::SEVERITY_MEDIUM,
            sprintf(__('Failed login attempt for user %s', 'ultimate-security'), $username),
            ['error_code' => $error_code, 'username_attempted' => $username],
            $user_id
        );
    }

    /**
     * Log user logout
     *
     * @param int $user_id User ID
     */
    public static function log_logout($user_id)
    {
        $user = get_user_by('id', $user_id);
        $username = $user ? $user->user_login : 'unknown';

        self::log(
            self::EVENT_LOGOUT,
            self::SEVERITY_LOW,
            sprintf(__('User %s logged out', 'ultimate-security'), $username),
            [],
            $user_id
        );
    }

    /**
     * Log password reset
     *
     * @param \WP_User $user User object
     * @param string $new_pass New password (not logged)
     */
    public static function log_password_reset($user, $new_pass)
    {
        self::log(
            self::EVENT_PASSWORD_RESET,
            self::SEVERITY_MEDIUM,
            sprintf(__('Password reset for user %s', 'ultimate-security'), $user->user_login),
            [],
            $user->ID
        );
    }

    /**
     * Log password changed via profile update
     *
     * @param int $user_id User ID
     * @param \WP_User $old_user_data Old user data
     * @param array $userdata New user data
     */
    public static function log_password_changed($user_id, $old_user_data, $userdata = [])
    {
        // Only log if password was actually changed
        if (empty($userdata['user_pass'])) {
            return;
        }

        $user = get_user_by('id', $user_id);

        self::log(
            self::EVENT_PASSWORD_CHANGED,
            self::SEVERITY_MEDIUM,
            sprintf(__('Password changed for user %s', 'ultimate-security'), $user ? $user->user_login : 'unknown'),
            [],
            $user_id
        );
    }

    /**
     * Log 2FA enabled
     *
     * @param int $user_id User ID
     * @param string $method 2FA method
     * @param \WP_User $user User object
     */
    public static function log_2fa_enabled($user_id, $method, $user)
    {
        self::log(
            self::EVENT_2FA_ENABLED,
            self::SEVERITY_LOW,
            sprintf(__('2FA enabled for user %s using %s', 'ultimate-security'), $user->user_login, $method),
            ['method' => $method],
            $user_id
        );
    }

    /**
     * Log 2FA disabled
     *
     * @param int $user_id User ID
     * @param string $method 2FA method
     * @param \WP_User $user User object
     */
    public static function log_2fa_disabled($user_id, $method, $user)
    {
        self::log(
            self::EVENT_2FA_DISABLED,
            self::SEVERITY_MEDIUM,
            sprintf(__('2FA disabled for user %s (%s)', 'ultimate-security'), $user->user_login, $method),
            ['method' => $method],
            $user_id
        );
    }

    /**
     * Log successful 2FA verification
     *
     * @param int $user_id User ID
     * @param string $method 2FA method
     */
    public static function log_2fa_success($user_id, $method)
    {
        $user = get_user_by('id', $user_id);

        self::log(
            self::EVENT_2FA_SUCCESS,
            self::SEVERITY_LOW,
            sprintf(__('2FA verification successful for user %s', 'ultimate-security'), $user ? $user->user_login : 'unknown'),
            ['method' => $method],
            $user_id
        );
    }

    /**
     * Log failed 2FA verification
     *
     * @param int $user_id User ID
     * @param string $method 2FA method
     */
    public static function log_2fa_failed($user_id, $method)
    {
        $user = get_user_by('id', $user_id);

        self::log(
            self::EVENT_2FA_FAILED,
            self::SEVERITY_HIGH,
            sprintf(__('2FA verification failed for user %s', 'ultimate-security'), $user ? $user->user_login : 'unknown'),
            ['method' => $method],
            $user_id
        );
    }

    /**
     * Log user lockout
     *
     * @param int $user_id User ID
     * @param string $reason Lockout reason
     * @param int $duration Lockout duration in seconds
     */
    public static function log_lockout($user_id, $reason, $duration)
    {
        $user = get_user_by('id', $user_id);

        self::log(
            self::EVENT_LOCKOUT,
            self::SEVERITY_HIGH,
            sprintf(__('User %s locked out: %s', 'ultimate-security'), $user ? $user->user_login : 'unknown', $reason),
            ['reason' => $reason, 'duration' => $duration],
            $user_id
        );
    }

    /**
     * Log device trusted
     *
     * @param int $user_id User ID
     * @param array $device_info Device information
     */
    public static function log_device_trusted($user_id, $device_info)
    {
        $user = get_user_by('id', $user_id);

        self::log(
            self::EVENT_DEVICE_TRUSTED,
            self::SEVERITY_LOW,
            sprintf(__('New device trusted for user %s', 'ultimate-security'), $user ? $user->user_login : 'unknown'),
            ['device' => $device_info['device_name'] ?? 'Unknown', 'browser' => $device_info['browser'] ?? 'Unknown'],
            $user_id
        );
    }

    /**
     * Log device revoked
     *
     * @param int $user_id User ID
     * @param array $device_info Device information
     */
    public static function log_device_revoked($user_id, $device_info)
    {
        $user = get_user_by('id', $user_id);

        self::log(
            self::EVENT_DEVICE_REVOKED,
            self::SEVERITY_MEDIUM,
            sprintf(__('Device revoked for user %s', 'ultimate-security'), $user ? $user->user_login : 'unknown'),
            ['device' => $device_info['device_name'] ?? 'Unknown'],
            $user_id
        );
    }

    /**
     * Clean up old logs based on retention setting
     *
     * @return int Number of rows deleted
     */
    public static function cleanup_old_logs()
    {
        global $wpdb;

        $options = Utils::ultimate_security_get_option();
        $retention_days = isset($options['audit_logs']['retention_days']) ?
            absint($options['audit_logs']['retention_days']) : 30;

        if ($retention_days < 1) {
            $retention_days = 30;
        }

        $cutoff_date = date('Y-m-d H:i:s', strtotime("-{$retention_days} days"));

        $deleted = $wpdb->query($wpdb->prepare(
            "DELETE FROM " . self::get_table_name() . " WHERE created_at < %s", // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
            $cutoff_date
        ));

        return $deleted !== false ? $deleted : 0;
    }

    /**
     * Register REST API routes
     */
    public static function register_rest_routes()
    {
        register_rest_route('ultimate-security/v1', '/audit-logs', [
            'methods' => 'GET',
            'callback' => [self::class, 'get_logs_endpoint'],
            'permission_callback' => function () {
                return current_user_can('manage_options');
            },
            'args' => [
                'page' => ['sanitize_callback' => 'absint', 'default' => 1],
                'per_page' => ['sanitize_callback' => 'absint', 'default' => 50],
                'event_type' => ['sanitize_callback' => 'sanitize_key'],
                'severity' => ['sanitize_callback' => 'sanitize_key'],
                'user_id' => ['sanitize_callback' => 'absint'],
                'search' => ['sanitize_callback' => 'sanitize_text_field'],
                'date_from' => ['sanitize_callback' => 'sanitize_text_field'],
                'date_to' => ['sanitize_callback' => 'sanitize_text_field'],
            ],
        ]);

        register_rest_route('ultimate-security/v1', '/audit-logs/stats', [
            'methods' => 'GET',
            'callback' => [self::class, 'get_stats_endpoint'],
            'permission_callback' => function () {
                return current_user_can('manage_options');
            },
        ]);

        register_rest_route('ultimate-security/v1', '/audit-logs/event-types', [
            'methods' => 'GET',
            'callback' => [self::class, 'get_event_types_endpoint'],
            'permission_callback' => function () {
                return current_user_can('manage_options');
            },
        ]);

        register_rest_route('ultimate-security/v1', '/audit-logs/cleanup', [
            'methods' => 'DELETE',
            'callback' => [self::class, 'cleanup_endpoint'],
            'permission_callback' => function () {
                return current_user_can('manage_options');
            },
            'args' => [
                'days' => ['sanitize_callback' => 'absint', 'default' => 30],
            ],
        ]);
    }

    /**
     * REST endpoint: Get logs
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public static function get_logs_endpoint(\WP_REST_Request $request)
    {
        global $wpdb;

        $page = $request->get_param('page');
        $per_page = min($request->get_param('per_page'), 100);
        $offset = ($page - 1) * $per_page;

        $where = ['1=1'];
        $values = [];

        if ($event_type = $request->get_param('event_type')) {
            $where[] = 'event_type = %s';
            $values[] = $event_type;
        }

        if ($severity = $request->get_param('severity')) {
            $where[] = 'severity = %s';
            $values[] = $severity;
        }

        if ($user_id = $request->get_param('user_id')) {
            $where[] = 'user_id = %d';
            $values[] = $user_id;
        }

        if ($search = $request->get_param('search')) {
            $where[] = '(message LIKE %s OR username LIKE %s OR ip_address LIKE %s)';
            $search_term = '%' . $wpdb->esc_like($search) . '%';
            $values[] = $search_term;
            $values[] = $search_term;
            $values[] = $search_term;
        }

        if ($date_from = $request->get_param('date_from')) {
            $where[] = 'created_at >= %s';
            $values[] = $date_from;
        }

        if ($date_to = $request->get_param('date_to')) {
            $where[] = 'created_at <= %s';
            $values[] = $date_to . ' 23:59:59';
        }

        $where_sql = implode(' AND ', $where);
        $table = self::get_table_name();

        // Get total count
        $count_query = "SELECT COUNT(*) FROM {$table} WHERE {$where_sql}";
        if (!empty($values)) {
            $total = $wpdb->get_var($wpdb->prepare($count_query, $values)); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
        } else {
            $total = $wpdb->get_var($count_query); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
        }

        // Get logs
        $query = "SELECT * FROM {$table} WHERE {$where_sql} ORDER BY created_at DESC LIMIT %d OFFSET %d";
        $values[] = $per_page;
        $values[] = $offset;

        $logs = $wpdb->get_results($wpdb->prepare($query, $values)); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared

        return new \WP_REST_Response([
            'success' => true,
            'data' => [
                'logs' => $logs,
                'total' => (int) $total,
                'page' => $page,
                'per_page' => $per_page,
                'total_pages' => ceil($total / $per_page),
            ],
        ], 200);
    }

    /**
     * REST endpoint: Get statistics
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public static function get_stats_endpoint(\WP_REST_Request $request)
    {
        global $wpdb;
        $table = self::get_table_name();

        $today = date('Y-m-d');
        $week_ago = date('Y-m-d', strtotime('-7 days'));

        $stats = [
            'total' => (int) $wpdb->get_var("SELECT COUNT(*) FROM {$table}"), // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
            'today' => (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$table} WHERE DATE(created_at) = %s", $today)), // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
            'this_week' => (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$table} WHERE created_at >= %s", $week_ago)), // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
            'by_severity' => [],
            'by_event_type' => [],
            'recent_high_severity' => [],
        ];

        // Count by severity
        $severity_counts = $wpdb->get_results("SELECT severity, COUNT(*) as count FROM {$table} GROUP BY severity", ARRAY_A); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
        foreach ($severity_counts as $row) {
            $stats['by_severity'][$row['severity']] = (int) $row['count'];
        }

        // Count by event type (last 7 days)
        $event_counts = $wpdb->get_results($wpdb->prepare(
            "SELECT event_type, COUNT(*) as count FROM {$table} WHERE created_at >= %s GROUP BY event_type ORDER BY count DESC LIMIT 10", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
            $week_ago
        ), ARRAY_A);
        foreach ($event_counts as $row) {
            $stats['by_event_type'][$row['event_type']] = (int) $row['count'];
        }

        // Recent high severity events
        $stats['recent_high_severity'] = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM {$table} WHERE severity IN ('high', 'critical') ORDER BY created_at DESC LIMIT %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
            10
        ));

        return new \WP_REST_Response([
            'success' => true,
            'data' => $stats,
        ], 200);
    }

    /**
     * REST endpoint: Get available event types
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public static function get_event_types_endpoint(\WP_REST_Request $request)
    {
        $event_types = [
            self::EVENT_LOGIN_SUCCESS => __('Login Success', 'ultimate-security'),
            self::EVENT_LOGIN_FAILED => __('Login Failed', 'ultimate-security'),
            self::EVENT_LOGOUT => __('Logout', 'ultimate-security'),
            self::EVENT_LOCKOUT => __('User Lockout', 'ultimate-security'),
            self::EVENT_2FA_SUCCESS => __('2FA Success', 'ultimate-security'),
            self::EVENT_2FA_FAILED => __('2FA Failed', 'ultimate-security'),
            self::EVENT_2FA_ENABLED => __('2FA Enabled', 'ultimate-security'),
            self::EVENT_2FA_DISABLED => __('2FA Disabled', 'ultimate-security'),
            self::EVENT_PASSWORD_RESET => __('Password Reset', 'ultimate-security'),
            self::EVENT_PASSWORD_CHANGED => __('Password Changed', 'ultimate-security'),
            self::EVENT_SUSPICIOUS_ACTIVITY => __('Suspicious Activity', 'ultimate-security'),
            self::EVENT_DEVICE_TRUSTED => __('Device Trusted', 'ultimate-security'),
            self::EVENT_DEVICE_REVOKED => __('Device Revoked', 'ultimate-security'),
        ];

        return new \WP_REST_Response([
            'success' => true,
            'data' => $event_types,
        ], 200);
    }

    /**
     * REST endpoint: Manual cleanup
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public static function cleanup_endpoint(\WP_REST_Request $request)
    {
        global $wpdb;

        $days = $request->get_param('days');
        $cutoff_date = date('Y-m-d H:i:s', strtotime("-{$days} days"));

        $deleted = $wpdb->query($wpdb->prepare(
            "DELETE FROM " . self::get_table_name() . " WHERE created_at < %s", // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
            $cutoff_date
        ));

        return new \WP_REST_Response([
            'success' => true,
            'message' => sprintf(__('Deleted %d log entries older than %d days', 'ultimate-security'), $deleted, $days),
            'deleted' => $deleted,
        ], 200);
    }
}
