<?php

namespace App\Http\Middleware;

use App\Models\Visit;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Schema;
use Symfony\Component\HttpFoundation\Response;

class TrackVisit
{
    protected static ?array $visitColumns = null;

    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);

        if (! $request->isMethod('GET') || $request->ajax() || ! Schema::hasTable('visits')) {
            return $response;
        }

        $route = $request->route();
        $country = $this->detectCountry($request);
        $traffic = $this->detectTraffic($request);

        $payload = [
            'session_id' => $request->session()->getId(),
            'path' => '/' . ltrim($request->path(), '/'),
            'route_name' => $route?->getName(),
            'referer' => $request->headers->get('referer'),
            'referrer_host' => $traffic['referrer_host'],
            'ip_hash' => $request->ip() ? hash('sha256', (string) $request->ip()) : null,
            'device_type' => $this->detectDeviceType((string) $request->userAgent()),
            'user_agent' => $request->userAgent(),
            'country_code' => $country['code'],
            'country_name' => $country['name'],
            'traffic_source' => $traffic['source'],
            'traffic_medium' => $traffic['medium'],
            'traffic_campaign' => $traffic['campaign'],
            'visited_at' => now(),
        ];

        $allowedColumns = array_flip($this->visitColumns());
        $payload = array_intersect_key($payload, $allowedColumns);

        Visit::query()->create($payload);

        return $response;
    }

    protected function visitColumns(): array
    {
        if (self::$visitColumns === null) {
            self::$visitColumns = Schema::getColumnListing('visits');
        }

        return self::$visitColumns;
    }

    protected function detectDeviceType(string $userAgent): string
    {
        $normalized = strtolower($userAgent);

        return match (true) {
            str_contains($normalized, 'tablet'), str_contains($normalized, 'ipad') => 'tablet',
            str_contains($normalized, 'mobile'), str_contains($normalized, 'android') => 'mobile',
            default => 'desktop',
        };
    }

    protected function detectCountry(Request $request): array
    {
        $headerCode = strtoupper((string) (
            $request->headers->get('CF-IPCountry')
            ?: $request->headers->get('CloudFront-Viewer-Country')
            ?: $request->headers->get('X-Appengine-Country')
        ));

        if (preg_match('/^[A-Z]{2}$/', $headerCode) === 1) {
            return [
                'code' => $headerCode,
                'name' => $this->countryName($headerCode),
            ];
        }

        $acceptLanguage = (string) $request->headers->get('Accept-Language', '');

        if (preg_match('/(?:^|,)\s*[a-z]{2,3}[-_](?<region>[A-Z]{2})/i', $acceptLanguage, $matches) === 1) {
            $region = strtoupper((string) ($matches['region'] ?? ''));

            return [
                'code' => $region,
                'name' => $this->countryName($region),
            ];
        }

        return [
            'code' => null,
            'name' => 'Unknown',
        ];
    }

    protected function countryName(string $code): string
    {
        return [
            'AE' => 'United Arab Emirates',
            'AU' => 'Australia',
            'BD' => 'Bangladesh',
            'CA' => 'Canada',
            'DE' => 'Germany',
            'ES' => 'Spain',
            'FR' => 'France',
            'GB' => 'United Kingdom',
            'IN' => 'India',
            'IT' => 'Italy',
            'NL' => 'Netherlands',
            'PK' => 'Pakistan',
            'QA' => 'Qatar',
            'SA' => 'Saudi Arabia',
            'TR' => 'Turkey',
            'US' => 'United States',
        ][$code] ?? $code;
    }

    protected function detectTraffic(Request $request): array
    {
        $referrer = (string) $request->headers->get('referer', '');
        $referrerHost = $this->normalizeHost($referrer);
        $siteHost = $this->normalizeHost($request->getSchemeAndHttpHost());

        $utmSource = trim((string) $request->query('utm_source', ''));
        $utmMedium = trim((string) $request->query('utm_medium', ''));
        $utmCampaign = trim((string) $request->query('utm_campaign', ''));

        if ($utmSource !== '' || $utmMedium !== '' || $utmCampaign !== '') {
            $request->session()->put([
                'traffic_source' => $utmSource ?: 'campaign',
                'traffic_medium' => $utmMedium ?: 'campaign',
                'traffic_campaign' => $utmCampaign ?: null,
            ]);
        }

        $sessionSource = (string) $request->session()->get('traffic_source', '');
        $sessionMedium = (string) $request->session()->get('traffic_medium', '');
        $sessionCampaign = $request->session()->get('traffic_campaign');

        if ($utmSource !== '' || $sessionSource !== '') {
            return [
                'source' => strtolower($utmSource ?: $sessionSource),
                'medium' => strtolower($utmMedium ?: $sessionMedium ?: 'campaign'),
                'campaign' => strtolower((string) ($utmCampaign ?: $sessionCampaign ?: '')),
                'referrer_host' => $referrerHost,
            ];
        }

        if ($referrerHost !== null && $referrerHost !== $siteHost) {
            if ($this->isSearchEngine($referrerHost)) {
                return [
                    'source' => $this->searchEngineName($referrerHost),
                    'medium' => 'organic',
                    'campaign' => null,
                    'referrer_host' => $referrerHost,
                ];
            }

            return [
                'source' => $referrerHost,
                'medium' => 'referral',
                'campaign' => null,
                'referrer_host' => $referrerHost,
            ];
        }

        return [
            'source' => 'direct',
            'medium' => 'direct',
            'campaign' => null,
            'referrer_host' => $referrerHost,
        ];
    }

    protected function normalizeHost(string $url): ?string
    {
        if ($url === '') {
            return null;
        }

        $host = parse_url($url, PHP_URL_HOST);

        return $host ? strtolower((string) $host) : null;
    }

    protected function isSearchEngine(string $host): bool
    {
        return str_contains($host, 'google.')
            || str_contains($host, 'bing.')
            || str_contains($host, 'yahoo.')
            || str_contains($host, 'duckduckgo.')
            || str_contains($host, 'yandex.');
    }

    protected function searchEngineName(string $host): string
    {
        return match (true) {
            str_contains($host, 'google.') => 'google',
            str_contains($host, 'bing.') => 'bing',
            str_contains($host, 'yahoo.') => 'yahoo',
            str_contains($host, 'duckduckgo.') => 'duckduckgo',
            str_contains($host, 'yandex.') => 'yandex',
            default => $host,
        };
    }
}
