<?php

declare(strict_types=1);

namespace Plugin\tnt_mcp\src\Mcp\OAuth;

use JTL\DB\DbInterface;
use Plugin\tnt_mcp\src\Mcp\Auth\TokenGuard;
use Psr\Http\Message\ServerRequestInterface;
use Throwable;

/**
 * Bridges an OAuth 2.1 bearer (a league-issued, signed access-token JWT) to the plugin's existing
 * {@see TokenGuard} scope model, so the MCP endpoint enforces scopes identically no matter whether
 * the caller used OAuth or a legacy static token. Returns null on any failure (bad signature,
 * expired, revoked, unknown id) — deny-by-default; the endpoint then answers 401 + challenge.
 */
final class OAuthTokenValidator
{
    public function __construct(
        private readonly DbInterface $db,
        private readonly KeyManager $keys,
    ) {
    }

    public static function default(DbInterface $db): self
    {
        return new self($db, KeyManager::default($db));
    }

    /**
     * @return TokenGuard|null a guard carrying the token's scopes, or null if the bearer is not a
     *                         valid OAuth access token
     */
    public function validate(ServerRequestInterface $request): ?TokenGuard
    {
        try {
            $validated = (new OAuthServerFactory($this->db, $this->keys))
                ->resourceServer()
                ->validateAuthenticatedRequest($request);
        } catch (Throwable) {
            return null;
        }

        $scopesAttr = $validated->getAttribute('oauth_scopes');
        $scopes     = [];
        if (\is_array($scopesAttr)) {
            foreach ($scopesAttr as $scope) {
                if (\is_string($scope) && $scope !== '') {
                    $scopes[] = $scope;
                }
            }
        }

        $tokenId = $validated->getAttribute('oauth_access_token_id');
        $label   = \is_string($tokenId) && $tokenId !== '' ? 'oauth_' . substr($tokenId, 0, 12) : 'oauth';

        return new TokenGuard($scopes, $label);
    }
}
