<?php

namespace App\Http\Middleware;

use App\Models\SecuritySetting;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;

class SecuritySettingsMiddleware
{
    /**
     * اعمال تنظیمات امنیتی بر روی درخواست
     */
    public function handle(Request $request, Closure $next)
    {
        // بررسی و اعمال تنظیمات XSS و CSRF
        $this->applyXssProtection($request);

        // بررسی و اعمال تنظیمات نشست
        $this->applySessionSettings($request);

        // بررسی و اعمال تنظیمات فایل
        if ($request->hasFile('*')) {
            $this->applyFileProtection($request);
        }

        // بررسی و اعمال تنظیمات محافظت در برابر حملات
        $this->applyBruteForceProtection($request);

        return $next($request);
    }

    /**
     * اعمال تنظیمات محافظت XSS
     */
    protected function applyXssProtection(Request $request): void
    {
        $settings = $this->getCachedSettings('xss');

        // اعمال سیاست امنیتی محتوا
        if (isset($settings['csp_policy'])) {
            header('Content-Security-Policy: '.$settings['csp_policy']);
        }

        // بررسی الگوهای XSS در داده‌های ورودی
        if (isset($settings['xss_patterns'])) {
            $patterns = json_decode($settings['xss_patterns'], true);
            $input = $request->all();

            array_walk_recursive($input, function (&$value) use ($patterns) {
                if (is_string($value)) {
                    foreach ($patterns as $pattern) {
                        if (stripos($value, $pattern) !== false) {
                            abort(403, 'محتوای مشکوک شناسایی شد');
                        }
                    }
                }
            });

            $request->merge($input);
        }
    }

    /**
     * اعمال تنظیمات نشست
     */
    protected function applySessionSettings(Request $request): void
    {
        $settings = $this->getCachedSettings('session');

        if (isset($settings['session_lifetime'])) {
            config(['session.lifetime' => (int) $settings['session_lifetime']]);
        }

        if (isset($settings['regenerate_interval'])) {
            $lastRegenerate = session('last_regenerate', 0);
            $interval = (int) $settings['regenerate_interval'] * 60;

            if (time() - $lastRegenerate > $interval) {
                $request->session()->regenerate();
                session(['last_regenerate' => time()]);
            }
        }

        if (isset($settings['max_sessions']) && auth()->check()) {
            $maxSessions = (int) $settings['max_sessions'];
            $activeSessions = Cache::get('user_sessions_'.auth()->id(), 0);

            if ($activeSessions >= $maxSessions && ! session()->has('active_session')) {
                auth()->logout();
                abort(403, 'حداکثر تعداد نشست‌های مجاز');
            }

            if (! session()->has('active_session')) {
                Cache::increment('user_sessions_'.auth()->id());
                session(['active_session' => true]);
            }
        }
    }

    /**
     * اعمال تنظیمات محافظت از فایل
     */
    protected function applyFileProtection(Request $request): void
    {
        $settings = $this->getCachedSettings('file');

        foreach ($request->allFiles() as $files) {
            foreach ((array) $files as $file) {
                // بررسی نوع فایل
                if (isset($settings['allowed_mimes'])) {
                    $allowedMimes = json_decode($settings['allowed_mimes'], true);
                    if (! in_array($file->getMimeType(), $allowedMimes)) {
                        abort(422, 'نوع فایل مجاز نیست');
                    }
                }

                // بررسی حجم فایل
                if (isset($settings['max_file_size'])) {
                    $maxSize = (int) $settings['max_file_size'];
                    if ($file->getSize() > $maxSize) {
                        abort(422, 'حجم فایل بیش از حد مجاز است');
                    }
                }

                // بررسی پسوند خطرناک
                if (isset($settings['dangerous_extensions'])) {
                    $dangerousExts = json_decode($settings['dangerous_extensions'], true);
                    if (in_array(strtolower($file->getClientOriginalExtension()), $dangerousExts)) {
                        abort(422, 'پسوند فایل مجاز نیست');
                    }
                }
            }
        }
    }

    /**
     * اعمال تنظیمات محافظت در برابر حملات
     */
    protected function applyBruteForceProtection(Request $request): void
    {
        if (! $request->is('login', 'admin/login', 'api/login')) {
            return;
        }

        $settings = $this->getCachedSettings('brute_force');
        $ip = $request->ip();
        $key = 'login_attempts_'.$ip;

        // پاکسازی تلاش‌های قدیمی
        if (isset($settings['attempt_cleanup'])) {
            $cleanup = (int) $settings['attempt_cleanup'];
            if (Cache::has($key.'_timestamp')) {
                $lastAttempt = Cache::get($key.'_timestamp');
                if (time() - $lastAttempt > $cleanup) {
                    Cache::forget($key);
                    Cache::forget($key.'_timestamp');
                }
            }
        }

        // بررسی قفل بودن
        if (Cache::has($key.'_locked')) {
            abort(429, 'دسترسی شما موقتاً مسدود شده است');
        }

        // بررسی تعداد تلاش‌ها
        if (isset($settings['max_attempts'])) {
            $attempts = Cache::get($key, 0);
            $maxAttempts = (int) $settings['max_attempts'];

            if ($attempts >= $maxAttempts) {
                $lockoutTime = (int) ($settings['lockout_time'] ?? 1800);
                Cache::put($key.'_locked', true, $lockoutTime);
                abort(429, 'دسترسی شما به دلیل تلاش‌های ناموفق مکرر مسدود شده است');
            }

            Cache::put($key, $attempts + 1, 3600);
            Cache::put($key.'_timestamp', time(), 3600);
        }
    }

    /**
     * دریافت تنظیمات از کش
     */
    protected function getCachedSettings(string $group): array
    {
        return Cache::tags(['security_settings'])->remember(
            'security_settings_'.$group,
            3600,
            fn () => SecuritySetting::where('group', $group)
                ->where('is_active', true)
                ->pluck('value', 'key')
                ->toArray()
        );
    }
}
