<?php
if ( ! defined( 'ABSPATH' ) ) { exit; }

class Vulnity_Admin_Created_Alert extends Vulnity_Alert_Base {
    
    private static $processed_events = array();
    private static $last_cleanup = 0;
    
    public function __construct() {
        $this->alert_type = 'user_management';
        parent::__construct();
    }
    
    protected function register_hooks() {
        // User creation
        add_action('user_register', array($this, 'handle_user_created'), 10, 1);
        
        // User deletion - only use delete_user to avoid duplicates
        add_action('delete_user', array($this, 'handle_user_deleted'), 10, 1);
        
        // Role changes
        add_action('set_user_role', array($this, 'handle_role_change'), 10, 3);
    }
    
    /**
     * Handle user creation
     */
    public function handle_user_created($user_id) {
        $event_key = 'create_' . $user_id . '_' . time();
        
        if ($this->is_duplicate($event_key)) {
            return;
        }
        
        $user = get_userdata($user_id);
        if (!$user) {
            return;
        }
        
        $current_user_info = $this->get_current_user_info();
        $is_admin = in_array('administrator', $user->roles);
        
        // Always create the user creation alert
        $this->create_alert(array(
            'severity' => 'info',
            'title' => 'New User Account',
            'message' => sprintf(
                'User "%s" was created by "%s" from IP %s',
                $user->user_login,
                $current_user_info['user_login'],
                $current_user_info['user_ip']
            ),
            'details' => array(
                'action' => 'user_created',
                'user_id' => $user->ID,
                'user_login' => $user->user_login,
                'user_email' => $user->user_email,
                'user_roles' => $user->roles,
                'is_admin' => $is_admin,
                'created_by' => $current_user_info['user_login'],
                'created_by_id' => $current_user_info['user_id'],
                'created_from_ip' => $current_user_info['user_ip'],
                'timestamp' => current_time('mysql')
            )
        ));
        
        // If user was created as administrator, also create an elevation alert
        if ($is_admin) {
            $this->create_alert(array(
                'severity' => 'high',
                'title' => 'Administrator Privileges Granted',
                'message' => sprintf(
                    'New user "%s" was granted administrator privileges by "%s" from IP %s',
                    $user->user_login,
                    $current_user_info['user_login'],
                    $current_user_info['user_ip']
                ),
                'details' => array(
                    'action' => 'role_elevated',
                    'user_id' => $user->ID,
                    'user_login' => $user->user_login,
                    'user_email' => $user->user_email,
                    'new_roles' => $user->roles,
                    'old_roles' => array(),
                    'elevated_to_admin' => true,
                    'elevated_by' => $current_user_info['user_login'],
                    'elevated_by_id' => $current_user_info['user_id'],
                    'elevated_from_ip' => $current_user_info['user_ip'],
                    'timestamp' => current_time('mysql')
                )
            ));
        }
    }
    
    /**
     * Handle user deletion
     */
    public function handle_user_deleted($user_id) {
        $event_key = 'delete_' . $user_id . '_' . time();
        
        if ($this->is_duplicate($event_key)) {
            return;
        }
        
        // Get user data before deletion
        $user = get_userdata($user_id);
        $user_login = $user ? $user->user_login : 'User ID ' . $user_id;
        $user_email = $user ? $user->user_email : '';
        $user_roles = $user ? $user->roles : array();
        
        $current_user_info = $this->get_current_user_info();
        
        $this->create_alert(array(
            'severity' => 'info',
            'title' => 'User Account Removed',
            'message' => sprintf(
                'User "%s" was deleted by "%s" from IP %s',
                $user_login,
                $current_user_info['user_login'],
                $current_user_info['user_ip']
            ),
            'details' => array(
                'action' => 'user_deleted',
                'deleted_user_id' => $user_id,
                'deleted_user_login' => $user_login,
                'deleted_user_email' => $user_email,
                'deleted_user_roles' => $user_roles,
                'deleted_by' => $current_user_info['user_login'],
                'deleted_by_id' => $current_user_info['user_id'],
                'deleted_from_ip' => $current_user_info['user_ip'],
                'timestamp' => current_time('mysql')
            )
        ));
    }
    
    /**
     * Handle role changes (elevation/degradation)
     */
    public function handle_role_change($user_id, $new_role, $old_roles) {
        // Clean up old events periodically
        $this->cleanup_old_events();
        
        // Create unique event key
        $old_roles_str = is_array($old_roles) ? implode(',', $old_roles) : '';
        $event_key = 'role_' . $user_id . '_' . md5($old_roles_str . '_' . $new_role);
        
        if ($this->is_duplicate($event_key)) {
            return;
        }
        
        $user = get_userdata($user_id);
        if (!$user) {
            return;
        }
        
        // Get actual roles
        $new_roles = $user->roles;
        $old_roles = is_array($old_roles) ? $old_roles : array();
        
        // Skip if no actual change
        if ($this->arrays_equal($old_roles, $new_roles)) {
            return;
        }

        // Skip the initial role assignment that happens during fresh user creation.
        if ($this->is_initial_role_assignment_for_new_user($user, $old_roles)) {
            return;
        }
        
        // Check if this is elevation or degradation
        $was_admin = in_array('administrator', $old_roles);
        $is_admin = in_array('administrator', $new_roles);
        
        // Calculate privilege level
        $role_weights = array(
            'administrator' => 10,
            'editor' => 7,
            'author' => 5,
            'contributor' => 3,
            'subscriber' => 1
        );
        
        $old_weight = $this->calculate_role_weight($old_roles, $role_weights);
        $new_weight = $this->calculate_role_weight($new_roles, $role_weights);
        
        // Skip if weight hasn't changed
        if ($old_weight === $new_weight) {
            return;
        }
        
        $current_user_info = $this->get_current_user_info();
        
        if ($new_weight > $old_weight) {
            // Privilege elevation
            $this->create_alert(array(
                'severity' => 'high',
                'title' => 'User Privileges Elevated',
                'message' => sprintf(
                    'User "%s" privileges elevated from %s to %s by "%s" from IP %s',
                    $user->user_login,
                    $this->format_roles($old_roles),
                    $this->format_roles($new_roles),
                    $current_user_info['user_login'],
                    $current_user_info['user_ip']
                ),
                'details' => array(
                    'action' => 'role_elevated',
                    'user_id' => $user->ID,
                    'user_login' => $user->user_login,
                    'user_email' => $user->user_email,
                    'old_roles' => $old_roles,
                    'new_roles' => $new_roles,
                    'was_admin' => $was_admin,
                    'is_admin' => $is_admin,
                    'old_weight' => $old_weight,
                    'new_weight' => $new_weight,
                    'elevated_by' => $current_user_info['user_login'],
                    'elevated_by_id' => $current_user_info['user_id'],
                    'elevated_from_ip' => $current_user_info['user_ip'],
                    'timestamp' => current_time('mysql')
                )
            ));
        } else {
            // Privilege degradation
            $this->create_alert(array(
                'severity' => 'high',
                'title' => 'User Privileges Reduced',
                'message' => sprintf(
                    'User "%s" privileges reduced from %s to %s by "%s" from IP %s',
                    $user->user_login,
                    $this->format_roles($old_roles),
                    $this->format_roles($new_roles),
                    $current_user_info['user_login'],
                    $current_user_info['user_ip']
                ),
                'details' => array(
                    'action' => 'role_degraded',
                    'user_id' => $user->ID,
                    'user_login' => $user->user_login,
                    'user_email' => $user->user_email,
                    'old_roles' => $old_roles,
                    'new_roles' => $new_roles,
                    'was_admin' => $was_admin,
                    'is_admin' => $is_admin,
                    'old_weight' => $old_weight,
                    'new_weight' => $new_weight,
                    'degraded_by' => $current_user_info['user_login'],
                    'degraded_by_id' => $current_user_info['user_id'],
                    'degraded_from_ip' => $current_user_info['user_ip'],
                    'timestamp' => current_time('mysql')
                )
            ));
        }
    }
    
    /**
     * Check if event is duplicate
     */
    private function is_duplicate($event_key) {
        // Check in memory cache
        if (isset(self::$processed_events[$event_key])) {
            $age = time() - self::$processed_events[$event_key];
            if ($age < 5) { // 5 second window
                return true;
            }
        }
        
        // Also check transient for cross-request deduplication
        if (get_transient('vulnity_evt_' . $event_key)) {
            return true;
        }
        
        // Mark as processed
        self::$processed_events[$event_key] = time();
        set_transient('vulnity_evt_' . $event_key, true, 5);
        
        return false;
    }
    
    /**
     * Clean up old events from memory
     */
    private function cleanup_old_events() {
        $now = time();
        
        // Only cleanup every 30 seconds
        if ($now - self::$last_cleanup < 30) {
            return;
        }
        
        self::$last_cleanup = $now;
        
        // Remove events older than 10 seconds
        foreach (self::$processed_events as $key => $timestamp) {
            if ($now - $timestamp > 10) {
                unset(self::$processed_events[$key]);
            }
        }
    }
    
    /**
     * Calculate total weight of roles
     */
    private function calculate_role_weight($roles, $weights) {
        $total = 0;
        foreach ($roles as $role) {
            if (isset($weights[$role])) {
                $total = max($total, $weights[$role]); // Use highest role weight
            }
        }
        return $total;
    }
    
    /**
     * Format roles for display
     */
    private function format_roles($roles) {
        if (empty($roles)) {
            return 'no role';
        }
        return implode(', ', $roles);
    }
    
    /**
     * Compare arrays regardless of order
     */
    private function arrays_equal($a, $b) {
        if (!is_array($a)) $a = array();
        if (!is_array($b)) $b = array();
        
        sort($a);
        sort($b);
        
        return $a === $b;
    }

    /**
     * Detect the initial role assignment performed by wp_insert_user() so it
     * does not also appear as a privilege escalation alert.
     */
    private function is_initial_role_assignment_for_new_user($user, $old_roles) {
        if (!$user || !empty($old_roles) || empty($user->user_registered)) {
            return false;
        }

        $registered_at = strtotime($user->user_registered . ' UTC');
        if ($registered_at === false) {
            return false;
        }

        return (time() - $registered_at) <= 10;
    }
    
    /**
     * Required by parent class - not used in this implementation
     */
    protected function evaluate($data) {
        // All logic is handled in the specific handlers above
        // This method is required by the abstract parent class but not used here
        return;
    }
}
