<?php
/**
 * Security Class - CSRF, XSS, Input Validation
 */
class Security {
    
    /**
     * Generate CSRF token
     */
    public static function csrfToken() {
        if (!Session::has('_csrf_token')) {
            Session::set('_csrf_token', bin2hex(random_bytes(32)));
        }
        return Session::get('_csrf_token');
    }
    
    /**
     * Validate CSRF token
     */
    public static function validateCsrfToken($token) {
        if (!Session::has('_csrf_token') || $token !== Session::get('_csrf_token')) {
            return false;
        }
        return true;
    }
    
    /**
     * CSRF field for forms
     */
    public static function csrfField() {
        return '<input type="hidden" name="csrf_token" value="' . self::csrfToken() . '">';
    }
    
    /**
     * Escape output for HTML (XSS prevention)
     */
    public static function escape($data) {
        if (is_array($data)) {
            return array_map([self::class, 'escape'], $data);
        }
        return htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
    }
    
    /**
     * Sanitize input
     */
    public static function sanitize($data, $type = 'string') {
        if (is_array($data)) {
            return array_map(function($item) use ($type) {
                return self::sanitize($item, $type);
            }, $data);
        }
        
        switch ($type) {
            case 'int':
                return filter_var($data, FILTER_SANITIZE_NUMBER_INT);
            case 'float':
                return filter_var($data, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
            case 'email':
                return filter_var($data, FILTER_SANITIZE_EMAIL);
            case 'url':
                return filter_var($data, FILTER_SANITIZE_URL);
            case 'string':
            default:
                return htmlspecialchars(strip_tags(trim($data)), ENT_QUOTES, 'UTF-8');
        }
    }
    
    /**
     * Validate input
     */
    public static function validate($data, $rules) {
        $errors = [];
        
        foreach ($rules as $field => $rule) {
            $value = $data[$field] ?? null;
            
            // Required
            if (strpos($rule, 'required') !== false && (empty($value) && $value !== '0')) {
                $errors[$field] = ucfirst($field) . ' is required';
                continue;
            }
            
            // Skip other validation if empty and not required
            if (empty($value) && $value !== '0') {
                continue;
            }
            
            // Email
            if (strpos($rule, 'email') !== false && !filter_var($value, FILTER_VALIDATE_EMAIL)) {
                $errors[$field] = 'Invalid email format';
            }
            
            // URL
            if (strpos($rule, 'url') !== false && !filter_var($value, FILTER_VALIDATE_URL)) {
                $errors[$field] = 'Invalid URL format';
            }
            
            // Integer
            if (strpos($rule, 'int') !== false && !filter_var($value, FILTER_VALIDATE_INT)) {
                $errors[$field] = 'Must be an integer';
            }
            
            // Numeric
            if (strpos($rule, 'numeric') !== false && !is_numeric($value)) {
                $errors[$field] = 'Must be a number';
            }
            
            // Date
            if (strpos($rule, 'date') !== false) {
                $d = DateTime::createFromFormat('Y-m-d', $value);
                if (!$d || $d->format('Y-m-d') !== $value) {
                    $errors[$field] = 'Invalid date format (YYYY-MM-DD)';
                }
            }
            
            // Min length
            preg_match('/min:(\d+)/', $rule, $minMatch);
            if ($minMatch && strlen($value) < (int)$minMatch[1]) {
                $errors[$field] = ucfirst($field) . ' must be at least ' . $minMatch[1] . ' characters';
            }
            
            // Max length
            preg_match('/max:(\d+)/', $rule, $maxMatch);
            if ($maxMatch && strlen($value) > (int)$maxMatch[1]) {
                $errors[$field] = ucfirst($field) . ' must not exceed ' . $maxMatch[1] . ' characters';
            }
            
            // Match field
            preg_match('/match:(\w+)/', $rule, $matchMatch);
            if ($matchMatch && $value !== ($data[$matchMatch[1]] ?? '')) {
                $errors[$field] = ucfirst($field) . ' does not match ' . $matchMatch[1];
            }
            
            // Unique
            preg_match('/unique:(\w+),(\w+)/', $rule, $uniqueMatch);
            if ($uniqueMatch) {
                $table = $uniqueMatch[1];
                $column = $uniqueMatch[2];
                $excludeId = $data['id'] ?? null;
                
                $sql = "SELECT COUNT(*) as count FROM $table WHERE $column = ?";
                $params = [$value];
                
                if ($excludeId) {
                    $sql .= " AND id != ?";
                    $params[] = $excludeId;
                }
                
                $result = Database::fetchOne($sql, $params);
                if ($result['count'] > 0) {
                    $errors[$field] = ucfirst($field) . ' already exists';
                }
            }
        }
        
        return $errors;
    }
    
    /**
     * Check if request is AJAX
     */
    public static function isAjax() {
        return !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && 
               strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest';
    }
    
    /**
     * Get client IP address
     */
    public static function getClientIp() {
        $ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
        
        if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
        } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
            $ip = $_SERVER['HTTP_CLIENT_IP'];
        }
        
        return filter_var($ip, FILTER_VALIDATE_IP) ? $ip : '0.0.0.0';
    }
}