<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\DocStoreBundle\Security\Authenticator;

use Chill\DocStoreBundle\Security\Authenticator\DavOnUrlTokenExtractor;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\InvalidTokenException;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTDecodeFailureException;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\MissingTokenException;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
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;

/**
 * JWT authenticator for DAV URL endpoints.
 */
class JWTOnDavUrlAuthenticator extends AbstractAuthenticator
{
    public function __construct(
        private readonly JWTTokenManagerInterface $jwtManager,
        private readonly DavOnUrlTokenExtractor $davOnUrlTokenExtractor,
    ) {
    }

    public function supports(Request $request): ?bool
    {
        return false !== $this->davOnUrlTokenExtractor->extract($request);
    }

    public function authenticate(Request $request): Passport
    {
        $token = $this->davOnUrlTokenExtractor->extract($request);

        if (false === $token) {
            throw new MissingTokenException('JWT Token not found');
        }

        try {
            $payload = $this->jwtManager->parse($token);
        } catch (JWTDecodeFailureException $e) {
            throw new InvalidTokenException('Invalid JWT Token', 0, $e);
        }

        $username = $payload['username'] ?? $payload['email'] ?? $payload['user_id'] ?? null;

        if (null === $username) {
            throw new InvalidTokenException('Invalid JWT Token: Username not found');
        }

        return new SelfValidatingPassport(new UserBadge($username));
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
    {
        // On success, let the request continue
        return null;
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
    {
        throw $exception;
    }
}
