<?php

declare(strict_types=1);

namespace App\Services;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Str;

/**
 * Enhanced Security Service
 * 
 * Provides comprehensive security features including rate limiting,
 * input sanitization, and threat detection.
 */
class EnhancedSecurityService
{
    /**
     * Rate limiting configuration
     */
    private const RATE_LIMITS = [
        'api_requests' => ['max_attempts' => 60, 'decay_minutes' => 1],
        'login_attempts' => ['max_attempts' => 5, 'decay_minutes' => 1],
        'license_verification' => ['max_attempts' => 30, 'decay_minutes' => 1],
        'password_reset' => ['max_attempts' => 3, 'decay_minutes' => 60],
        'file_upload' => ['max_attempts' => 10, 'decay_minutes' => 1],
    ];

    /**
     * Dangerous patterns for XSS detection
     */
    private const DANGEROUS_PATTERNS = [
        '/<script[^>]*>.*?<\/script>/si',
        '/javascript:/i',
        '/vbscript:/i',
        '/on\w+\s*=/i',
        '/<iframe[^>]*>/si',
        '/<object[^>]*>/si',
        '/<embed[^>]*>/si',
        '/<applet[^>]*>/si',
        '/expression\s*\(/i',
        '/url\s*\(/i',
        '/import\s*\(/i',
        '/<meta[^>]*>/si',
        '/<link[^>]*>/si',
        '/<style[^>]*>.*?<\/style>/si',
    ];

    /**
     * Check rate limit for specific action
     *
     * @param string $action
     * @param string $identifier
     * @return bool
     */
    public function checkRateLimit(string $action, string $identifier): bool
    {
        $config = self::RATE_LIMITS[$action] ?? self::RATE_LIMITS['api_requests'];
        
        return RateLimiter::attempt(
            $action . ':' . $identifier,
            $config['max_attempts'],
            function () {
                // Rate limit not exceeded
            },
            $config['decay_minutes'] * 60
        );
    }

    /**
     * Get remaining attempts for rate limit
     *
     * @param string $action
     * @param string $identifier
     * @return int
     */
    public function getRemainingAttempts(string $action, string $identifier): int
    {
        $config = self::RATE_LIMITS[$action] ?? self::RATE_LIMITS['api_requests'];
        $key = $action . ':' . $identifier;
        
        return RateLimiter::remaining($key, $config['max_attempts']);
    }

    /**
     * Clear rate limit for specific action and identifier
     *
     * @param string $action
     * @param string $identifier
     * @return void
     */
    public function clearRateLimit(string $action, string $identifier): void
    {
        RateLimiter::clear($action . ':' . $identifier);
    }

    /**
     * Sanitize input data to prevent XSS attacks
     *
     * @param mixed $data
     * @param bool $allowHtml
     * @return mixed
     */
    public function sanitizeInput(mixed $data, bool $allowHtml = false): mixed
    {
        if (is_array($data)) {
            return array_map(fn($item) => $this->sanitizeInput($item, $allowHtml), $data);
        }

        if (!is_string($data)) {
            return $data;
        }

        // Remove null bytes
        $data = str_replace("\0", '', $data);

        // Remove dangerous patterns
        foreach (self::DANGEROUS_PATTERNS as $pattern) {
            $data = preg_replace($pattern, '', $data);
        }

        if (!$allowHtml) {
            // Strip all HTML tags
            $data = strip_tags($data);
        }

        // Convert special characters to HTML entities
        $data = htmlspecialchars($data, ENT_QUOTES | ENT_HTML5, 'UTF-8');

        return $data;
    }

    /**
     * Validate file upload security
     *
     * @param \Illuminate\Http\UploadedFile $file
     * @return array
     */
    public function validateFileUpload(\Illuminate\Http\UploadedFile $file): array
    {
        $errors = [];
        $maxSize = config('security.file_upload_security.max_size_kb', 2048) * 1024;
        $allowedMimes = config('security.file_upload_security.allowed_mimes', []);

        // Check file size
        if ($file->getSize() > $maxSize) {
            $errors[] = 'File size exceeds maximum allowed size';
        }

        // Check MIME type
        if (!empty($allowedMimes) && !in_array($file->getClientOriginalExtension(), $allowedMimes)) {
            $errors[] = 'File type not allowed';
        }

        // Check for malicious content in filename
        if ($this->containsMaliciousContent($file->getClientOriginalName())) {
            $errors[] = 'Filename contains potentially malicious content';
        }

        // Additional security checks
        if ($this->isExecutableFile($file)) {
            $errors[] = 'Executable files are not allowed';
        }

        return $errors;
    }

    /**
     * Check if content contains malicious patterns
     *
     * @param string $content
     * @return bool
     */
    public function containsMaliciousContent(string $content): bool
    {
        foreach (self::DANGEROUS_PATTERNS as $pattern) {
            if (preg_match($pattern, $content)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Check if file is potentially executable
     *
     * @param \Illuminate\Http\UploadedFile $file
     * @return bool
     */
    private function isExecutableFile(\Illuminate\Http\UploadedFile $file): bool
    {
        $executableExtensions = [
            'php', 'php3', 'php4', 'php5', 'phtml', 'pht',
            'exe', 'bat', 'cmd', 'com', 'scr', 'vbs', 'js',
            'jar', 'sh', 'pl', 'py', 'rb', 'cgi'
        ];

        $extension = strtolower($file->getClientOriginalExtension());
        
        return in_array($extension, $executableExtensions);
    }

    /**
     * Generate secure random token
     *
     * @param int $length
     * @return string
     */
    public function generateSecureToken(int $length = 32): string
    {
        return bin2hex(random_bytes($length / 2));
    }

    /**
     * Hash sensitive data for logging
     *
     * @param string $data
     * @return string
     */
    public function hashForLogging(string $data): string
    {
        return hash('sha256', $data . config('app.key'));
    }

    /**
     * Log security event
     *
     * @param string $event
     * @param Request $request
     * @param array $context
     * @return void
     */
    public function logSecurityEvent(string $event, Request $request, array $context = []): void
    {
        Log::warning('Security event: ' . $event, array_merge([
            'event' => $event,
            'url' => $request->fullUrl(),
            'method' => $request->method(),
            'ip' => $request->ip(),
            'user_agent' => $request->userAgent(),
            'user_id' => auth()->id(),
            'timestamp' => now()->toISOString(),
        ], $context));
    }

    /**
     * Check for suspicious activity patterns
     *
     * @param Request $request
     * @return array
     */
    public function detectSuspiciousActivity(Request $request): array
    {
        $suspicious = [];

        // Check for SQL injection patterns
        $sqlPatterns = [
            '/union\s+select/i',
            '/drop\s+table/i',
            '/delete\s+from/i',
            '/insert\s+into/i',
            '/update\s+set/i',
            '/or\s+1\s*=\s*1/i',
            '/and\s+1\s*=\s*1/i',
        ];

        $input = json_encode($request->all());
        foreach ($sqlPatterns as $pattern) {
            if (preg_match($pattern, $input)) {
                $suspicious[] = 'SQL injection attempt detected';
                break;
            }
        }

        // Check for XSS patterns
        if ($this->containsMaliciousContent($input)) {
            $suspicious[] = 'XSS attempt detected';
        }

        // Check for directory traversal
        if (preg_match('/\.\.\//', $input)) {
            $suspicious[] = 'Directory traversal attempt detected';
        }

        // Check for command injection
        $commandPatterns = [
            '/;\s*rm\s+/i',
            '/;\s*cat\s+/i',
            '/;\s*ls\s+/i',
            '/;\s*wget\s+/i',
            '/;\s*curl\s+/i',
            '/\|\s*nc\s+/i',
        ];

        foreach ($commandPatterns as $pattern) {
            if (preg_match($pattern, $input)) {
                $suspicious[] = 'Command injection attempt detected';
                break;
            }
        }

        return $suspicious;
    }

    /**
     * Get client fingerprint for tracking
     *
     * @param Request $request
     * @return string
     */
    public function getClientFingerprint(Request $request): string
    {
        $components = [
            $request->ip(),
            $request->userAgent(),
            $request->header('Accept-Language'),
            $request->header('Accept-Encoding'),
        ];

        return hash('sha256', implode('|', array_filter($components)));
    }

    /**
     * Check if IP is in whitelist
     *
     * @param string $ip
     * @return bool
     */
    public function isIpWhitelisted(string $ip): bool
    {
        $whitelist = config('security.ip_control.whitelist', '');
        
        if (empty($whitelist)) {
            return false;
        }

        $whitelistedIps = array_map('trim', explode(',', $whitelist));
        
        return in_array($ip, $whitelistedIps);
    }

    /**
     * Check if IP is in blacklist
     *
     * @param string $ip
     * @return bool
     */
    public function isIpBlacklisted(string $ip): bool
    {
        $blacklist = config('security.ip_control.blacklist', '');
        
        if (empty($blacklist)) {
            return false;
        }

        $blacklistedIps = array_map('trim', explode(',', $blacklist));
        
        return in_array($ip, $blacklistedIps);
    }
}
