<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;

class BotDetector
{
    // Tamamen engelle — 403
    private const BAD_BOTS = [
        'scrapy', 'httrack', 'webcopier', 'webzip', 'teleportpro', 'webstripper',
        'offline explorer', 'larbin', 'libwww-perl',
        'masscan', 'nikto', 'sqlmap', 'nmap', 'zgrab', 'nuclei',
        'dirsearch', 'gobuster', 'feroxbuster', 'ffuf',
        'ahrefsbot', 'semrushbot', 'dotbot', 'mj12bot', 'blexbot',
        'bytespider', 'gptbot', 'claudebot', 'chatgpt-user',
        'ccbot', 'anthropic-ai', 'cohere-ai', 'omgili', 'omgilibot',
        'pinterestbot', 'petalbot', 'proximic', 'mediatoolkitbot',
        'dataforseobot', 'sitechecker', 'seokicks', 'linkfluence',
        'serpstatbot', 'serendeputy', 'riddler', 'netcraftsurveyagent',
        'netsystemsresearch', 'ioncrawl', 'brandverity',
        'wp_is_mobile', 'wordpress', 'wpbot',
    ];

    // Bu IP prefix'leri için tüm kontroller atlanır (güvenilir crawler'lar)
    private const TRUSTED_IP_PREFIXES = [
        '66.249.',  // Googlebot
        '64.233.',  // Google
        '74.125.',  // Google
        '209.85.',  // Google
        '216.239.', // Google
        '34.68.',   // Google Cloud us-central1
        '34.64.',   // Google Cloud
        '35.187.',  // Google Cloud
        '35.190.',  // Google Cloud
    ];

    // İzin ver ama bot olarak işaretle (SEO crawler'lar)
    private const GOOD_BOTS = [
        'googlebot', 'google-inspectiontool', 'google-structured-data-testing-tool',
        'adsbot-google', 'adsbot-google-mobile', 'mediapartners-google',
        'apis-google', 'feedfetcher-google', 'google-adwords-instant',
        'bingbot', 'msnbot', 'adidxbot', 'bingpreview',
        'duckduckbot', 'baiduspider', 'yandexbot', 'yandexmobilebot',
        'slurp', 'teoma', 'ia_archiver',
        'facebot', 'facebookexternalhit',
        'twitterbot', 'linkedinbot', 'whatsapp',
        'applebot', 'discordbot', 'telegrambot',
        'slackbot', 'pingdom',
    ];

    // Rate-limit uygula (dakikada 10'dan fazla → uyarı, 30'dan fazla → otomatik engel)
    private const GENERIC_BOTS = [
        'curl/', 'wget/', 'python-requests', 'python-urllib',
        'go-http-client', 'java/', 'okhttp/', 'libcurl',
        'node-fetch', 'node.js', 'axios', 'got/',
        'apache-httpclient', 'php/', 'perl/', 'ruby',
        'postman', 'insomnia', 'httpie',
    ];

    // Bu path'ler için sadece log tut, engelleme yapma
    private const SKIP_PATHS = [
        '/up', '/api/', '/sitemap',
    ];

    public function handle(Request $request, Closure $next)
    {
        $ip = $request->ip();
        $ua = strtolower($request->userAgent() ?? '');
        $path = $request->path();

        // Skip paths
        foreach (self::SKIP_PATHS as $skip) {
            if (str_starts_with('/' . $path, $skip)) {
                return $next($request);
            }
        }

        // Güvenilir IP (Google vb.) — tüm kontrolleri atla
        foreach (self::TRUSTED_IP_PREFIXES as $prefix) {
            if (str_starts_with($ip, $prefix)) {
                return $next($request);
            }
        }

        // Manuel engelli IP kontrolü
        if ($this->isBlockedIp($ip)) {
            $this->logBot($ip, $request->userAgent(), '/' . $path, $request->method(), 'ip_blocked', 'blocked_ip');
            return response('Erişim engellendi.', 403);
        }

        // UA boşsa bot olarak işaretle
        if (empty($ua)) {
            $request->attributes->set('is_bot', true);
            $request->attributes->set('bot_type', 'noua');
            $this->logBot($ip, '', '/' . $path, $request->method(), 'allowed', 'no_ua');
            return $next($request);
        }

        // Kötü bot mu?
        foreach (self::BAD_BOTS as $pattern) {
            if (str_contains($ua, $pattern)) {
                $this->logBot($ip, $request->userAgent(), '/' . $path, $request->method(), 'blocked', $pattern);
                return response('', 403);
            }
        }

        // İyi bot mu?
        foreach (self::GOOD_BOTS as $pattern) {
            if (str_contains($ua, $pattern)) {
                $request->attributes->set('is_bot', true);
                $request->attributes->set('bot_type', 'good');
                // İyi botlar için çok agresif rate limit (dakikada 60)
                if ($this->isRateLimited($ip, 60, 'good_bot')) {
                    return response('', 429);
                }
                return $next($request);
            }
        }

        // Generic araç mı?
        foreach (self::GENERIC_BOTS as $pattern) {
            if (str_contains($ua, $pattern)) {
                $request->attributes->set('is_bot', true);
                $request->attributes->set('bot_type', 'generic');
                if ($this->isRateLimited($ip, 10, 'generic')) {
                    $this->logBot($ip, $request->userAgent(), '/' . $path, $request->method(), 'rate_limited', $pattern);
                    // 30+ istek → otomatik engelle
                    $count = Cache::get("bot_count_{$ip}", 0);
                    if ($count > 30) {
                        $this->autoBlock($ip, 'Otomatik: dakikada 30+ generic bot isteği');
                    }
                    return response('', 429);
                }
                $this->logBot($ip, $request->userAgent(), '/' . $path, $request->method(), 'allowed', $pattern);
                return $next($request);
            }
        }

        // Normal kullanıcı — genel rate limit (dakikada 120 istek)
        if ($this->isRateLimited($ip, 120, 'human')) {
            $this->logBot($ip, $request->userAgent(), '/' . $path, $request->method(), 'rate_limited', 'human_flood');
            $count = Cache::get("bot_count_{$ip}", 0);
            if ($count > 200) {
                $this->autoBlock($ip, 'Otomatik: dakikada 200+ istek flood');
            }
            return response('', 429);
        }

        $request->attributes->set('is_bot', false);
        return $next($request);
    }

    private function isBlockedIp(string $ip): bool
    {
        return Cache::remember("blocked_ip_{$ip}", 300, function () use ($ip) {
            try {
                return DB::table('blocked_ips')
                    ->where('ip', $ip)
                    ->where(function ($q) {
                        $q->whereNull('expires_at')->orWhere('expires_at', '>', now());
                    })
                    ->exists();
            } catch (\Exception) {
                return false;
            }
        });
    }

    private function isRateLimited(string $ip, int $maxPerMinute, string $type): bool
    {
        $key   = "rl_{$type}_{$ip}";
        $count = Cache::get($key, 0);

        if ($count === 0) {
            Cache::put($key, 1, 60);
        } else {
            Cache::increment($key);
        }

        // Bot count ayrı izle
        Cache::put("bot_count_{$ip}", Cache::get("bot_count_{$ip}", 0) + 1, 60);

        return $count >= $maxPerMinute;
    }

    private function autoBlock(string $ip, string $reason): void
    {
        try {
            DB::table('blocked_ips')->insertOrIgnore([
                'ip'           => $ip,
                'reason'       => $reason,
                'auto_blocked' => 1,
                'blocked_at'   => now(),
                'expires_at'   => now()->addHours(24),
            ]);
            Cache::forget("blocked_ip_{$ip}");
        } catch (\Exception) {}
    }

    private function logBot(string $ip, ?string $ua, string $path, string $method, string $action, string $botName): void
    {
        try {
            DB::table('analytics_bot_logs')->insert([
                'ip'         => $ip,
                'user_agent' => mb_substr($ua ?? '', 0, 500),
                'path'       => mb_substr($path, 0, 500),
                'method'     => $method,
                'action'     => $action,
                'bot_name'   => mb_substr($botName, 0, 100),
                'created_at' => now(),
            ]);
        } catch (\Exception) {}
    }
}
