<?php

declare(strict_types=1);

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Session\Middleware\StartSession;
use Symfony\Component\HttpFoundation\Response;

class ConditionalStartSession extends StartSession
{
    public function handle($request, Closure $next): mixed
    {
        if (! $request instanceof Request || ! config('skinbase-sessions.enabled', true)) {
            return parent::handle($request, $next);
        }

        if ($this->shouldSkipSession($request)) {
            $request->attributes->set('skinbase.session_skipped', true);

            $response = $next($request);

            if ($response instanceof Response && config('skinbase-sessions.debug_header', false)) {
                $response->headers->set('X-Skinbase-Session', 'skipped');
            }

            return $response;
        }

        $request->attributes->set('skinbase.session_skipped', false);

        $response = parent::handle($request, $next);

        if ($response instanceof Response && config('skinbase-sessions.debug_header', false)) {
            $response->headers->set('X-Skinbase-Session', 'started');
        }

        return $response;
    }

    protected function shouldSkipSession(Request $request): bool
    {
        if (! $this->isSafeReadMethod($request)) {
            return false;
        }

        if ($this->hasExistingSessionCookie($request)) {
            return false;
        }

        if ($this->matchesAnyPath($request, config('skinbase-sessions.always_session_paths', []))) {
            return false;
        }

        if (! $this->matchesAnyPath($request, config('skinbase-sessions.public_paths', []))) {
            return false;
        }

        if (config('skinbase-sessions.skip_anonymous_public_get', true)) {
            return true;
        }

        return config('skinbase-sessions.skip_known_crawlers_on_public_get', true)
            && $this->isKnownCrawler($request);
    }

    protected function isSafeReadMethod(Request $request): bool
    {
        return in_array($request->getMethod(), ['GET', 'HEAD'], true);
    }

    protected function hasExistingSessionCookie(Request $request): bool
    {
        $cookieName = config('session.cookie');

        return is_string($cookieName)
            && $cookieName !== ''
            && $request->cookies->has($cookieName);
    }

    protected function matchesAnyPath(Request $request, array $patterns): bool
    {
        foreach ($patterns as $pattern) {
            if (! is_string($pattern) || $pattern === '') {
                continue;
            }

            if ($pattern === '/' && $request->path() === '/') {
                return true;
            }

            $normalizedPattern = trim($pattern, '/');

            if ($normalizedPattern !== '' && $request->is($normalizedPattern)) {
                return true;
            }
        }

        return false;
    }

    protected function isKnownCrawler(Request $request): bool
    {
        $userAgent = strtolower((string) $request->userAgent());

        if ($userAgent === '') {
            return false;
        }

        foreach (config('skinbase-sessions.bot_user_agent_keywords', []) as $keyword) {
            $normalizedKeyword = strtolower((string) $keyword);

            if ($normalizedKeyword !== '' && str_contains($userAgent, $normalizedKeyword)) {
                return true;
            }
        }

        return false;
    }
}