<?php

namespace Drupal\miniorange_webauthn\MoRepo;

use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\miniorange_webauthn\Entity\MoWebauthnCredential;
use Drupal\user\Entity\User;
use ParagonIE\ConstantTime\Base64UrlSafe;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\Component\Uid\Uuid;
use Webauthn\PublicKeyCredentialSource;
use Webauthn\PublicKeyCredentialUserEntity;

class MoPubKeyCredSourceRepo implements MoPubKeyCredSourceRepoInterface
{
  /**
   * @throws InvalidPluginDefinitionException
   * @throws PluginNotFoundException
   */
  public function findAllForUserEntity(PublicKeyCredentialUserEntity $pubKeyCredUser): array
  {
    $credRepo = \Drupal::entityTypeManager()->getStorage(MoWebauthnCredential::ENTITY_ID);
    $credentials = $credRepo->loadByProperties([
      MoWebauthnCredential::UID => Base64UrlSafe::decode($pubKeyCredUser->id),
    ]);
    foreach ($credentials as $credential) {
      $new[] = $this->generatePublicKeyCredentialSource($credential);
    }
    return $new ?? [];
  }

  public function findByPublicKeyCredentialId(string $publicKeyCredentialId): mixed
  {
    $credRepo = \Drupal::entityTypeManager()->getStorage(MoWebauthnCredential::ENTITY_ID);
    $resp = $credRepo->loadByProperties([MoWebauthnCredential::CREDENTIAL_ID => $publicKeyCredentialId]);
    return current($resp);
  }
  public function checkDuplicate(string $publicKeyCredentialId)
  {
    return !empty($this->findByPublicKeyCredentialId($publicKeyCredentialId));
  }
  /**
   * @throws EntityStorageException
   */
  private function saveCredentials(MoWebauthnCredential $moWebauthnCredential): void
  {
    $moWebauthnCredential->save();
  }

  public function createCredentials(PublicKeyCredentialSource $publicKeyCredentialSource, string $label): void
  {

    $credentials = [
      'publicKeyCredentialId' => $publicKeyCredentialSource->publicKeyCredentialId,
      'type' => $publicKeyCredentialSource->type,
      'transports' => $publicKeyCredentialSource->transports,
      'attestationType' => $publicKeyCredentialSource->attestationType,
      'trustPath' => get_class($publicKeyCredentialSource->trustPath),
      'aaguid' => (string) $publicKeyCredentialSource->aaguid,
      'credentialPublicKey' => $publicKeyCredentialSource->credentialPublicKey,
      'userHandle' => $publicKeyCredentialSource->userHandle,
      'counter' => $publicKeyCredentialSource->counter,
      'backupEligible' => $publicKeyCredentialSource->backupEligible,
      'backupStatus' => $publicKeyCredentialSource->backupStatus,
      'uvInitialized' => $publicKeyCredentialSource->uvInitialized,
    ];
    $webauthn_credential = MoWebauthnCredential::create();
    $webauthn_credential->setLabel($label);
    $webauthn_credential->setCredentialId($publicKeyCredentialSource->publicKeyCredentialId);
    $webauthn_credential->setCredential(json_encode($credentials));
    $webauthn_credential->setUid(User::load(Base64UrlSafe::decode($publicKeyCredentialSource->userHandle)));

    $this->saveCredentials($webauthn_credential);
  }
  public function validateLabel(string $label): bool
  {
    $currentUser = \Drupal::currentUser();
    $credRepo = \Drupal::entityTypeManager()->getStorage('mo_webauthn_credential');
    return empty($credRepo->loadByProperties(['label' => $label, 'uid' => Base64UrlSafe::encode($currentUser->id())]));
  }
  public function getPublicKeyCredentialSource(string $credentialId): ?PublicKeyCredentialSource {
    $credentials = $this->findByPublicKeyCredentialId($credentialId);
    if (!$credentials) {
      return NULL;
    }
    return $this->generatePublicKeyCredentialSource($credentials);
  }

  private function generatePublicKeyCredentialSource(MoWebauthnCredential $credential): PublicKeyCredentialSource
  {
    $cred = json_decode($credential->getCredential(), TRUE);

    $cred['trustPath'] =  new $cred['trustPath']();
    $cred['aaguid'] = Uuid::fromString($cred['aaguid']);

    return PublicKeyCredentialSource::create(...$cred);
  }
}
