<?php declare(strict_types=1);

namespace App\Engine\Admin\Auth;

final readonly class WebauthnCredentialAdministratorUserEntityRepository implements
    \Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepositoryInterface,
    \Webauthn\Bundle\Repository\CanRegisterUserEntity,
    \Webauthn\Bundle\Repository\CanGenerateUserEntity
{

    public function __construct(
        private \App\Engine\AdministratorUser\AdministratorUserRepository $administratorUserRepository,
        private \Doctrine\ORM\EntityManagerInterface                      $entityManager,
    ) {
    }

    public function saveUserEntity(\Webauthn\PublicKeyCredentialUserEntity $userEntity): void
    {
        $user = $this->administratorUserRepository->findOneBy(['uuid' => $userEntity->id]);

        if ($user === null) {
            $user = (new \App\Engine\AdministratorUser\AdministratorUser())
                ->setEmail($userEntity->name)
                ->setRoles([\App\Engine\Security\Role::ADMINISTRATOR->value]);
        }

        $this->entityManager->persist($user);
        $this->entityManager->flush();
    }

    public function findOneByUsername(string $username): ?\Webauthn\PublicKeyCredentialUserEntity
    {
        $user = $this->administratorUserRepository->findOneBy(['email' => $username]);
        return $this->getUserEntity($user);
    }

    private function getUserEntity(null|\App\Engine\AdministratorUser\AdministratorUser $administratorUser): ?\Webauthn\PublicKeyCredentialUserEntity
    {
        if ($administratorUser === null) {
            return null;
        }

        return new \Webauthn\PublicKeyCredentialUserEntity(
            name: $administratorUser->getUserIdentifier(),
            id: $administratorUser->getUuid(),
            displayName: $this->formatDisplayName(displayName: $administratorUser->getDisplayName(), userEntityId: $administratorUser->getUuid()),
            icon: null
        );
    }

    private function formatDisplayName(string $displayName, string $userEntityId): string
    {
        return "$displayName ({$this::provideClientHashFromUserEntityId($userEntityId)})";
    }

    public static function provideClientHashFromUserEntityId(string $userEntityId): string
    {
        return substr(md5($userEntityId), 0, 10);
    }

    public function findOneByUserHandle(string $userHandle): ?\Webauthn\PublicKeyCredentialUserEntity
    {
        $user = $this->administratorUserRepository->findOneBy(['uuid' => $userHandle]);
        return $this->getUserEntity($user);
    }

    public function generateUserEntity(?string $username, ?string $displayName): \Webauthn\PublicKeyCredentialUserEntity
    {
        $randomUserData = \ParagonIE\ConstantTime\Base64UrlSafe::encodeUnpadded(random_bytes(32));

        return \Webauthn\PublicKeyCredentialUserEntity::create(
            name: $username ?? $randomUserData,
            id: $userEntityId = $this->generateNextUserEntityId(),
            displayName: $this->formatDisplayName(displayName: $displayName ?? $username ?? $randomUserData, userEntityId: $userEntityId),
        );
    }

    public function generateNextUserEntityId(): string
    {
        return \Symfony\Component\Uid\UuidV7::generate();
    }

}
