<?php

namespace App\Services;

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

/**
 * Security Service
 * 
 * This service provides centralized security functionality including
 * input validation, threat detection, and security monitoring.
 */
class SecurityService
{
    /**
     * Validate and sanitize input data
     *
     * @param array $data
     * @param array $rules
     * @return array
     */
    public function validateAndSanitizeInput(array $data, array $rules = []): array
    {
        $sanitized = [];
        $maxStringLength = config('security.validation.max_string_length', 10000);
        $sanitizeHtml = config('security.validation.sanitize_html', true);

        foreach ($data as $key => $value) {
            if (is_string($value)) {
                // Limit string length
                if (strlen($value) > $maxStringLength) {
                    $value = substr($value, 0, $maxStringLength);
                }

                // Sanitize HTML if enabled
                if ($sanitizeHtml) {
                    $value = $this->sanitizeHtml($value);
                }

                // Apply specific validation rules if provided
                if (isset($rules[$key])) {
                    $value = $this->applyValidationRule($value, $rules[$key]);
                }
            } elseif (is_array($value)) {
                $value = $this->validateAndSanitizeInput($value, $rules[$key] ?? []);
            }

            $sanitized[$key] = $value;
        }

        return $sanitized;
    }

    /**
     * Sanitize HTML content
     *
     * @param string $content
     * @return string
     */
    public function sanitizeHtml(string $content): string
    {
        // Remove null bytes
        $content = str_replace("\0", '', $content);

        // Get allowed tags from configuration
        $allowedTags = config('security.xss_protection.allowed_tags', '');
        
        // Strip dangerous tags
        $content = strip_tags($content, $allowedTags);

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

        // Remove dangerous JavaScript patterns
        $content = $this->removeDangerousPatterns($content);

        return $content;
    }

    /**
     * Remove dangerous patterns from content
     *
     * @param string $content
     * @return string
     */
    private function removeDangerousPatterns(string $content): string
    {
        $dangerousPatterns = [
            '/javascript:/i',
            '/vbscript:/i',
            '/data:text\/html/i',
            '/on\w+\s*=/i',
            '/<script.*?<\/script>/si',
            '/<iframe.*?<\/iframe>/si',
            '/<object.*?<\/object>/si',
            '/<embed.*?<\/embed>/si',
            '/<applet.*?<\/applet>/si',
            '/expression\s*\(/i',
            '/url\s*\(/i',
            '/import\s*\(/i',
            '/@import/i',
            '/binding\s*:/i',
            '/behaviour\s*:/i',
            '/-moz-binding/i',
        ];

        foreach ($dangerousPatterns as $pattern) {
            $content = preg_replace($pattern, '', $content);
        }

        return $content;
    }

    /**
     * Apply specific validation rule
     *
     * @param mixed $value
     * @param string $rule
     * @return mixed
     */
    private function applyValidationRule($value, string $rule)
    {
        switch ($rule) {
            case 'email':
                return filter_var($value, FILTER_SANITIZE_EMAIL);
            case 'url':
                return filter_var($value, FILTER_SANITIZE_URL);
            case 'int':
                return filter_var($value, FILTER_SANITIZE_NUMBER_INT);
            case 'float':
                return filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
            case 'string':
                return filter_var($value, FILTER_SANITIZE_STRING);
            default:
                return $value;
        }
    }

    /**
     * Check if request is from a suspicious source
     *
     * @param \Illuminate\Http\Request $request
     * @return bool
     */
    public function isSuspiciousRequest(Request $request): bool
    {
        $suspiciousIndicators = [
            $this->hasHighRequestRate($request),
            $this->hasSuspiciousUserAgent($request),
            $this->hasSuspiciousHeaders($request),
            $this->isFromBlacklistedIP($request),
            $this->hasKnownAttackPatterns($request),
        ];

        return in_array(true, $suspiciousIndicators);
    }

    /**
     * Check if request has high rate
     *
     * @param \Illuminate\Http\Request $request
     * @return bool
     */
    private function hasHighRequestRate(Request $request): bool
    {
        $key = 'rate_limit:' . $request->ip();
        $maxRequests = config('security.rate_limiting.api_requests_per_minute', 60);
        
        return RateLimiter::tooManyAttempts($key, $maxRequests);
    }

    /**
     * Check if user agent is suspicious
     *
     * @param \Illuminate\Http\Request $request
     * @return bool
     */
    private function hasSuspiciousUserAgent(Request $request): bool
    {
        $userAgent = strtolower($request->userAgent() ?? '');
        
        $suspiciousAgents = [
            'bot', 'crawler', 'spider', 'scraper', 'scanner',
            'nikto', 'sqlmap', 'nmap', 'burp', 'zap',
            'python-requests', 'curl', 'wget', 'httpclient',
        ];

        foreach ($suspiciousAgents as $agent) {
            if (strpos($userAgent, $agent) !== false) {
                return true;
            }
        }

        return false;
    }

    /**
     * Check if request has suspicious headers
     *
     * @param \Illuminate\Http\Request $request
     * @return bool
     */
    private function hasSuspiciousHeaders(Request $request): bool
    {
        $suspiciousHeaders = [
            'X-Forwarded-For' => ['127.0.0.1', 'localhost'],
            'X-Real-IP' => ['127.0.0.1', 'localhost'],
            'X-Originating-IP' => ['127.0.0.1', 'localhost'],
        ];

        foreach ($suspiciousHeaders as $header => $suspiciousValues) {
            $headerValue = $request->header($header);
            if ($headerValue && in_array($headerValue, $suspiciousValues)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Check if IP is blacklisted
     *
     * @param \Illuminate\Http\Request $request
     * @return bool
     */
    private function isFromBlacklistedIP(Request $request): bool
    {
        $ip = $request->ip();
        $blacklist = explode(',', config('security.ip_control.blacklist', ''));
        
        return in_array($ip, array_filter($blacklist));
    }

    /**
     * Check if request contains known attack patterns
     *
     * @param \Illuminate\Http\Request $request
     * @return bool
     */
    private function hasKnownAttackPatterns(Request $request): bool
    {
        $attackPatterns = [
            // SQL Injection patterns
            '/union\s+select/i',
            '/select\s+.*\s+from/i',
            '/insert\s+into/i',
            '/delete\s+from/i',
            '/drop\s+table/i',
            '/update\s+.*\s+set/i',
            '/or\s+1\s*=\s*1/i',
            '/and\s+1\s*=\s*1/i',
            '/\'\s*or\s*\'/i',
            '/\"\s*or\s*\"/i',
            
            // XSS patterns
            '/<script.*?>/i',
            '/javascript:/i',
            '/on\w+\s*=/i',
            '/<iframe.*?>/i',
            
            // Command injection patterns
            '/;\s*cat\s+/i',
            '/;\s*ls\s+/i',
            '/;\s*pwd/i',
            '/;\s*id/i',
            '/;\s*whoami/i',
            '/\|\s*nc\s+/i',
            '/\|\s*netcat\s+/i',
            
            // Path traversal patterns
            '/\.\.\/\.\.\/\.\.\//i',
            '/\.\.\\\.\.\\\..\\/i',
            '/%2e%2e%2f/i',
            '/%2e%2e%5c/i',
        ];

        $requestData = json_encode($request->all());
        
        foreach ($attackPatterns as $pattern) {
            try {
                if (preg_match($pattern, $requestData)) {
                    return true;
                }
            } catch (\Exception $e) {
                // Skip invalid patterns
                continue;
            }
        }

        return false;
    }

    /**
     * Log security event
     *
     * @param string $event
     * @param array $data
     * @param string $level
     * @return void
     */
    public function logSecurityEvent(string $event, array $data = [], string $level = 'warning'): void
    {
        $logData = array_merge([
            'event' => $event,
            'timestamp' => now()->toISOString(),
            'ip' => request()->ip(),
            'user_agent' => request()->userAgent(),
            'url' => request()->fullUrl(),
            'method' => request()->method(),
        ], $data);

        // Security event logged
    }

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

    /**
     * Validate file upload security
     *
     * @param \Illuminate\Http\UploadedFile $file
     * @return array
     */
    public function validateFileUpload($file): array
    {
        $result = [
            'valid' => true,
            'errors' => [],
        ];

        // Check file size
        $maxSize = config('security.file_upload_security.max_upload_size', 10240) * 1024;
        if ($file->getSize() > $maxSize) {
            $result['valid'] = false;
            $result['errors'][] = 'File size exceeds maximum allowed size';
        }

        // Check file extension
        $allowedExtensions = config('security.file_upload_security.allowed_extensions', []);
        $extension = strtolower($file->getClientOriginalExtension());
        
        $isAllowed = false;
        foreach ($allowedExtensions as $category => $extensions) {
            if (in_array($extension, $extensions)) {
                $isAllowed = true;
                break;
            }
        }

        if (!$isAllowed) {
            $result['valid'] = false;
            $result['errors'][] = 'File type not allowed';
        }

        // Check MIME type
        $mimeType = $file->getMimeType();
        if (!$this->isAllowedMimeType($mimeType)) {
            $result['valid'] = false;
            $result['errors'][] = 'Invalid file MIME type';
        }

        // Scan file content for malicious patterns
        if (config('security.file_upload_security.validate_file_content', true)) {
            $content = file_get_contents($file->getRealPath());
            if ($this->containsMaliciousContent($content)) {
                $result['valid'] = false;
                $result['errors'][] = 'File contains potentially malicious content';
            }
        }

        return $result;
    }

    /**
     * Check if MIME type is allowed
     *
     * @param string $mimeType
     * @return bool
     */
    private function isAllowedMimeType(string $mimeType): bool
    {
        $allowedMimeTypes = [
            'image/jpeg',
            'image/png',
            'image/gif',
            'image/webp',
            'application/pdf',
            'application/msword',
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            'text/plain',
            'text/html',
            'text/css',
            'application/javascript',
            'text/javascript',
            'application/json',
        ];

        return in_array($mimeType, $allowedMimeTypes);
    }

    /**
     * Check if content contains malicious patterns
     *
     * @param string $content
     * @return bool
     */
    private function containsMaliciousContent(string $content): bool
    {
        $maliciousPatterns = [
            '/eval\s*\(/i',
            '/exec\s*\(/i',
            '/system\s*\(/i',
            '/shell_exec\s*\(/i',
            '/passthru\s*\(/i',
            '/file_get_contents\s*\(/i',
            '/file_put_contents\s*\(/i',
            '/fopen\s*\(/i',
            '/fwrite\s*\(/i',
            '/include\s*\(/i',
            '/require\s*\(/i',
            '/<\?php/i',
            '/<script.*?>/i',
            '/javascript:/i',
            '/vbscript:/i',
        ];

        foreach ($maliciousPatterns as $pattern) {
            if (preg_match($pattern, $content)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Rate limit a specific action
     *
     * @param string $key
     * @param int $maxAttempts
     * @param int $decayMinutes
     * @return bool
     */
    public function rateLimitExceeded(string $key, int $maxAttempts, int $decayMinutes): bool
    {
        return RateLimiter::tooManyAttempts($key, $maxAttempts);
    }

    /**
     * Clear rate limit for a key
     *
     * @param string $key
     * @return void
     */
    public function clearRateLimit(string $key): void
    {
        RateLimiter::clear($key);
    }
}
