<?php

declare(strict_types=1);

namespace App\Mcp\Security;

use App\Entity\Profile;
use App\Repository\ProfileRepository;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\RateLimiter\RateLimiterFactory;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;

final class McpHeaderAuthenticator extends AbstractAuthenticator
{
    public function __construct(
        private readonly ProfileRepository $profiles,
        private readonly UserPasswordHasherInterface $passwordHasher,
        private readonly RateLimiterFactory $mcpAuthLimiter,
        private readonly LoggerInterface $logger,
    ) {}

    public function supports(Request $request): ?bool
    {
        if (!$request->headers->has('X-Profile-Id') || !$request->headers->has('X-Profile-Password')) {
            return false;
        }

        return true;
    }

    public function authenticate(Request $request): Passport
    {
        $profileId = $request->headers->get('X-Profile-Id', '');
        $password  = $request->headers->get('X-Profile-Password', '');

        $limiter = $this->mcpAuthLimiter->create($request->getClientIp() ?? 'unknown');
        $limit   = $limiter->consume(1);

        if (!$limit->isAccepted()) {
            $this->logger->warning('MCP auth rate limited', ['ip' => $request->getClientIp()]);

            throw new CustomUserMessageAuthenticationException('Rate limited: too many authentication attempts.');
        }

        return new SelfValidatingPassport(
            new UserBadge($profileId, function (string $id) use ($password, $limiter, $request): Profile {
                $profile = $this->profiles->find($id);

                if (!$profile || !$profile->isActive()) {
                    $this->logger->warning('MCP auth failed: profile not found', ['profileId' => $id]);

                    throw new CustomUserMessageAuthenticationException('Authentication failed: invalid credentials.');
                }

                if (!$this->passwordHasher->isPasswordValid($profile, $password)) {
                    $this->logger->warning('MCP auth failed: invalid password', ['profileId' => $id]);

                    throw new CustomUserMessageAuthenticationException('Authentication failed: invalid credentials.');
                }

                $limiter->reset();

                $this->logger->info('MCP auth success', [
                    'profileId' => $id,
                    'roles'     => $profile->getRoles(),
                    'ip'        => $request->getClientIp(),
                ]);

                return $profile;
            })
        );
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
    {
        return null;
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
    {
        $statusCode = str_contains($exception->getMessageKey(), 'Rate limited')
            ? Response::HTTP_TOO_MANY_REQUESTS
            : Response::HTTP_UNAUTHORIZED;

        return new JsonResponse(
            ['message' => $exception->getMessageKey()],
            $statusCode,
        );
    }
}
