<?php
/**
 * AUTO TESTER - Security Enhancements
 * Account lockout, rate limiting, and additional security measures
 * Created: 2026-04-22
 */

require_once __DIR__ . '/../config.php';
require_once __DIR__ . '/../database/functions/Database.php';

class SecurityEnhancements {
    private $db;
    private $maxLoginAttempts = 5;
    private $lockoutDuration = 900; // 15 minutes
    private $rateLimitWindow = 3600; // 1 hour
    private $maxRequestsPerWindow = 1000;
    
    public function __construct() {
        $this->db = Database::getInstance();
    }
    
    /**
     * Check if user is locked out due to failed login attempts
     */
    public function isUserLockedOut($email) {
        try {
            $stmt = $this->db->prepare("
                SELECT COUNT(*) as attempt_count, 
                       MAX(created_at) as last_attempt
                FROM login_attempts 
                WHERE email = ? AND created_at > DATE_SUB(NOW(), INTERVAL ? SECOND)
            ");
            
            $stmt->execute([$email, $this->lockoutDuration]);
            $result = $stmt->fetch();
            
            if ($result['attempt_count'] >= $this->maxLoginAttempts) {
                return [
                    'locked' => true,
                    'remaining_time' => $this->lockoutDuration - (time() - strtotime($result['last_attempt'])),
                    'attempts' => $result['attempt_count']
                ];
            }
            
            return ['locked' => false];
            
        } catch (Exception $e) {
            // Log error but don't block login on database errors
            error_log("Lockout check error: " . $e->getMessage());
            return ['locked' => false];
        }
    }
    
    /**
     * Record failed login attempt
     */
    public function recordFailedLogin($email, $ipAddress = null) {
        try {
            $stmt = $this->db->prepare("
                INSERT INTO login_attempts (email, ip_address, created_at)
                VALUES (?, ?, NOW())
            ");
            
            $stmt->execute([$email, $ipAddress]);
            
            // Clean old attempts
            $this->cleanupOldAttempts();
            
        } catch (Exception $e) {
            error_log("Failed to record login attempt: " . $e->getMessage());
        }
    }
    
    /**
     * Clear login attempts after successful login
     */
    public function clearLoginAttempts($email) {
        try {
            $stmt = $this->db->prepare("
                DELETE FROM login_attempts WHERE email = ?
            ");
            
            $stmt->execute([$email]);
            
        } catch (Exception $e) {
            error_log("Failed to clear login attempts: " . $e->getMessage());
        }
    }
    
    /**
     * Check API rate limit
     */
    public function checkRateLimit($endpoint, $ipAddress, $userId = null) {
        try {
            $identifier = $userId ? "user:$userId" : "ip:$ipAddress";
            
            $stmt = $this->db->prepare("
                SELECT COUNT(*) as request_count
                FROM api_rate_limits 
                WHERE identifier = ? AND endpoint = ? AND created_at > DATE_SUB(NOW(), INTERVAL ? SECOND)
            ");
            
            $stmt->execute([$identifier, $endpoint, $this->rateLimitWindow]);
            $result = $stmt->fetch();
            
            if ($result['request_count'] >= $this->maxRequestsPerWindow) {
                return [
                    'limited' => true,
                    'remaining_time' => $this->rateLimitWindow - (time() - strtotime($result['last_request'] ?? time())),
                    'requests' => $result['request_count']
                ];
            }
            
            // Record this request
            $this->recordApiRequest($identifier, $endpoint);
            
            return ['limited' => false];
            
        } catch (Exception $e) {
            error_log("Rate limit check error: " . $e->getMessage());
            return ['limited' => false]; // Don't block on database errors
        }
    }
    
    /**
     * Record API request for rate limiting
     */
    private function recordApiRequest($identifier, $endpoint) {
        try {
            $stmt = $this->db->prepare("
                INSERT INTO api_rate_limits (identifier, endpoint, created_at)
                VALUES (?, ?, NOW())
            ");
            
            $stmt->execute([$identifier, $endpoint]);
            
            // Clean old records
            $this->cleanupOldRateLimits();
            
        } catch (Exception $e) {
            error_log("Failed to record API request: " . $e->getMessage());
        }
    }
    
    /**
     * Validate password strength
     */
    public function validatePasswordStrength($password) {
        $errors = [];
        
        // Minimum length
        if (strlen($password) < 8) {
            $errors[] = "Password must be at least 8 characters long";
        }
        
        // Require uppercase letter
        if (!preg_match('/[A-Z]/', $password)) {
            $errors[] = "Password must contain at least one uppercase letter";
        }
        
        // Require lowercase letter
        if (!preg_match('/[a-z]/', $password)) {
            $errors[] = "Password must contain at least one lowercase letter";
        }
        
        // Require number
        if (!preg_match('/[0-9]/', $password)) {
            $errors[] = "Password must contain at least one number";
        }
        
        // Require special character
        if (!preg_match('/[!@#$%^&*(),.?":{}|<>]/', $password)) {
            $errors[] = "Password must contain at least one special character";
        }
        
        // Check for common patterns
        $commonPatterns = ['123456', 'password', 'qwerty', 'admin', 'letmein'];
        foreach ($commonPatterns as $pattern) {
            if (stripos($password, $pattern) !== false) {
                $errors[] = "Password cannot contain common patterns";
                break;
            }
        }
        
        return [
            'valid' => empty($errors),
            'errors' => $errors,
            'strength' => $this->calculatePasswordStrength($password)
        ];
    }
    
    /**
     * Calculate password strength score
     */
    private function calculatePasswordStrength($password) {
        $score = 0;
        
        // Length contribution
        $score += min(strlen($password) * 2, 20);
        
        // Character variety
        if (preg_match('/[A-Z]/', $password)) $score += 10;
        if (preg_match('/[a-z]/', $password)) $score += 10;
        if (preg_match('/[0-9]/', $password)) $score += 10;
        if (preg_match('/[!@#$%^&*(),.?":{}|<>]/', $password)) $score += 15;
        
        // Complexity bonus
        $uniqueChars = count(array_unique(str_split($password)));
        $score += min($uniqueChars * 2, 25);
        
        return min($score, 100);
    }
    
    /**
     * Validate request size
     */
    public function validateRequestSize($data, $maxSize = 1048576) { // 1MB default
        $contentLength = strlen(json_encode($data));
        
        if ($contentLength > $maxSize) {
            return [
                'valid' => false,
                'error' => 'Request too large',
                'size' => $contentLength,
                'max_size' => $maxSize
            ];
        }
        
        return ['valid' => true, 'size' => $contentLength];
    }
    
    /**
     * Generate secure random token
     */
    public function generateSecureToken($length = 32) {
        if (function_exists('random_bytes')) {
            return bin2hex(random_bytes($length));
        } elseif (function_exists('openssl_random_pseudo_bytes')) {
            return bin2hex(openssl_random_pseudo_bytes($length));
        } else {
            // Fallback (less secure)
            return substr(md5(uniqid(mt_rand(), true)), 0, $length);
        }
    }
    
    /**
     * Sanitize input data
     */
    public function sanitizeInput($data) {
        if (is_array($data)) {
            return array_map([$this, 'sanitizeInput'], $data);
        } elseif (is_string($data)) {
            return htmlspecialchars(trim($data), ENT_QUOTES, 'UTF-8');
        }
        
        return $data;
    }
    
    /**
     * Validate email format
     */
    public function validateEmail($email) {
        return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
    }
    
    /**
     * Check for suspicious activity patterns
     */
    public function checkSuspiciousActivity($email, $ipAddress) {
        try {
            $stmt = $this->db->prepare("
                SELECT COUNT(*) as activity_count
                FROM security_events 
                WHERE (email = ? OR ip_address = ?) 
                AND created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR)
                AND event_type IN ('failed_login', 'blocked_request', 'suspicious_activity')
            ");
            
            $stmt->execute([$email, $ipAddress]);
            $result = $stmt->fetch();
            
            if ($result['activity_count'] > 10) {
                // Log suspicious activity
                $this->logSecurityEvent('suspicious_activity', $email, $ipAddress, 'High activity detected');
                
                return [
                    'suspicious' => true,
                    'activity_count' => $result['activity_count']
                ];
            }
            
            return ['suspicious' => false];
            
        } catch (Exception $e) {
            error_log("Suspicious activity check error: " . $e->getMessage());
            return ['suspicious' => false];
        }
    }
    
    /**
     * Log security event
     */
    public function logSecurityEvent($eventType, $email = null, $ipAddress = null, $details = null) {
        try {
            $stmt = $this->db->prepare("
                INSERT INTO security_events (event_type, email, ip_address, details, created_at)
                VALUES (?, ?, ?, ?, NOW())
            ");
            
            $stmt->execute([$eventType, $email, $ipAddress, $details]);
            
        } catch (Exception $e) {
            error_log("Failed to log security event: " . $e->getMessage());
        }
    }
    
    /**
     * Clean old login attempts
     */
    private function cleanupOldAttempts() {
        try {
            $stmt = $this->db->prepare("
                DELETE FROM login_attempts 
                WHERE created_at < DATE_SUB(NOW(), INTERVAL 1 DAY)
            ");
            
            $stmt->execute();
            
        } catch (Exception $e) {
            error_log("Failed to cleanup old login attempts: " . $e->getMessage());
        }
    }
    
    /**
     * Clean old rate limit records
     */
    private function cleanupOldRateLimits() {
        try {
            $stmt = $this->db->prepare("
                DELETE FROM api_rate_limits 
                WHERE created_at < DATE_SUB(NOW(), INTERVAL 1 HOUR)
            ");
            
            $stmt->execute();
            
        } catch (Exception $e) {
            error_log("Failed to cleanup old rate limits: " . $e->getMessage());
        }
    }
    
    /**
     * Get security statistics
     */
    public function getSecurityStats() {
        try {
            $stats = [];
            
            // Failed login attempts (last 24 hours)
            $stmt = $this->db->prepare("
                SELECT COUNT(*) as count FROM login_attempts 
                WHERE created_at > DATE_SUB(NOW(), INTERVAL 24 HOUR)
            ");
            $stmt->execute();
            $stats['failed_logins_24h'] = $stmt->fetch()['count'];
            
            // Blocked requests (last 24 hours)
            $stmt = $this->db->prepare("
                SELECT COUNT(*) as count FROM security_events 
                WHERE event_type = 'blocked_request' 
                AND created_at > DATE_SUB(NOW(), INTERVAL 24 HOUR)
            ");
            $stmt->execute();
            $stats['blocked_requests_24h'] = $stmt->fetch()['count'];
            
            // Suspicious activities (last 24 hours)
            $stmt = $this->db->prepare("
                SELECT COUNT(*) as count FROM security_events 
                WHERE event_type = 'suspicious_activity' 
                AND created_at > DATE_SUB(NOW(), INTERVAL 24 HOUR)
            ");
            $stmt->execute();
            $stats['suspicious_activities_24h'] = $stmt->fetch()['count'];
            
            // Locked out users
            $stmt = $this->db->prepare("
                SELECT COUNT(DISTINCT email) as count FROM login_attempts 
                WHERE created_at > DATE_SUB(NOW(), INTERVAL ? SECOND)
                GROUP BY email HAVING COUNT(*) >= ?
            ");
            $stmt->execute([$this->lockoutDuration, $this->maxLoginAttempts]);
            $stats['locked_users'] = $stmt->rowCount();
            
            return [
                'success' => true,
                'data' => $stats
            ];
            
        } catch (Exception $e) {
            return [
                'success' => false,
                'message' => 'Database error: ' . $e->getMessage()
            ];
        }
    }
}

// Usage example for login protection
if (basename($_SERVER['PHP_SELF']) === 'security-enhancements.php') {
    $security = new SecurityEnhancements();
    
    // Example: Check if user is locked out
    $email = $_POST['email'] ?? '';
    $lockoutStatus = $security->isUserLockedOut($email);
    
    if ($lockoutStatus['locked']) {
        header('HTTP/1.1 429 Too Many Requests');
        echo json_encode([
            'success' => false,
            'message' => 'Account temporarily locked. Please try again later.',
            'remaining_time' => $lockoutStatus['remaining_time']
        ]);
        exit;
    }
    
    // Example: Check rate limit
    $endpoint = $_SERVER['REQUEST_URI'];
    $ipAddress = $_SERVER['REMOTE_ADDR'];
    $rateLimitStatus = $security->checkRateLimit($endpoint, $ipAddress);
    
    if ($rateLimitStatus['limited']) {
        header('HTTP/1.1 429 Too Many Requests');
        echo json_encode([
            'success' => false,
            'message' => 'Rate limit exceeded. Please try again later.',
            'remaining_time' => $rateLimitStatus['remaining_time']
        ]);
        exit;
    }
}
?>
