<?php
// CORRECTED Security.php - Fixed CSRF Token Generation
// Replace your existing Security.php with this version

class Security {
    
    // Generate CSRF token - ONLY if one doesn't exist
    public static function generateToken() {
        // Only generate new token if one doesn't exist or is expired
        if (!isset($_SESSION['csrf_token']) || !isset($_SESSION['csrf_token_time']) || 
            (time() - $_SESSION['csrf_token_time']) > 3600) {
            $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
            $_SESSION['csrf_token_time'] = time();
        }
        return $_SESSION['csrf_token'];
    }
    
    // Validate CSRF token
    public static function validateToken($token) {
        // Check if session exists and token is set
        if (!isset($_SESSION) || !isset($_SESSION['csrf_token']) || !isset($_SESSION['csrf_token_time'])) {
            return false;
        }
        
        // Token expires after 1 hour (3600 seconds)
        if (time() - $_SESSION['csrf_token_time'] > 3600) {
            unset($_SESSION['csrf_token']);
            unset($_SESSION['csrf_token_time']);
            return false;
        }
        
        // Use hash_equals for timing attack protection
        return hash_equals($_SESSION['csrf_token'], $token);
    }
    
    // Generate CSRF field for forms
    public static function csrfField() {
        $token = self::generateToken();
        return '<input type="hidden" name="csrf_token" value="' . $token . '">';
    }
    
    // Clean input data
    public static function clean($data) {
        if (is_array($data)) {
            return array_map([self::class, 'clean'], $data);
        }
        return htmlspecialchars(strip_tags(trim($data)), ENT_QUOTES, 'UTF-8');
    }
    
    // Sanitize input (less aggressive than clean)
    public static function sanitize($data) {
        if (is_array($data)) {
            return array_map([self::class, 'sanitize'], $data);
        }
        return htmlspecialchars(trim($data), ENT_QUOTES, 'UTF-8');
    }
    
    // Hash password
    public static function hashPassword($password) {
        return password_hash($password, PASSWORD_DEFAULT);
    }
    
    // Verify password
    public static function verifyPassword($password, $hash) {
        return password_verify($password, $hash);
    }
    
    // Generate random string
    public static function generateRandomString($length = 32) {
        return bin2hex(random_bytes($length / 2));
    }
    
    // Validate email
    public static function validateEmail($email) {
        return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
    }
    
    // Validate phone number (Nigerian format)
    public static function validatePhone($phone) {
        // Remove spaces and dashes
        $phone = preg_replace('/[\s\-]/', '', $phone);
        
        // Check Nigerian phone format
        $pattern = '/^(\+?234|0)?[789][01]\d{8}$/';
        return preg_match($pattern, $phone) === 1;
    }
    
    // Validate URL
    public static function validateUrl($url) {
        return filter_var($url, FILTER_VALIDATE_URL) !== false;
    }
    
    // Prevent XSS
    public static function escapeOutput($data) {
        if (is_array($data)) {
            return array_map([self::class, 'escapeOutput'], $data);
        }
        return htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
    }
    
    // Encrypt data
    public static function encrypt($data) {
        $key = ENCRYPTION_KEY;
        $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
        $encrypted = openssl_encrypt($data, 'aes-256-cbc', $key, 0, $iv);
        return base64_encode($encrypted . '::' . $iv);
    }
    
    // Decrypt data
    public static function decrypt($data) {
        $key = ENCRYPTION_KEY;
        list($encrypted, $iv) = explode('::', base64_decode($data), 2);
        return openssl_decrypt($encrypted, 'aes-256-cbc', $key, 0, $iv);
    }
    
    // Rate limiting check
    public static function checkRateLimit($identifier, $max_attempts = 5, $window = 300) {
        $key = 'rate_limit_' . md5($identifier);
        
        if (!isset($_SESSION[$key])) {
            $_SESSION[$key] = [
                'count' => 1,
                'first_attempt' => time()
            ];
            return true;
        }
        
        $data = $_SESSION[$key];
        $elapsed = time() - $data['first_attempt'];
        
        // Reset if window has passed
        if ($elapsed > $window) {
            $_SESSION[$key] = [
                'count' => 1,
                'first_attempt' => time()
            ];
            return true;
        }
        
        // Check if limit exceeded
        if ($data['count'] >= $max_attempts) {
            return false;
        }
        
        // Increment count
        $_SESSION[$key]['count']++;
        return true;
    }
    
    // Validate file upload
    public static function validateFile($file, $allowed_types, $max_size = null) {
        if (!isset($file['error']) || is_array($file['error'])) {
            return ['success' => false, 'message' => 'Invalid file upload'];
        }
        
        // Check for upload errors
        switch ($file['error']) {
            case UPLOAD_ERR_OK:
                break;
            case UPLOAD_ERR_INI_SIZE:
            case UPLOAD_ERR_FORM_SIZE:
                return ['success' => false, 'message' => 'File is too large'];
            case UPLOAD_ERR_NO_FILE:
                return ['success' => false, 'message' => 'No file was uploaded'];
            default:
                return ['success' => false, 'message' => 'Unknown upload error'];
        }
        
        // Check file size
        $max_size = $max_size ?: MAX_FILE_SIZE;
        if ($file['size'] > $max_size) {
            return ['success' => false, 'message' => 'File is too large. Maximum size is ' . self::formatBytes($max_size)];
        }
        
        // Check file type
        $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
        
        $allowed = explode(',', $allowed_types);
        if (!in_array($ext, $allowed)) {
            return ['success' => false, 'message' => 'Invalid file type. Allowed types: ' . $allowed_types];
        }
        
        // Additional security check for images
        if (in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp'])) {
            $image_info = getimagesize($file['tmp_name']);
            if ($image_info === false) {
                return ['success' => false, 'message' => 'Invalid image file'];
            }
        }
        
        return ['success' => true];
    }
    
    // Format bytes
    private static function formatBytes($bytes, $precision = 2) {
        $units = ['B', 'KB', 'MB', 'GB'];
        $bytes = max($bytes, 0);
        $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
        $pow = min($pow, count($units) - 1);
        $bytes /= (1 << (10 * $pow));
        return round($bytes, $precision) . ' ' . $units[$pow];
    }
    
    // Generate secure filename
    public static function generateSecureFilename($original_name) {
        $ext = strtolower(pathinfo($original_name, PATHINFO_EXTENSION));
        $filename = bin2hex(random_bytes(16));
        return $filename . '.' . $ext;
    }
}