<?php
/**
 * VMPFence User Security & Password Scanner
 *
 * Analyzes WordPress user accounts for username vulnerabilities, account security issues,
 * and provides framework for future password strength implementation.
 * Current focus: Username vulnerability analysis and account security patterns.
 * Future: Integration with password cracking tools for true password strength analysis.
 *
 * @package VMPFence
 * @since 1.0.0
 */

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

class VMPFence_Password_Scanner {
    
    /**
     * Scan type identifier
     * @var string
     */
    private $scan_type = 'user_security';
    
    /**
     * Scanner mode: 'username_analysis' or 'password_strength' (future)
     * @var string
     */
    private $scanner_mode = 'username_analysis';
    
    /**
     * Enable future password strength checking
     * @var bool
     */
    private $enable_password_cracking = false;
    
    /**
     * Common weak passwords list
     * @var array
     */
    private $common_weak_passwords = [
        'password', 'password1', 'password123', '123456', '123456789', 'qwerty',
        'abc123', 'password1!', 'admin', 'administrator', 'root', 'test',
        'guest', 'user', 'demo', 'welcome', 'letmein', '1234567890',
        'qwertyuiop', 'asdfghjkl', 'zxcvbnm', 'monkey', 'dragon', 'master',
        'superman', 'batman', 'football', 'baseball', 'basketball', 'soccer',
        'princess', 'flower', 'sunshine', 'rainbow', 'shadow', 'silver',
        'golden', 'winner', 'login', 'pass', 'secret', 'trustno1',
        'Password', 'Password1', 'Admin123', 'Test123', 'Welcome1',
        '12345678', '1234567', '123123', '111111', '000000', 'password!',
        'pass123', 'admin123', 'root123', 'guest123', 'test123'
    ];
    
    /**
     * Dictionary words to check against
     * @var array
     */
    private $dictionary_words = [
        'house', 'green', 'black', 'white', 'red', 'blue', 'yellow',
        'love', 'home', 'family', 'friend', 'happy', 'computer',
        'internet', 'email', 'phone', 'mobile', 'windows', 'linux',
        'apple', 'google', 'facebook', 'twitter', 'instagram', 'wordpress'
    ];
    
    /**
     * Minimum password length for security
     * @var int
     */
    private $minimum_secure_length = 12;
    
    /**
     * Debug mode flag
     * @var bool
     */
    private $debug = false;
    
    /**
     * Debug log array
     * @var array
     */
    private $debug_log = [];
    
    /**
     * Constructor
     */
    public function __construct() {
        $this->debug = defined('WP_DEBUG') && WP_DEBUG;
        
        // Future: Enable password cracking if external tools are available
        $this->enable_password_cracking = $this->check_password_tools_availability();
        
        if ($this->enable_password_cracking) {
            $this->scanner_mode = 'password_strength';
        }
    }
    
    /**
     * Check if external password cracking tools are available
     * Future implementation: Detect John the Ripper, Hashcat, etc.
     *
     * @return bool True if password cracking tools are available
     */
    private function check_password_tools_availability() {
        // Future implementation:
        // - Check for John the Ripper binary
        // - Check for Hashcat installation
        // - Verify sufficient system resources
        // - Check for dictionary files
        
        return false; // Disabled for now - requires external tools
    }
    
    /**
     * Run the password strength scan
     *
     * @return array Scan results
     */
    public function run_scan() {
        // Initialize performance controls if available
        $use_performance_controls = false;
        if (class_exists('VMPFence_Performance_Manager')) {
            VMPFence_Performance_Manager::init();
            $use_performance_controls = true;
            $this->debug_log[] = '[' . current_time('H:i:s') . '] Password Scanner: Performance controls initialized';
        }
        
        $start_time = microtime(true);
        $this->debug_log[] = '[' . current_time('H:i:s') . '] Starting Password Strength Scan';
        $this->log_debug("Starting password strength scan");
        
        try {
            // Clear previous scan data
            $this->clear_previous_scan_data();
            $this->debug_log[] = '[' . current_time('H:i:s') . '] Previous password scan data cleared';
            
            // Get all WordPress users
            $users = $this->get_all_users();
            $this->debug_log[] = '[' . current_time('H:i:s') . '] Found ' . count($users) . ' users to analyze';
            $this->log_debug("Found " . count($users) . " users to analyze");
            
            $scan_results = [];
            $users_checked = 0;
            $weak_passwords_found = 0;
            
            foreach ($users as $user) {
                // Performance throttling for user processing
                if (class_exists('VMPFence_Performance_Manager')) {
                    VMPFence_Performance_Manager::throttle_processing($users_checked);
                }
                
                $users_checked++;
                $user_issues = $this->analyze_user_password($user);
                
                if (!empty($user_issues)) {
                    $scan_results = array_merge($scan_results, $user_issues);
                    $weak_passwords_found += count($user_issues);
                }
            }
            
            $this->debug_log[] = '[Password Check] Analyzed ' . $users_checked . ' user accounts';
            
            // Additional security checks
            $admin_issues = $this->check_admin_user_security();
            if (!empty($admin_issues)) {
                $scan_results = array_merge($scan_results, $admin_issues);
                $this->debug_log[] = '[Password Check] Found ' . count($admin_issues) . ' admin security issues';
            }
            
            // Save scan results
            $this->save_scan_results($scan_results);
            $this->debug_log[] = '[Password Check] Saved ' . count($scan_results) . ' password security issues to database';
            
            $scan_time = round((microtime(true) - $start_time), 2);
            $this->debug_log[] = '[' . current_time('H:i:s') . '] Password Strength Scan Complete - Found ' . count($scan_results) . ' issues in ' . $scan_time . 's';
            $this->log_debug("Password strength scan completed in {$scan_time} seconds");
            
            // Apply performance controls to limit issues if enabled
            if (class_exists('VMPFence_Performance_Manager')) {
                $scan_results = VMPFence_Performance_Manager::limit_issues($scan_results);
                $this->debug_log[] = '[' . current_time('H:i:s') . '] Performance Manager: Limited issues to ' . count($scan_results);
            }
            
            $result = [
                'success' => true,
                'users_checked' => $users_checked,
                'issues_found' => count($scan_results),
                'issues' => $scan_results,
                'weak_passwords_found' => $weak_passwords_found,
                'scan_time' => $scan_time,
                'scan_type' => $this->scan_type,
                'debug_log' => $this->debug_log
            ];
            
            return $result;
            
        } catch (Exception $e) {
            $this->debug_log[] = '[' . current_time('H:i:s') . '] SCAN ERROR: ' . $e->getMessage();
            $this->log_debug("Password scan error: " . $e->getMessage());
            return [
                'success' => false,
                'error' => $e->getMessage(),
                'scan_type' => $this->scan_type,
                'debug_log' => $this->debug_log
            ];
        }
    }
    
    /**
     * Get all WordPress users
     *
     * @return array Array of WP_User objects
     */
    private function get_all_users() {
        $users = get_users([
            'number' => -1, // Get all users
            'fields' => 'all'
        ]);
        
        return $users;
    }
    
    /**
     * Analyze a user's password for security issues
     *
     * @param WP_User $user WordPress user object
     * @return array Array of password issues found
     */
    private function analyze_user_password($user) {
        $issues = [];
        
        // Note: WordPress doesn't store plain text passwords, so we can't directly check them
        // Instead, we'll check for other password-related security issues
        
        // Check for default usernames (security risk)
        if ($this->is_risky_username($user->user_login)) {
            $this->debug_log[] = '[Password Check] Found risky username: ' . $user->user_login;
            $issues[] = [
                'type' => 'weak_username',
                'severity' => 'high',
                'title' => 'Risky Username Detected',
                'description' => "User '{$user->user_login}' uses a common/predictable username that increases brute force attack risk.",
                'details' => "Username: {$user->user_login} (ID: {$user->ID})",
                'recommendation' => 'Consider changing this username or implementing strong password policies and 2FA.',
                'user_id' => $user->ID,
                'username' => $user->user_login,
                'user_role' => implode(', ', $user->roles)
            ];
        }
        
        // Check for admin users with simple usernames
        if ($this->is_admin_user($user) && $this->is_simple_username($user->user_login)) {
            $this->debug_log[] = '[Password Check] Found admin with simple username: ' . $user->user_login;
            $issues[] = [
                'type' => 'admin_simple_username',
                'severity' => 'high',
                'title' => 'Administrator with Simple Username',
                'description' => "Administrator '{$user->user_login}' has a simple username that could be easily guessed.",
                'details' => "Admin Username: {$user->user_login} (ID: {$user->ID})",
                'recommendation' => 'Admin users should have complex, non-predictable usernames.',
                'user_id' => $user->ID,
                'username' => $user->user_login,
                'user_role' => implode(', ', $user->roles)
            ];
        }
        
        // Check if user email matches username (potential security risk)
        if ($user->user_login === $user->user_email || 
            strpos($user->user_email, $user->user_login . '@') === 0) {
            $this->debug_log[] = '[Password Check] Found username-email match: ' . $user->user_login;
            $issues[] = [
                'type' => 'username_email_match',
                'severity' => 'medium',
                'title' => 'Username Matches Email Pattern',
                'description' => "User '{$user->user_login}' has a username that matches their email, making it easier to guess.",
                'details' => "Username: {$user->user_login}, Email: {$user->user_email}",
                'recommendation' => 'Use different usernames and email addresses for better security.',
                'user_id' => $user->ID,
                'username' => $user->user_login,
                'user_role' => implode(', ', $user->roles)
            ];
        }
        
        // Check for users without display names (potential enumeration risk)
        if (empty($user->display_name) || $user->display_name === $user->user_login) {
            $this->debug_log[] = '[Password Check] Found username as display name: ' . $user->user_login;
            $issues[] = [
                'type' => 'username_display_name',
                'severity' => 'low',
                'title' => 'Username Exposed as Display Name',
                'description' => "User '{$user->user_login}' has their username exposed as display name.",
                'details' => "Username/Display Name: {$user->user_login}",
                'recommendation' => 'Set a different display name to prevent username enumeration.',
                'user_id' => $user->ID,
                'username' => $user->user_login,
                'user_role' => implode(', ', $user->roles)
            ];
        }
        
        return $issues;
    }
    
    /**
     * Check if username is risky (common attack targets)
     *
     * @param string $username Username to check
     * @return bool True if risky
     */
    private function is_risky_username($username) {
        $risky_usernames = [
            'admin', 'administrator', 'root', 'test', 'demo', 'guest',
            'user', 'manager', 'support', 'service', 'operator',
            'wp-admin', 'wordpress', 'wp', 'blog', 'website'
        ];
        
        return in_array(strtolower($username), $risky_usernames);
    }
    
    /**
     * Check if username is too simple
     *
     * @param string $username Username to check
     * @return bool True if simple
     */
    private function is_simple_username($username) {
        // Check for simple patterns
        if (strlen($username) < 4) {
            return true;
        }
        
        // Check for numeric only
        if (is_numeric($username)) {
            return true;
        }
        
        // Check for simple patterns like user1, admin1, test123
        if (preg_match('/^(user|admin|test|demo)\d*$/i', $username)) {
            return true;
        }
        
        return false;
    }
    
    /**
     * Check if user has admin privileges
     *
     * @param WP_User $user WordPress user object
     * @return bool True if admin
     */
    private function is_admin_user($user) {
        return in_array('administrator', $user->roles) || user_can($user, 'manage_options');
    }
    
    /**
     * Check admin user security issues
     *
     * @return array Array of admin security issues
     */
    private function check_admin_user_security() {
        $issues = [];
        
        // Check for multiple admin users (security concern)
        $admin_users = get_users(['role' => 'administrator']);
        $this->debug_log[] = '[Password Check] Found ' . count($admin_users) . ' administrator accounts';
        
        if (count($admin_users) > 3) {
            $this->debug_log[] = '[Password Check] WARNING: Excessive admin users detected (' . count($admin_users) . ')';
            $issues[] = [
                'type' => 'multiple_admins',
                'severity' => 'medium',
                'title' => 'Multiple Administrator Accounts',
                'description' => 'Your site has ' . count($admin_users) . ' administrator accounts, which increases security risk.',
                'details' => 'Admin users: ' . implode(', ', array_map(function($u) { return $u->user_login; }, $admin_users)),
                'recommendation' => 'Limit administrator accounts to only necessary users and use lower privilege roles when possible.',
                'user_count' => count($admin_users)
            ];
        }
        
        // Check for admin user with ID 1 (common attack target)
        $admin_user_1 = get_user_by('id', 1);
        if ($admin_user_1 && in_array('administrator', $admin_user_1->roles)) {
            $this->debug_log[] = '[Password Check] WARNING: Admin user with ID 1 detected: ' . $admin_user_1->user_login;
            $issues[] = [
                'type' => 'admin_user_id_1',
                'severity' => 'medium',
                'title' => 'Administrator User with ID 1',
                'description' => "Administrator user '{$admin_user_1->user_login}' has user ID 1, which is commonly targeted by attackers.",
                'details' => "Username: {$admin_user_1->user_login}, ID: 1",
                'recommendation' => 'Consider creating a new admin user and demoting or removing this account.',
                'user_id' => 1,
                'username' => $admin_user_1->user_login
            ];
        }
        
        return $issues;
    }
    
    /**
     * Clear previous scan data for this scan type
     */
    private function clear_previous_scan_data() {
        global $wpdb;
        
        // Clear previous scan issues from new table structure
        $issues_table = $wpdb->prefix . 'vmpfence_scan_issues';
        
        $deleted_issues = $wpdb->query($wpdb->prepare(
            "DELETE FROM {$issues_table} WHERE issue_type = %s AND status != 'ignored'",  // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
            $this->scan_type
        ));
        
        // Clear previous scan results
        $results_table = $wpdb->prefix . 'vmpfence_scan_results';
        $deleted_results = $wpdb->query($wpdb->prepare(
            "DELETE FROM {$results_table} WHERE scan_type = %s",  // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
            $this->scan_type
        ));
        $this->log_debug("Cleared previous password strength scan data from both issues and results tables");
    }
    
    /**
     * Save scan results to database
     *
     * @param array $results Scan results to save
     */
    private function save_scan_results($results) {
        if (empty($results)) {
            $this->log_debug("No password strength issues found");
            return;
        }

        global $wpdb;
        
        // Save scan summary to scan_results table
        $scan_results_table = $wpdb->prefix . 'vmpfence_scan_results';
        $scan_result_id = $wpdb->insert(
            $scan_results_table,
            [
                'scan_type' => 'password_security',
                'scan_time' => current_time('mysql'),
                'total_files' => 0, // Password scans don't scan files
                'issues_found' => count($results),
                'scan_status' => 'completed',
                'scan_data' => wp_json_encode([
                    'users_scanned' => count($results),
                    'scan_type' => 'password_security'
                ])
            ]
        );
        
        if ($scan_result_id === false) {
            $this->log_debug('Error saving scan summary: ' . $wpdb->last_error);
            return;
        }
        
        $scan_id = $wpdb->insert_id;
        
        // Save individual issues to scan_issues table
        $scan_issues_table = $wpdb->prefix . 'vmpfence_scan_issues';
        
        foreach ($results as $result) {
            $file_path = isset($result['username']) ? 'user:' . $result['username'] : '';
            $description = $result['title'] . ': ' . $result['description'];
            
            // Check if this issue is already ignored
            if ($this->handle_ignored_issue($file_path, 'password_security', $description, $scan_id)) {
                continue; // Issue was handled as ignored, skip normal processing
            }
            
            if ($this->is_issue_ignored($file_path, 'password_security', $description)) {
                continue; // Issue is ignored, skip
            }
            
            $insert_result = $wpdb->insert(
                $scan_issues_table,
                [
                    'scan_id' => $scan_id,
                    'file_path' => $file_path,
                    'issue_type' => 'password_security',
                    'severity' => $result['severity'],
                    'description' => $description,
                    'detected_time' => current_time('mysql'),
                    'status' => 'new'
                ]
            );
            
            if ($insert_result === false) {
                $this->log_debug('Error saving password issue: ' . $wpdb->last_error);
            }
        }
        
        $this->log_debug("Saved scan summary (ID: " . $scan_id . ") and " . count($results) . " password strength issues to database");
    }
    
    /**
     * Map text severity to numeric value for database storage
     *
     * @param string $severity Severity text (critical, high, medium, low)
     * @return int Numeric severity value
     */
    private function map_severity_to_number($severity) {
        switch (strtolower($severity)) {
            case 'critical':
                return 4;
            case 'high':
                return 3;
            case 'medium':
                return 2;
            case 'low':
                return 1;
            default:
                return 2; // Default to medium
        }
    }
    
    /**
     * Get password strength statistics
     *
     * @return array Statistics about password security
     */
    public function get_password_stats() {
        global $wpdb;
        
        $total_users = count(get_users(['fields' => 'ID']));
        $admin_users = count(get_users(['role' => 'administrator']));
        
        $table = $wpdb->prefix . 'vmpfence_scan_issues';
        $issues_count = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$table} WHERE issue_type = %s AND status != 'ignored'",  // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
            'password_security'
        ));
        
        return [
            'total_users' => $total_users,
            'admin_users' => $admin_users,
            'password_issues' => (int) $issues_count,
            'security_score' => $this->calculate_password_security_score($total_users, $issues_count)
        ];
    }
    
    /**
     * Calculate password security score
     *
     * @param int $total_users Total number of users
     * @param int $issues_count Number of password issues
     * @return int Security score (0-100)
     */
    private function calculate_password_security_score($total_users, $issues_count) {
        if ($total_users === 0) {
            return 100;
        }
        
        $issue_ratio = $issues_count / $total_users;
        $score = max(0, 100 - ($issue_ratio * 100));
        
        return (int) round($score);
    }
    
    /**
     * Log debug messages
     *
     * @param string $message Debug message
     */
    private function log_debug($message) {
        // if ($this->debug) {
        //     // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Intentional for debugging
        //     error_log("VMPFence Password Scanner: " . $message);
        // }
    }
    
    /**
     * Get scan type identifier
     *
     * @return string Scan type
     */
    public function get_scan_type() {
        return $this->scan_type;
    }
    
    /**
     * Simulate password strength check (for educational purposes)
     * 
     * Note: This is a simulation since WordPress passwords are hashed
     * Real implementation would require additional tools/plugins
     *
     * @param string $password Plain text password to check
     * @return array Password analysis results
     */
    public function simulate_password_check($password) {
        $score = 0;
        $issues = [];
        $suggestions = [];
        
        // Length check
        if (strlen($password) < 8) {
            $issues[] = 'Password is too short (minimum 8 characters)';
        } elseif (strlen($password) < $this->minimum_secure_length) {
            $issues[] = 'Password should be at least ' . $this->minimum_secure_length . ' characters';
        } else {
            $score += 20;
        }
        
        // Character variety
        if (preg_match('/[a-z]/', $password)) $score += 10;
        else $suggestions[] = 'Add lowercase letters';
        
        if (preg_match('/[A-Z]/', $password)) $score += 10;
        else $suggestions[] = 'Add uppercase letters';
        
        if (preg_match('/[0-9]/', $password)) $score += 10;
        else $suggestions[] = 'Add numbers';
        
        if (preg_match('/[^a-zA-Z0-9]/', $password)) $score += 20;
        else $suggestions[] = 'Add special characters';
        
        // Common password check
        if (in_array(strtolower($password), array_map('strtolower', $this->common_weak_passwords))) {
            $score = 0;
            $issues[] = 'This is a commonly used weak password';
        }
        
        // Dictionary word check
        foreach ($this->dictionary_words as $word) {
            if (stripos($password, $word) !== false) {
                $score -= 15;
                $issues[] = "Contains dictionary word: {$word}";
                break;
            }
        }
        
        $score = max(0, min(100, $score));
        
        if ($score >= 80) $strength = 'Strong';
        elseif ($score >= 60) $strength = 'Good';
        elseif ($score >= 40) $strength = 'Fair';
        else $strength = 'Weak';
        
        return [
            'score' => $score,
            'strength' => $strength,
            'issues' => $issues,
            'suggestions' => $suggestions
        ];
    }
    
    /**
     * Check if an issue is already ignored
     */
    private function is_issue_ignored($file_path, $issue_type, $description) {
        global $wpdb;
        
        $issues_table = $wpdb->prefix . 'vmpfence_scan_issues';
        
        $count = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$issues_table} WHERE file_path = %s AND issue_type = %s AND description = %s AND status = 'ignored'",  // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
            $file_path,
            $issue_type,
            $description
        ));
        
        return $count > 0;
    }
    
    /**
     * Handle previously ignored issues - recreate them in current scan as ignored
     */
    private function handle_ignored_issue($file_path, $issue_type, $description, $scan_id) {
        global $wpdb;
        
        $issues_table = $wpdb->prefix . 'vmpfence_scan_issues';
        
        // Check if this exact issue was previously ignored
        $ignored_issue = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM {$issues_table} WHERE file_path = %s AND issue_type = %s AND description = %s AND status = 'ignored' ORDER BY detected_time DESC LIMIT 1",  // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
            $file_path,
            $issue_type,
            $description
        ));
        
        if ($ignored_issue) {
            // Recreate the ignored issue in current scan
            $insert_result = $wpdb->insert(
                $issues_table,
                [
                    'scan_id' => $scan_id,
                    'file_path' => $file_path,
                    'issue_type' => $issue_type,
                    'severity' => $ignored_issue->severity ?? 'medium',
                    'description' => $description,
                    'status' => 'ignored',
                    'detected_time' => current_time('mysql'),
                    'resolved_time' => current_time('mysql')
                ],
                ['%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s']
            );
            
            if ($insert_result) {
                // Clean up old ignored entries for this same issue (keep only the latest)
                $wpdb->query($wpdb->prepare(
                    "DELETE FROM {$issues_table} WHERE file_path = %s AND issue_type = %s AND description = %s AND status = 'ignored' AND id != %d",  // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
                    $file_path,
                    $issue_type,
                    $description,
                    $wpdb->insert_id
                ));
                
                return true;
            }
        }
        
        return false;
    }
}
