<?php

declare(strict_types=1);

namespace App\Administration\Infrastructure\Api\Controller;

use App\Administration\Domain\Event\Deconnexion;
use App\Administration\Domain\Model\RefreshToken\RefreshToken;
use App\Administration\Domain\Repository\RefreshTokenRepository;
use App\Administration\Domain\Repository\SessionRepository;
use App\Shared\Domain\Clock;
use DateTimeImmutable;
use InvalidArgumentException;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Routing\Attribute\Route;

/**
 * Logout endpoint.
 *
 * Invalidates the refresh token, deletes the session, and clears the cookie.
 *
 * @see Story 1.4 - User login
 * @see Story 1.6 - Session management
 */
final readonly class LogoutController
{
    public function __construct(
        private RefreshTokenRepository $refreshTokenRepository,
        private SessionRepository $sessionRepository,
        private MessageBusInterface $eventBus,
        private Clock $clock,
    ) {
    }

    #[Route('/api/token/logout', name: 'api_logout', methods: ['POST'])]
    public function __invoke(Request $request): Response
    {
        $refreshTokenValue = $request->cookies->get('refresh_token');

        // Invalidate the entire token family for a complete logout
        if ($refreshTokenValue !== null) {
            try {
                $tokenId = RefreshToken::extractIdFromTokenString($refreshTokenValue);
                $refreshToken = $this->refreshTokenRepository->find($tokenId);

                if ($refreshToken !== null) {
                    // Delete the session
                    $this->sessionRepository->delete($refreshToken->familyId);

                    // Invalidate the entire family (disconnects all devices)
                    $this->refreshTokenRepository->invalidateFamily($refreshToken->familyId);

                    // Dispatch logout event
                    $this->eventBus->dispatch(new Deconnexion(
                        userId: (string) $refreshToken->userId,
                        familyId: (string) $refreshToken->familyId,
                        tenantId: $refreshToken->tenantId,
                        ipAddress: $request->getClientIp() ?? 'unknown',
                        userAgent: $request->headers->get('User-Agent', 'unknown'),
                        occurredOn: $this->clock->now(),
                    ));
                }
            } catch (InvalidArgumentException) {
                // Malformed token, ignore
            }
        }

        // Create the response with cookie deletion
        $response = new JsonResponse(['message' => 'Logout successful'], Response::HTTP_OK);

        // Delete the refresh_token cookie
        // Secure flag only on HTTPS (prod), not HTTP (dev)
        $isSecure = $request->isSecure();

        // Clear cookie at current path /api
        $response->headers->setCookie(
            Cookie::create('refresh_token')
                ->withValue('')
                ->withExpires(new DateTimeImmutable('-1 hour'))
                ->withPath('/api')
                ->withHttpOnly(true)
                ->withSecure($isSecure)
                ->withSameSite($isSecure ? 'strict' : 'lax'),
        );

        // Also clear legacy cookie at /api/token (migration period)
        $response->headers->setCookie(
            Cookie::create('refresh_token')
                ->withValue('')
                ->withExpires(new DateTimeImmutable('-1 hour'))
                ->withPath('/api/token')
                ->withHttpOnly(true)
                ->withSecure($isSecure)
                ->withSameSite($isSecure ? 'strict' : 'lax'),
        );

        // Clear session ID cookie (active role scoping)
        $response->headers->setCookie(
            Cookie::create('classeo_sid')
                ->withValue('')
                ->withExpires(new DateTimeImmutable('-1 hour'))
                ->withPath('/api')
                ->withHttpOnly(true)
                ->withSecure($isSecure)
                ->withSameSite($isSecure ? 'strict' : 'lax'),
        );

        return $response;
    }
}
