<?php

namespace Drupal\miniorange_webauthn\MoDto;

use CBOR\Decoder;
use CBOR\MapObject;
use CBOR\NegativeIntegerObject;
use CBOR\TextStringObject;
use Drupal\Core\Field\FieldItemInterface;
use Drupal\Core\Field\Plugin\Field\FieldType\MapItem;
use ParagonIE\ConstantTime\Base64UrlSafe;
use Symfony\Component\Uid\Uuid;
use Webauthn\AttestationStatement\AttestationObject;
use Webauthn\AttestationStatement\AttestationStatement;
use Webauthn\AttestedCredentialData;
use Webauthn\AuthenticatorAttestationResponse;
use Webauthn\AuthenticatorData;
use Webauthn\AuthenticatorResponse;
use Webauthn\CollectedClientData;
use Webauthn\PublicKeyCredential;
use Webauthn\PublicKeyCredentialDescriptor;
use CBOR\StringStream;
use Webauthn\TrustPath\EmptyTrustPath;

class MoPublicKeyCredentialDto extends PublicKeyCredential
{
  public static function build(string $id, string $type, string $rawId, array $response, array $extensions): self
  {
    $decoder = new Decoder();
    $stream = new StringStream(Base64UrlSafe::decodeNoPadding($response['attestationObject']));
    $cborObject = $decoder->decode($stream);
    $cborData = $cborObject->normalize();

    $attStmt = AttestationStatement::create($cborData['fmt'], $cborData['attStmt'], AttestationStatement::TYPE_BASIC, EmptyTrustPath::create());
    $authData =AuthenticatorData::create($cborData['authData'], ...self::parseAuthData($cborData['authData'], $extensions));

    $response = AuthenticatorAttestationResponse::create(
      CollectedClientData::createFormJson($response['clientDataJSON']),
      AttestationObject::create($response['attestationObject'], $attStmt, $authData)
    );
    return new self($type, $rawId, $response);
  }

  public function getPublicKeyCredentialDescriptor(): PublicKeyCredentialDescriptor
  {
    $transport = $this->response instanceof AuthenticatorAttestationResponse ? $this->response->transports : [];

    return PublicKeyCredentialDescriptor::create($this->type, $this->rawId, $transport);
  }

  public static function parseAuthData(string $authData, ?array $extensions): array {
    $offset = 0;

    // Step 1: RP ID Hash (32 bytes)
    $rpIdHash = substr($authData, $offset, 32);
    $offset += 32;

    // Step 2: Flags (1 byte)
    $flagStr = $authData[$offset];
    $flagsByte = ord($flagStr);
    $offset += 1;

    // Step 3: Signature counter (4 bytes)
    $signCount = unpack('N', substr($authData, $offset, 4))[1];
    $offset += 4;

    $attestedData = null;
    if (((bool)($flagsByte & 0x40))) {
      // Step 5: AAGUID (16 bytes)
      $aaguid = substr($authData, $offset, 16);
      $offset += 16;

      // Step 6: Credential ID Length (2 bytes)
      $credIdLen = unpack('n', substr($authData, $offset, 2))[1];
      $offset += 2;

      // Step 7: Credential ID
      $credId = substr($authData, $offset, $credIdLen);
      $offset += $credIdLen;

      // Step 8: COSE public key (CBOR) — rest of the data (or until extension block)
      $credentialPublicKey = substr($authData, $offset);

      $aaguid = Uuid::fromString(vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($aaguid), 4)));
      $attestedData = new AttestedCredentialData($aaguid, base64_encode($credId), base64_encode($credentialPublicKey));
    }

    return [
      'rpIdHash' => bin2hex($rpIdHash),
      'flags' => $flagStr,
      'signCount' => $signCount,
      'attestedCredentialData' => $attestedData,
      //todo if extensions comes in need to handle no need as-of now
      'extensions' => empty($extensions) ? null : $extensions,
    ];
  }
}
