<?php

namespace App\WebAuthn;

use App\WebAuthn\Repository\PublicKeyCredentialSourceRepositoryImpl;
use Cose\Algorithm\Manager;
use Cose\Algorithm\Signature\ECDSA\ES256;
use Cose\Algorithm\Signature\RSA\RS256;
use Cose\Algorithms;
use Webauthn\AttestationStatement\AttestationObjectLoader;
use Webauthn\AttestationStatement\AttestationStatementSupportManager;
use Webauthn\AttestationStatement\NoneAttestationStatementSupport;
use Webauthn\AuthenticationExtensions\ExtensionOutputCheckerHandler;
use Webauthn\AuthenticatorAssertionResponseValidator;
use Webauthn\AuthenticatorAttestationResponseValidator;
use Webauthn\AuthenticatorSelectionCriteria;
use Webauthn\PublicKeyCredentialCreationOptions;
use Webauthn\PublicKeyCredentialLoader;
use Webauthn\PublicKeyCredentialParameters;
use Webauthn\PublicKeyCredentialRpEntity;
use Webauthn\PublicKeyCredentialSourceRepository;
use Webauthn\PublicKeyCredentialUserEntity;
use Webauthn\TokenBinding\IgnoreTokenBindingHandler;

class WebAuthnService
{
    private static $rpName = "开心鄢的录播查询小站";
    private static $rpId = "comment.sc.jerryyan.top";
    private static $timeout = 45000;
    private static $publicKeyCredentialSourceRepositoryInstance = null;
    private static $authenticatorAssertionResponseValidator = null;
    private static $attestationStatementSupportManager = null;
    public static function createRequestOptions(PublicKeyCredentialUserEntity $userEntity, string $challenge): PublicKeyCredentialCreationOptions
    {
        $publicKeyCredentialParametersList = [
            new PublicKeyCredentialParameters("public-key", Algorithms::COSE_ALGORITHM_ES256),
            new PublicKeyCredentialParameters("public-key", Algorithms::COSE_ALGORITHM_RS256),
        ];
        return new PublicKeyCredentialCreationOptions(
            static::getRpEntity(),
            $userEntity,
            $challenge,
            $publicKeyCredentialParametersList,
            static::$timeout
        );
    }

    public static function getPublicKeyCredentialSourceRepository(): PublicKeyCredentialSourceRepository
    {
        if (static::$publicKeyCredentialSourceRepositoryInstance === null) {
            static::$publicKeyCredentialSourceRepositoryInstance = new PublicKeyCredentialSourceRepositoryImpl();
        }
        return static::$publicKeyCredentialSourceRepositoryInstance;
    }

    public static function getAuthenticatorAssertionResponseValidator(): AuthenticatorAssertionResponseValidator
    {
        if (static::$authenticatorAssertionResponseValidator === null) {
            $algorithmManager = new Manager();
            $algorithmManager->add(new ES256());
            $algorithmManager->add(new RS256());
            static::$authenticatorAssertionResponseValidator =  new AuthenticatorAssertionResponseValidator(
                static::getPublicKeyCredentialSourceRepository(),
                new IgnoreTokenBindingHandler(),
                new ExtensionOutputCheckerHandler(),
                $algorithmManager
            );
        }
        return static::$authenticatorAssertionResponseValidator;
    }

    public static function getAuthenticatorAttestationResponseValidator(): AuthenticatorAttestationResponseValidator
    {
        return new AuthenticatorAttestationResponseValidator(
            static::getAttestationStatementSupportManager(),
            static::getPublicKeyCredentialSourceRepository(),
            new IgnoreTokenBindingHandler(),
            new ExtensionOutputCheckerHandler()
        );
    }

    public static function getPublicKeyCredentialLoader(): PublicKeyCredentialLoader
    {
        return new PublicKeyCredentialLoader(
            static::getAttestationObjectLoader()
        );
    }

    public static function getAttestationObjectLoader(): AttestationObjectLoader
    {
        return new AttestationObjectLoader(static::getAttestationStatementSupportManager());
    }

    private static function getAttestationStatementSupportManager(): AttestationStatementSupportManager
    {
        if (static::$attestationStatementSupportManager === null) {
            static::$attestationStatementSupportManager = new AttestationStatementSupportManager();
            static::$attestationStatementSupportManager->add(new NoneAttestationStatementSupport());
        }
        return static::$attestationStatementSupportManager;
    }

    private static function getRpEntity(): PublicKeyCredentialRpEntity
    {
        return new PublicKeyCredentialRpEntity(
            static::$rpName,
            static::$rpId
        );
    }
}