<?php

declare(strict_types=1);

namespace Drupal\miniorange_webauthn\MoDto;

use InvalidArgumentException;
use Webauthn\AuthenticatorSelectionCriteria;
use Webauthn\PublicKeyCredentialCreationOptions;

class MoPubKeyCredCreateOptionReqDto
{
  public ?string $attestation = null;
  public ?string $userVerification = null;
  public ?string $residentKey = null;
  public ?string $authenticatorAttachment = null;
  public ?array $extensions = null;

  /**
   * @return string|null
   */
  public function getAttestation(): ?string
  {
    return $this->attestation;
  }

  /**
   * @param string|null $attestation
   * @return MoPubKeyCredCreateOptionReqDto
   */
  public function setAttestation(?string $attestation): MoPubKeyCredCreateOptionReqDto
  {
    $this->attestation = $attestation;
    return $this;
  }

  /**
   * @return string|null
   */
  public function getUserVerification(): ?string
  {
    return $this->userVerification;
  }

  /**
   * @param string|null $userVerification
   * @return MoPubKeyCredCreateOptionReqDto
   */
  public function setUserVerification(?string $userVerification): MoPubKeyCredCreateOptionReqDto
  {
    $this->userVerification = $userVerification;
    return $this;
  }

  /**
   * @return string|null
   */
  public function getResidentKey(): ?string
  {
    return $this->residentKey;
  }

  /**
   * @param string|null $residentKey
   * @return MoPubKeyCredCreateOptionReqDto
   */
  public function setResidentKey(?string $residentKey): MoPubKeyCredCreateOptionReqDto
  {
    $this->residentKey = $residentKey;
    return $this;
  }

  /**
   * @return string|null
   */
  public function getAuthenticatorAttachment(): ?string
  {
    return $this->authenticatorAttachment;
  }

  /**
   * @param string|null $authenticatorAttachment
   * @return MoPubKeyCredCreateOptionReqDto
   */
  public function setAuthenticatorAttachment(?string $authenticatorAttachment): MoPubKeyCredCreateOptionReqDto
  {
    $this->authenticatorAttachment = $authenticatorAttachment;
    return $this;
  }

  /**
   * @return array|null
   */
  public function getExtensions(): ?array
  {
    return $this->extensions;
  }

  /**
   * @param array|null $extensions
   * @return MoPubKeyCredCreateOptionReqDto
   */
  public function setExtensions(?array $extensions): MoPubKeyCredCreateOptionReqDto
  {
    $this->extensions = $extensions;
    return $this;
  }

  public function validate(MoPubKeyCredCreateOptionReqDto $dto): array {
    $errors = [];

    // Attestation
    $allowedAttestations = [
      PublicKeyCredentialCreationOptions::ATTESTATION_CONVEYANCE_PREFERENCE_NONE,
      PublicKeyCredentialCreationOptions::ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT,
      PublicKeyCredentialCreationOptions::ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT,
    ];
    if (!is_null($dto->attestation) && !in_array($dto->attestation, $allowedAttestations, true)) {
      $errors['attestation'] = 'Invalid attestation value.';
    }

    // User Verification
    $allowedUserVerification = [
      AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_PREFERRED,
      AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_REQUIRED,
      AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED,
    ];
    if (!is_null($dto->userVerification) && !in_array($dto->userVerification, $allowedUserVerification, true)) {
      $errors['userVerification'] = 'Invalid userVerification value.';
    }

    // Resident Key
    $allowedResidentKeys = [
      AuthenticatorSelectionCriteria::RESIDENT_KEY_REQUIREMENT_NO_PREFERENCE,
      AuthenticatorSelectionCriteria::RESIDENT_KEY_REQUIREMENT_REQUIRED,
      AuthenticatorSelectionCriteria::RESIDENT_KEY_REQUIREMENT_PREFERRED,
      AuthenticatorSelectionCriteria::RESIDENT_KEY_REQUIREMENT_DISCOURAGED,
    ];
    if (!is_null($dto->residentKey) && !in_array($dto->residentKey, $allowedResidentKeys, true)) {
      $errors['residentKey'] = 'Invalid residentKey value.';
    }

    // Authenticator Attachment
    $allowedAuthenticatorAttachments = [
      AuthenticatorSelectionCriteria::AUTHENTICATOR_ATTACHMENT_NO_PREFERENCE,
      AuthenticatorSelectionCriteria::AUTHENTICATOR_ATTACHMENT_PLATFORM,
      AuthenticatorSelectionCriteria::AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM,
    ];
    if (!is_null($dto->authenticatorAttachment) && !in_array($dto->authenticatorAttachment, $allowedAuthenticatorAttachments, true)) {
      $errors['authenticatorAttachment'] = 'Invalid authenticatorAttachment value.';
    }

    // Extensions (optional type check)
    if (!is_null($dto->extensions) && !is_array($dto->extensions)) {
      $errors['extensions'] = 'Extensions must be an array or null.';
    }

    return $errors;
  }

  public function buildDtoFromJson(string $json): MoPubKeyCredCreateOptionReqDto {

    $decoded = json_decode($json, true);
    if (json_last_error() !== JSON_ERROR_NONE) {
      throw new \InvalidArgumentException('Invalid JSON provided.');
    }

    $dto = new MoPubKeyCredCreateOptionReqDto();

    $dto->setAttestation($decoded['attestation'] ?? null);
    $dto->setUserVerification($decoded['userVerification'] ?? null);
    $dto->setResidentKey($decoded['residentKey'] ?? null);
    $dto->setAuthenticatorAttachment($decoded['authenticatorAttachment'] ?? null);
    $dto->setExtensions(isset($decoded['extensions']) && is_array($decoded['extensions']) ? $decoded['extensions'] : null);

    if (!empty($errors = $dto->validate($dto))) {
      foreach ($errors as $field => $message) {
        $messages[] = $field . ': ' . $message;
      }
      throw new InvalidArgumentException(implode("\n", $messages ?? []));
    }
    return $dto;
  }
}
