<?php
/**
 * ZLO Platform - Security Module
 * Enterprise-level security protection
 */

declare(strict_types=1);

class Security
{
    private array $config;
    
    public function __construct()
    {
        $config = require __DIR__ . '/../config/config.php';
        $this->config = $config['security'];
        $this->initSession();
    }
    
    /**
     * Initialize secure session
     */
    private function initSession(): void
    {
        if (session_status() === PHP_SESSION_NONE) {
            ini_set('session.cookie_httponly', '1');
            ini_set('session.cookie_secure', '1');
            ini_set('session.cookie_samesite', 'Strict');
            ini_set('session.use_strict_mode', '1');
            ini_set('session.gc_maxlifetime', $this->config['session_lifetime']);
            
            session_name($this->config['session_name']);
            session_start();
            
            // Regenerate session ID periodically
            if (!isset($_SESSION['created'])) {
                $_SESSION['created'] = time();
            } else if (time() - $_SESSION['created'] > 1800) {
                session_regenerate_id(true);
                $_SESSION['created'] = time();
            }
        }
    }
    
    /**
     * Generate CSRF token
     */
    public function generateCsrfToken(): string
    {
        if (empty($_SESSION[$this->config['csrf_token_name']])) {
            $_SESSION[$this->config['csrf_token_name']] = bin2hex(random_bytes(32));
        }
        return $_SESSION[$this->config['csrf_token_name']];
    }
    
    /**
     * Validate CSRF token
     */
    public function validateCsrfToken(?string $token): bool
    {
        if (empty($token) || empty($_SESSION[$this->config['csrf_token_name']])) {
            return false;
        }
        return hash_equals($_SESSION[$this->config['csrf_token_name']], $token);
    }
    
    /**
     * Clear CSRF token
     */
    public function clearCsrfToken(): void
    {
        unset($_SESSION[$this->config['csrf_token_name']]);
    }
    
    /**
     * Hash password securely
     */
    public function hashPassword(string $password): string
    {
        return password_hash($password, PASSWORD_BCRYPT, [
            'cost' => $this->config['password_cost']
        ]);
    }
    
    /**
     * Verify password
     */
    public function verifyPassword(string $password, string $hash): bool
    {
        return password_verify($password, $hash);
    }
    
    /**
     * Sanitize input against XSS
     */
    public function sanitize(string $data): string
    {
        $data = trim($data);
        $data = stripslashes($data);
        $data = htmlspecialchars($data, ENT_QUOTES | ENT_HTML5, 'UTF-8');
        return $data;
    }
    
    /**
     * Sanitize array recursively
     */
    public function sanitizeArray(array $data): array
    {
        return array_map(function($item) {
            if (is_array($item)) {
                return $this->sanitizeArray($item);
            }
            return is_string($item) ? $this->sanitize($item) : $item;
        }, $data);
    }
    
    /**
     * Escape output for HTML
     */
    public function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
    }
    
    /**
     * Validate email
     */
    public function validateEmail(string $email): bool
    {
        return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
    }
    
    /**
     * Validate URL
     */
    public function validateUrl(string $url): bool
    {
        return filter_var($url, FILTER_VALIDATE_URL) !== false;
    }
    
    /**
     * Generate secure random token
     */
    public function generateToken(int $length = 32): string
    {
        return bin2hex(random_bytes($length / 2));
    }
    
    /**
     * Rate limiting check
     */
    public function checkRateLimit(string $identifier, int $maxAttempts = 5, int $window = 900): bool
    {
        $key = 'rate_limit_' . md5($identifier);
        
        if (!isset($_SESSION[$key])) {
            $_SESSION[$key] = [
                'attempts' => 1,
                'first_attempt' => time()
            ];
            return true;
        }
        
        $data = $_SESSION[$key];
        
        // Reset if window expired
        if (time() - $data['first_attempt'] > $window) {
            $_SESSION[$key] = [
                'attempts' => 1,
                'first_attempt' => time()
            ];
            return true;
        }
        
        // Check limit
        if ($data['attempts'] >= $maxAttempts) {
            return false;
        }
        
        $_SESSION[$key]['attempts']++;
        return true;
    }
    
    /**
     * Reset rate limit
     */
    public function resetRateLimit(string $identifier): void
    {
        unset($_SESSION['rate_limit_' . md5($identifier)]);
    }
    
    /**
     * Get client IP address
     */
    public function getClientIp(): string
    {
        $headers = [
            'HTTP_CF_CONNECTING_IP',
            'HTTP_X_FORWARDED_FOR',
            'HTTP_X_FORWARDED',
            'HTTP_X_CLUSTER_CLIENT_IP',
            'HTTP_FORWARDED_FOR',
            'HTTP_FORWARDED',
            'REMOTE_ADDR'
        ];
        
        foreach ($headers as $header) {
            if (!empty($_SERVER[$header])) {
                $ips = explode(',', $_SERVER[$header]);
                $ip = trim($ips[0]);
                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
                    return $ip;
                }
            }
        }
        
        return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
    }
    
    /**
     * Set secure cookie
     */
    public function setCookie(string $name, string $value, int $expire = 0, array $options = []): void
    {
        $defaults = [
            'path' => '/',
            'domain' => '',
            'secure' => true,
            'httponly' => true,
            'samesite' => 'Strict'
        ];
        
        $options = array_merge($defaults, $options);
        
        if (PHP_VERSION_ID >= 70300) {
            setcookie($name, $value, [
                'expires' => $expire,
                'path' => $options['path'],
                'domain' => $options['domain'],
                'secure' => $options['secure'],
                'httponly' => $options['httponly'],
                'samesite' => $options['samesite']
            ]);
        } else {
            setcookie(
                $name,
                $value,
                $expire,
                $options['path'] . '; SameSite=' . $options['samesite'],
                $options['domain'],
                $options['secure'],
                $options['httponly']
            );
        }
    }
    
    /**
     * Verify file upload
     */
    public function verifyUpload(array $file, array $allowedTypes, int $maxSize): array
    {
        $errors = [];
        
        if ($file['error'] !== UPLOAD_ERR_OK) {
            $errors[] = 'Upload failed with error code: ' . $file['error'];
            return ['valid' => false, 'errors' => $errors];
        }
        
        if ($file['size'] > $maxSize) {
            $errors[] = 'File size exceeds maximum allowed';
        }
        
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $mimeType = finfo_file($finfo, $file['tmp_name']);
        finfo_close($finfo);
        
        $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
        $validExtension = false;
        
        foreach ($allowedTypes as $type => $extensions) {
            if (in_array($extension, $extensions)) {
                $validExtension = true;
                break;
            }
        }
        
        if (!$validExtension) {
            $errors[] = 'Invalid file type';
        }
        
        // Check for malicious content in images
        if (strpos($mimeType, 'image/') === 0) {
            if (!getimagesize($file['tmp_name'])) {
                $errors[] = 'Invalid image file';
            }
        }
        
        return [
            'valid' => empty($errors),
            'errors' => $errors,
            'mime_type' => $mimeType,
            'extension' => $extension
        ];
    }
    
    /**
     * Generate secure filename
     */
    public function generateFilename(string $originalName, string $extension): string
    {
        $timestamp = time();
        $random = bin2hex(random_bytes(8));
        $safeName = preg_replace('/[^a-zA-Z0-9_-]/', '_', pathinfo($originalName, PATHINFO_FILENAME));
        
        return "{$timestamp}_{$random}_{$safeName}.{$extension}";
    }
    
    /**
     * Create directory if not exists
     */
    public function ensureDirectory(string $path): bool
    {
        if (!is_dir($path)) {
            return mkdir($path, 0755, true);
        }
        return true;
    }
    
    /**
     * Log security event
     */
    public function logEvent(string $event, array $data = []): void
    {
        $logEntry = [
            'timestamp' => date('Y-m-d H:i:s'),
            'event' => $event,
            'ip' => $this->getClientIp(),
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
            'data' => $data
        ];
        
        $logFile = __DIR__ . '/../../logs/security.log';
        $this->ensureDirectory(dirname($logFile));
        
        error_log(json_encode($logEntry) . "\n", 3, $logFile);
    }
}
