<?php

declare(strict_types=1);

namespace NoahVet\Reef\Phpunit\Security;

use NoahVet\Reef\Phpunit\Fixture\FixtureRepository;
use NoahVet\Reef\Security\Authentication\Token\ReefOAuthToken;
use NoahVet\Reef\Security\ReefOAuthAuthenticatorInterface;
use NoahVet\Reef\Security\User\ReefOAuthUser;
use Symfony\Component\HttpFoundation\JsonResponse;
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;

class MockedReefOAuthAuthenticator extends AbstractAuthenticator implements ReefOAuthAuthenticatorInterface
{
    public function __construct(
        protected FixtureRepository $fixtureRepository,
    ) {
    }

    public function supports(Request $request): ?bool
    {
        return $request->headers->has('Authorization')
            && \str_starts_with($request->headers->get('Authorization') ?? '', 'Bearer ');
    }

    public function authenticate(Request $request): Passport
    {
        $oauthToken = \trim(\substr($request->headers->get('Authorization') ?? '', 7));

        return new SelfValidatingPassport(
            new UserBadge($oauthToken, [$this, 'loadUser']),
        );
    }

    public function createToken(Passport $passport, string $firewallName): TokenInterface
    {
        /** @var UserBadge $userBadge */
        $userBadge = $passport->getBadge(UserBadge::class);
        /** @var ReefOAuthUser $user */
        $user = $userBadge->getUser();

        return new ReefOAuthToken($firewallName, $userBadge->getUserIdentifier(), $user, new \DateTimeImmutable('+1 hour'));
    }

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

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
    {
        if (\str_starts_with($request->headers->get('Authorization') ?? '', 'Bearer ')) {
            return new JsonResponse(['error' => 'Invalid bearer token provided'], Response::HTTP_FORBIDDEN);
        } else {
            return new JsonResponse(['error' => 'No bearer token provided'], Response::HTTP_UNAUTHORIZED);
        }
    }

    public function loadUser(string $bearerToken): ?ReefOAuthUser
    {
        return $this->fixtureRepository->findByOAuthToken($bearerToken)?->getUser();
    }
}
