<?php

declare(strict_types=1);

namespace App\Administration\Infrastructure\Security;

use App\Administration\Application\Port\GeoLocationService;
use App\Administration\Application\Service\RefreshTokenManager;
use App\Administration\Domain\Event\ConnexionReussie;
use App\Administration\Domain\Model\RefreshToken\DeviceFingerprint;
use App\Administration\Domain\Model\Session\DeviceInfo;
use App\Administration\Domain\Model\Session\Session;
use App\Administration\Domain\Model\User\UserId;
use App\Administration\Domain\Repository\SessionRepository;
use App\Shared\Domain\Clock;
use App\Shared\Domain\Tenant\TenantId;
use App\Shared\Infrastructure\RateLimit\LoginRateLimiterInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationSuccessEvent;
use Ramsey\Uuid\Uuid;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Messenger\MessageBusInterface;

/**
 * Handles post-login success actions: refresh token, session, reset rate limit, audit.
 *
 * @see Story 1.4 - T5: Backend Login Endpoint
 * @see Story 1.6 - Session management
 */
final readonly class LoginSuccessHandler
{
    public function __construct(
        private RefreshTokenManager $refreshTokenManager,
        private SessionRepository $sessionRepository,
        private GeoLocationService $geoLocationService,
        private LoginRateLimiterInterface $rateLimiter,
        private MessageBusInterface $eventBus,
        private Clock $clock,
        private RequestStack $requestStack,
    ) {
    }

    public function onAuthenticationSuccess(AuthenticationSuccessEvent $event): void
    {
        $user = $event->getUser();
        $response = $event->getResponse();
        $request = $this->requestStack->getCurrentRequest();

        if (!$user instanceof SecurityUser || $request === null) {
            return;
        }

        $email = $user->email();
        $userId = UserId::fromString($user->userId());
        $tenantId = TenantId::fromString($user->tenantId());
        $ipAddress = $request->getClientIp() ?? 'unknown';
        $userAgent = $request->headers->get('User-Agent', 'unknown');

        // Create the device fingerprint
        $fingerprint = DeviceFingerprint::fromRequest($userAgent, $ipAddress);

        // Detect if this is a mobile device (for refresh token TTL)
        $isMobile = str_contains(strtolower($userAgent), 'mobile');

        // Create the refresh token
        $refreshToken = $this->refreshTokenManager->create(
            $userId,
            $tenantId,
            $fingerprint,
            $isMobile,
        );

        // Create the session with metadata
        $deviceInfo = DeviceInfo::fromUserAgent($userAgent);
        $location = $this->geoLocationService->locate($ipAddress);
        $now = $this->clock->now();

        $session = Session::create(
            familyId: $refreshToken->familyId,
            userId: $userId,
            tenantId: $tenantId,
            deviceInfo: $deviceInfo,
            location: $location,
            createdAt: $now,
        );

        // Calculate TTL (same as refresh token)
        $ttlSeconds = $refreshToken->expiresAt->getTimestamp() - $now->getTimestamp();
        $this->sessionRepository->save($session, $ttlSeconds);

        // Add the refresh token as HttpOnly cookie
        // Path is /api to allow session identification on /api/me/sessions endpoints
        // Secure flag only on HTTPS (prod), not HTTP (dev)
        $isSecure = $request->isSecure();
        $cookie = Cookie::create('refresh_token')
            ->withValue($refreshToken->toTokenString())
            ->withExpires($refreshToken->expiresAt)
            ->withPath('/api')
            ->withSecure($isSecure)
            ->withHttpOnly(true)
            ->withSameSite($isSecure ? 'strict' : 'lax');

        $response->headers->setCookie($cookie);

        // Session ID cookie for active role scoping (per-device isolation)
        $sessionIdCookie = Cookie::create('classeo_sid')
            ->withValue(Uuid::uuid4()->toString())
            ->withExpires($refreshToken->expiresAt)
            ->withPath('/api')
            ->withSecure($isSecure)
            ->withHttpOnly(true)
            ->withSameSite($isSecure ? 'strict' : 'lax');
        $response->headers->setCookie($sessionIdCookie);

        // Reset the rate limiter for this email
        $this->rateLimiter->reset($email);

        // Dispatch the successful login event
        $this->eventBus->dispatch(new ConnexionReussie(
            userId: $user->userId(),
            email: $email,
            tenantId: $tenantId,
            ipAddress: $ipAddress,
            userAgent: $userAgent,
            occurredOn: $this->clock->now(),
        ));
    }
}
