<?php

namespace App\Security;

use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
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\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;

final class ParentJsonLoginAuthenticator extends AbstractAuthenticator
{
    public function __construct(
        private readonly RateLimiterFactory $parentLoginLimiter,
        private readonly ParentLoginSuccessHandler $successHandler,
    ) {
    }

    public function supports(Request $request): ?bool
    {
        return '/api/v1/parent/login' === $request->getPathInfo() && $request->isMethod('POST');
    }

    public function authenticate(Request $request): Passport
    {
        try {
            $payload = $request->toArray();
        } catch (\Throwable) {
            throw new CustomUserMessageAuthenticationException('Identifiants invalides.');
        }

        $email = mb_strtolower(trim((string) ($payload['email'] ?? '')));
        $password = (string) ($payload['password'] ?? '');
        $csrfToken = (string) ($payload['csrfToken'] ?? '');
        $key = hash('sha256', ($request->getClientIp() ?? 'unknown').'|'.$email);
        $limit = $this->parentLoginLimiter->create($key)->consume();

        if (!$limit->isAccepted()) {
            $request->attributes->set('_login_rate_limited', true);
            throw new CustomUserMessageAuthenticationException('Trop de tentatives.');
        }

        if (false === filter_var($email, FILTER_VALIDATE_EMAIL) || '' === $password || '' === $csrfToken) {
            throw new CustomUserMessageAuthenticationException('Identifiants invalides.');
        }

        return new Passport(
            new UserBadge($email),
            new PasswordCredentials($password),
            [new CsrfTokenBadge('authenticate', $csrfToken)],
        );
    }

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

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
    {
        $status = true === $request->attributes->get('_login_rate_limited') ? 429 : 401;

        return new JsonResponse(['error' => 429 === $status ? 'login_rate_limited' : 'invalid_credentials'], $status);
    }
}
