<?php

namespace NoahVet\Reef\Infrastructure\Security\Symfony\Voter;

use NoahVet\Reef\Domain\Security\Model\Resource;
use NoahVet\Reef\Domain\Security\Model\ResourceType;
use NoahVet\Reef\Domain\Security\Port\PermissionCheckerInterface;
use NoahVet\Reef\Infrastructure\Security\IAM\Mapper\IAMResourceMapperInterface;
use NoahVet\Reef\Security\Authentication\Token\ReefOAuthToken;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;

class IAMVoter implements IAMVoterInterface
{
    public function __construct(
        private readonly PermissionCheckerInterface $checker,
        private readonly IAMResourceMapperInterface $mapper,
    ) {
    }

    #[\Override]
    public function vote(TokenInterface $token, mixed $subject, array $attributes): int
    {
        $bearer = $this->extractBearer($token);

        if (null === $bearer) {
            return self::ACCESS_ABSTAIN;
        }

        if (1 === \count($attributes) && \str_starts_with($attributes[0], 'reef:iam:principals:')) {
            return $this->checker->checkGroup($attributes[0], $bearer)
                ? self::ACCESS_GRANTED
                : self::ACCESS_DENIED;
        }

        if ($subject instanceof ResourceType) {
            return $this->checker->check($subject, $attributes, $bearer)
                ? self::ACCESS_GRANTED
                : self::ACCESS_DENIED;
        }

        $resource = $subject instanceof Resource ? $subject : $this->mapper->map($subject);

        if (null === $resource) {
            return self::ACCESS_ABSTAIN;
        }

        return $this->checker->check($resource, $attributes, $bearer)
            ? self::ACCESS_GRANTED
            : self::ACCESS_DENIED;
    }

    private function extractBearer(TokenInterface $token): ?string
    {
        if ($token instanceof ReefOAuthToken) {
            return $token->getBearerToken();
        }

        if (\class_exists(\HWI\Bundle\OAuthBundle\Security\Core\Authentication\Token\OAuthToken::class)
            && $token instanceof \HWI\Bundle\OAuthBundle\Security\Core\Authentication\Token\OAuthToken
        ) {
            return $token->getAccessToken();
        }

        return null;
    }
}
