<?php

namespace Drupal\miniorange_webauthn\Form;

use CBOR\Decoder;
use Drupal\Core\Access\CsrfTokenGenerator;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\CssCommand;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\Core\Ajax\InvokeCommand;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Drupal\Core\Url;
use Drupal\miniorange_2fa\MoAuthUtilities;
use Drupal\miniorange_webauthn\MoDto\MoPublicKeyCredentialDto;
use Drupal\miniorange_webauthn\MoProtector\MoStorage\MoItem;
use Drupal\miniorange_webauthn\MoRepo\MoPubKeyCredSourceRepoInterface;
use ParagonIE\ConstantTime\Base64UrlSafe;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Webauthn\AuthenticatorAttestationResponse;
use Webauthn\AuthenticatorAttestationResponseValidator;
use Webauthn\CeremonyStep\CeremonyStepManager;
use Webauthn\CeremonyStep\CheckAlgorithm;
use Webauthn\CeremonyStep\CheckChallenge;
use Webauthn\CeremonyStep\CheckOrigin;
use Webauthn\PublicKeyCredential;
use Webauthn\PublicKeyCredentialCreationOptions;
use Webauthn\PublicKeyCredentialRpEntity;
use Webauthn\PublicKeyCredentialSource;
use Webauthn\PublicKeyCredentialUserEntity;
use Webauthn\StringStream;

/**
 * Registration WebAuthn Credential Form.
 */
class RegistrationForm extends FormBase {

  private $tempstore;


  private $csrfToken;

  private $credSourceRepo;

  public function __construct(
    PrivateTempStoreFactory $tempStorage,
    CsrfTokenGenerator $csrf_token,
    MoPubKeyCredSourceRepoInterface $credSourceRepo
  ) {
    $this->tempstore = $tempStorage->get('miniorange_webauthn');
    $this->csrfToken = $csrf_token;
    $this->credSourceRepo = $credSourceRepo;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
      return new static(
        $container->get('tempstore.private'),
        $container->get('csrf_token'),
        $container->get('mo_webauthn.pub_key_cred_source_repo')
      );
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'mo_webauthn_registration_form';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $user_id  = \Drupal::routeMatch()->getParameter('user');
    $web_authn_configured = MoAuthUtilities::isWebAuthnConfiguredForUser($user_id);

    if(!$web_authn_configured){
      $this->messenger()->addWarning($this->t('Please configure Web Authentication as 2FA method for this user first.'));
    }

    $form['#attached']['library'][] = 'miniorange_webauthn/mo_webauthn_register_js';

    $form['label'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Label'),
      '#required' => TRUE,
      '#disabled' => !$web_authn_configured,
      '#description' => $this->t('Enter the name of your device.'),
      '#attributes' => ['id' => 'mo-label'],
    ];

    $form['credential'] = [
      '#type' => 'hidden',
      '#default_value' => '',
    ];

    $form['csrf_token'] = [
      '#type' => 'hidden',
      '#value' => $this->csrfToken?->get("/mo-webauthn/attestation"),
      '#attributes' => ['id' => 'mo-csrf-token']
    ];

    $form['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
      '#disabled' => !$web_authn_configured,
      '#button_type' => 'primary',
      '#attributes' => [
        'id' => 'mo-webauthn-register-btn',
      ]
    ];

    return $form;
  }

  public function validateCallback(array &$form, FormStateInterface $form_state): AjaxResponse
  {
    $response = new AjaxResponse();

    $label = $form_state->getValue('label');

    // Your backend validation logic
    $valid = $this->credSourceRepo->validateLabel($label);

    if ($valid) {
      $response->addCommand(new HtmlCommand('#validation-result', '<div class="messages messages--status"> ✅ Name is available</div>'));
      $response->addCommand(new InvokeCommand('#mo-label', 'prop', ['disabled', true]));
      $response->addCommand(new CssCommand('#mo-webauthn-register-btn', ['display' => 'inline-block']));
    } else {
      $response->addCommand(new HtmlCommand('#validation-result', '<div class="messages messages--error"> ❌ Name already Used.</div>'));
    }

    return $response;
  }

  public function validateForm(array &$form, FormStateInterface $form_state)
  {
    $label = $form_state->getValue('label');
    $is_valid_label = $this->credSourceRepo->validateLabel($label);
    if(!$is_valid_label) {
      $form_state->setErrorByName('label', $this->t('Name already used.'));
    }
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $label = $form_state->getValue('label');
    $credential = $form_state->getValue('credential');
    try {
      $credential = json_decode($credential, true);
      /** @var MoItem $storedData */
      $storedData = $this->tempstore->get('mo_webauthn_cred_create_opt');

      $publicKeyCredential = MoPublicKeyCredentialDto::build(...$credential);
      $response = $publicKeyCredential->response;
      $response instanceof AuthenticatorAttestationResponse || throw new BadRequestHttpException(
        'Invalid response'
      );

      $pubKeyCredCreateOpt = $storedData->getPublicKeyCredentialOptions();
      $pubKeyCredCreateOpt instanceof PublicKeyCredentialCreationOptions || throw new BadRequestHttpException(
        'Unable to find the public key credential creation options'
      );
      $pubKeyCredCreateOpt->challenge = Base64UrlSafe::decode($pubKeyCredCreateOpt->challenge);
      $attestationValidator = new AuthenticatorAttestationResponseValidator(
        new CeremonyStepManager(
          [
            new CheckChallenge(),
          ]
        )
      );
      $publicKeyCredentialSource = $attestationValidator->check($response, $pubKeyCredCreateOpt, \Drupal::request()->getHost());

      if (empty($this->credSourceRepo->checkDuplicate($publicKeyCredentialSource->publicKeyCredentialId))) {
        $this->credSourceRepo->createCredentials($publicKeyCredentialSource, $label);
      }
      $this->messenger()->addStatus($this->t('Success registation WebAuthn.'));
      $form_state->setRedirectUrl(
        Url::fromRoute('mo_webauthn.read_configuration',
        ['user' => Base64UrlSafe::decode($publicKeyCredentialSource->userHandle)])
      );
    }
    catch (\Exception $e) {
      $this->logger('webauthn_authenticator')->error('@message', ['@message' => $e->getMessage()]);
      $this->messenger()->addError($this->t('WebAuthn registration Failed.'));
    }
  }

}
