import '../../generic/typed-enum.js';
import TypedBytes from '../../generic/typed-bytes.js';
import TypedString from '../../generic/typed-string.js';
import { maybeToCheqdPayloadOrJSON } from '../../../utils/interfaces.js';
import '../../generic/typed-array.js';
import '../../generic/typed-tuple.js';
import '../../generic/typed-null.js';
import option from '../../generic/option.js';
import '../../../utils/types/array.js';
import { fmtIterable } from '../../../utils/types/iterable.js';
import { filterObj } from '../../../utils/types/object.js';
import { u8aToString, valueBytes, normalizeToU8a, stringToU8a } from '../../../utils/types/bytes.js';
import { ensureEqualToOrPrototypeOf } from '../../../utils/inheritance.js';
import { encodeAsBase58 } from '../../../utils/encoding/base-58.js';
import 'multiformats/bases/base58';
import 'varint';
import '../../../utils/encoding/ss-58.js';
import TypedNumber from '../../generic/typed-number.js';
import '../../generic/typed-set.js';
import '../../generic/typed-map.js';
import TypedStruct from '../../generic/typed-struct.js';
import 'uuid';
import 'js-sha256';
import withFrom from '../../generic/with-from.js';
import withProp from '../../generic/with-prop.js';
import withProps from '../../generic/with-props.js';
import withBase58 from '../../generic/with-base58.js';
import withBase58btc from '../../generic/with-base58btc.js';
import withMultibase from '../../generic/with-multibase.js';
import '../../offchain-signatures/params/index.js';
import { BBDT16PublicKey, PSPublicKey, BBSPublicKey, BBSPlusPublicKey } from '../../offchain-signatures/public-keys/index.js';
import { CurveType, CurveTypeBls12381 } from '../../offchain-signatures/curve-type.js';
import { PublicKeySecp256k1, PublicKeyEd25519, PublicKeySr25519 } from '../../public-keys/public-key.js';
import '../../public-keys/public-key-sr25519-value.js';
import '../../public-keys/public-key-ed25519-value.js';
import '../../public-keys/public-key-secp256k1-value.js';
import '../../public-keys/public-key-x25519-value.js';
import '../../../vc/crypto/Bls12381BBDT16KeyPairDock2024.js';
import '../../../vc/crypto/Bls12381BBDT16MACDock2024.js';
import '../../../vc/crypto/Bls12381BBDT16MACProofDock2024.js';
import '../../../vc/crypto/Bls12381BBSKeyPairDock2023.js';
import '../../../vc/crypto/Bls12381BBSSignatureDock2022.js';
import '../../../vc/crypto/Bls12381BBSSignatureDock2023.js';
import '../../../vc/crypto/Bls12381BBSSignatureProofDock2022.js';
import '../../../vc/crypto/Bls12381BBSSignatureProofDock2023.js';
import '../../../vc/crypto/Bls12381BDDT16KeyPairDock2024.js';
import '../../../vc/crypto/Bls12381BDDT16MACDock2024.js';
import '../../../vc/crypto/Bls12381BDDT16MACProofDock2024.js';
import '../../../vc/crypto/Bls12381G2KeyPairDock2022.js';
import '../../../vc/crypto/Bls12381PSKeyPairDock2023.js';
import '../../../vc/crypto/Bls12381PSSignatureDock2023.js';
import '../../../vc/crypto/Bls12381PSSignatureProofDock2023.js';
import { Bls12381BBDT16DockVerKeyName, Bls12381PSDockVerKeyName, Bls12381BBS23DockVerKeyName, Bls12381BBSDockVerKeyName, EcdsaSecp256k1VerKeyName, Ed255192020VerKeyName, Sr25519VerKeyName, Ed25519VerKeyName } from '../../../vc/crypto/constants.js';
import 'bs58';
import '@juanelas/base64';
import '../../../keypairs/keypair-secp256k1.js';
import { isMultibaseBytes } from '../../../utils/encoding/multibase.js';
import '../../../vc/crypto/common/CustomLinkedDataSignature.js';
import '@stablelib/ed25519';
import '../../signatures/signature-value.js';
import '../../signatures/signature.js';
import '../../../keypairs/dock-keypair.js';
import '@transmute/json-web-signature';
import { VerificationMethodType, Ed25519Verification2018Method, Ed25519Verification2020Method, JsonWebKey2020Method } from './verification-method-type.js';
import { VerificationMethodRef, CheqdVerificationMethodRef, CheqdTestnetVerificationMethodRef, CheqdMainnetVerificationMethodRef } from './verification-method-ref.js';
import { NamespaceDid } from '../onchain/typed-did/index.js';
import { DockOrCheqdOffchainSignatureParamsRef, DockOffchainSignatureParamsRef, CheqdOffchainSignatureParamsRef, CheqdTestnetOffchainSignatureParamsRef, CheqdMainnetOffchainSignatureParamsRef } from '../../offchain-signatures/public-keys/ref.js';
import { CheqdTestnetDid, CheqdMainnetDid } from '../onchain/typed-did/cheqd-did.js';
import 'crypto';
import '../../generic/with-base.js';
import '../../generic/with-catch-null.js';
import '../../generic/with-eq.js';
import '../../generic/any-of.js';
import '../../../utils/types/set.js';
import 'blake2b';
import '../../../utils/assert.js';
import '../../../utils/error.js';
import '../../../utils/types/string.js';
import '../../generic/with-from-json-bytes.js';
import '../../generic/create-placeholder.js';
import '../../../utils/encoding/base-58-btc.js';
import '../../offchain-signatures/params/value.js';
import '../../offchain-signatures/params/id.js';
import '../../generic/typed-uuid.js';
import '../../generic/with-from-dock-id.js';
import '../onchain/typed-did/did-method-key/did-method-key-public-key.js';
import '../../generic/sized.js';
import '../../../utils/types/number.js';
import '../../generic/with-qualifier.js';
import '../onchain/constants.js';
import '../onchain/typed-did/signature.js';
import '../onchain/typed-did/did-method-key/did-method-key-signature.js';
import '../../signatures/signature-ed25519-value.js';
import '../../signatures/signature-secp256k1-value.js';
import '../../signatures/signature-sr25519-value.js';
import '../onchain/typed-did/dock-did-value.js';
import '../../generic/utils.js';
import '../../generic/typed-bytes-array.js';
import '../../offchain-signatures/public-keys/value.js';
import '../../offchain-signatures/public-keys/id.js';
import '@docknetwork/crypto-wasm-ts';
import '../../../vc/crypto/common/DockCryptoKeyPair.js';
import '@docknetwork/crypto-wasm-ts/lib/index.js';
import '../../../vc/crypto/common/DockCryptoSignature.js';
import 'jsonld-signatures';
import 'json-stringify-deterministic';
import 'semver/preload.js';
import 'base64url';
import 'jsonld';
import '../../../vc/jws.js';
import '../../../vc/crypto/common/DockCryptoSignatureProof.js';
import 'elliptic';

class PublicKeyBase58 extends withBase58(TypedBytes) {}

class PublicKeyBase64 extends withBase58btc(TypedBytes) {}

class PublicKeyMultibase extends withMultibase(TypedBytes) {}

class PublicKeyBase58OrMultibase extends PublicKeyBase58 {
  toString() {
    return this.bytes.length === 32 ? super.toString() : `z${super.toString()}`;
  }

  toCheqdPayload() {
    return this.toString();
  }

  static from(val) {
    if (typeof val === 'string' && val.substr(0, 1) === 'z') {
      return PublicKeyBase58OrMultibase.fromBase58(val.substr(1));
    } else if (typeof val === 'string') {
      return PublicKeyBase58OrMultibase.fromBase58(val);
    }

    return new PublicKeyBase58OrMultibase(val);
  }
}

class TypedJsonWebKey extends withFrom(
  TypedStruct,
  (value, fromFn) => fromFn(value),
) {
  static Classes = {
    crv: option(TypedString),
    kty: option(TypedString),
    x: option(TypedString),
  };

  get bytes() {
    return this.toJSONStringBytes();
  }

  static fromBytes(bytes) {
    return this.fromJSON(JSON.parse(u8aToString(bytes)));
  }
}

class PublicKeyMetadata extends withFrom(TypedStruct, (value, from) => {
  const self = from(value);

  if (self?.paramsRef == null && self?.participantId == null) {
    return null;
  } else {
    return self;
  }
}) {
  static Classes = {
    paramsRef: option(DockOrCheqdOffchainSignatureParamsRef),
    curveType: CurveType,
    participantId: option(TypedNumber),
  };

  constructor(paramsRef, curveType = new CurveTypeBls12381(), ...args) {
    super(paramsRef, curveType, ...args);
  }

  toArgs() {
    const { paramsRef, curveType, participantId } = this;

    return [paramsRef, curveType, participantId];
  }
}

class DockPublicKeyMetadata extends withProp(
  PublicKeyMetadata,
  'paramsRef',
  DockOffchainSignatureParamsRef,
) {}

class CheqdPublicKeyMetadata extends withProp(
  PublicKeyMetadata,
  'paramsRef',
  CheqdOffchainSignatureParamsRef,
) {}

class CheqdTestnetPublicKeyMetadata extends withProp(
  PublicKeyMetadata,
  'paramsRef',
  CheqdTestnetOffchainSignatureParamsRef,
) {}

class CheqdMainnetPublicKeyMetadata extends withProp(
  PublicKeyMetadata,
  'paramsRef',
  CheqdMainnetOffchainSignatureParamsRef,
) {}

class VerificationMethod extends withFrom(
  TypedStruct,
  function from(value, fromFn) {
    return fromFn(
      // eslint-disable-next-line no-use-before-define
      value instanceof CheqdVerificationMethod
        ? value.toVerificationMethod(this)
        : value,
    );
  },
) {
  static Classes = {
    id: VerificationMethodRef,
    type: VerificationMethodType,
    controller: NamespaceDid,
    publicKeyBase58: option(PublicKeyBase58),
    publicKeyBase64: option(PublicKeyBase64),
    publicKeyMultibase: option(PublicKeyMultibase),
    publicKeyJwk: option(TypedJsonWebKey),
    publicKeyHex: option(TypedBytes),
    metadata: option(PublicKeyMetadata),
  };

  isOffchain() {
    return !(
      this.type instanceof Ed25519Verification2018Method
      || this.type instanceof Ed25519Verification2020Method
      || this.type instanceof JsonWebKey2020Method
    );
  }

  publicKeyBytes(fullRepresentation = false) {
    const bytes = (
      this.publicKeyMultibase
      || this.publicKeyBase58
      || this.publicKeyBase64
      || this.publicKeyJwk
      || this.publicKeyHex
    )?.bytes;

    if (bytes == null) {
      throw new Error(
        `Expected either of ${fmtIterable([
          'publicKeyMultibase',
          'publicKeyBase58',
          'publicKeyBase64',
          'publicKeyJwk',
          'publicKeyHex',
        ])} to be specified`,
      );
    }

    if (this.publicKeyMultibase) {
      return (fullRepresentation || bytes.length === 32) ? bytes : bytes.slice(bytes.length - 32);
    }

    return bytes;
  }

  publicKeyClass() {
    switch (this.type.type) {
      case Ed25519VerKeyName:
        return PublicKeyEd25519;
      case Sr25519VerKeyName:
        return PublicKeySr25519;
      case Ed255192020VerKeyName:
        return PublicKeyEd25519;
      case EcdsaSecp256k1VerKeyName:
        return PublicKeySecp256k1;
      case Bls12381BBSDockVerKeyName:
        return BBSPlusPublicKey;
      case Bls12381BBS23DockVerKeyName:
        return BBSPublicKey;
      case Bls12381PSDockVerKeyName:
        return PSPublicKey;
      case Bls12381BBDT16DockVerKeyName:
        return BBDT16PublicKey;
      default:
        throw new Error(`Unknown key type ${this.type.type}`);
    }
  }

  publicKey() {
    const PublicKey = this.publicKeyClass();
    const bytes = this.publicKeyBytes();
    const metaArgs = this.metadata?.toArgs() || [];

    return new PublicKey(new PublicKey.Class(bytes, ...metaArgs));
  }

  static fromDidKey(keyRef, didKey) {
    const ref = VerificationMethodRef.from(keyRef);

    const isJsonWebKey = didKey.publicKey.constructor.VerKeyType === JsonWebKey2020Method.Type;
    const pkBytes = valueBytes(didKey.publicKey);
    const isMultibaseKey = !isJsonWebKey && isMultibaseBytes(pkBytes);

    return new this(
      ref,
      didKey.publicKey.constructor.VerKeyType,
      ref[0],
      (!isMultibaseKey && !isJsonWebKey) ? pkBytes : void 0,
      void 0,
      isMultibaseKey ? pkBytes : void 0,
      isJsonWebKey ? TypedJsonWebKey.fromBytes(pkBytes) : void 0,
      void 0,
      didKey.isOffchain() ? didKey.publicKey.value : null,
    );
  }

  // eslint-disable-next-line no-use-before-define
  toCheqd(Class = CheqdVerificationMethod) {
    // eslint-disable-next-line no-use-before-define
    return new (ensureEqualToOrPrototypeOf(CheqdVerificationMethod, Class))(
      this.id,
      this.controller,
      this.type,
      normalizeToU8a(this.publicKeyBytes(true)),
      this.metadata,
    );
  }

  toJSON() {
    return filterObj(super.toJSON(), (_, value) => value != null);
  }

  toCheqdPayload() {
    return filterObj(
      this.apply(maybeToCheqdPayloadOrJSON),
      (_, value) => value != null,
    );
  }
}

class CheqdVerificationMethod extends withFrom(
  TypedStruct,
  function from(value, fromFn) {
    if (
      !(value instanceof VerificationMethod)
      && value?.verificationMethodType === JsonWebKey2020Method.Type
      && typeof value?.verificationMaterial === 'string'
    ) {
      return fromFn({
        ...value,
        verificationMaterial: stringToU8a(value.verificationMaterial),
      });
    }

    return fromFn(
      value instanceof VerificationMethod ? value.toCheqd(this) : value,
    );
  },
) {
  static Classes = {
    id: CheqdVerificationMethodRef,
    controller: NamespaceDid,
    verificationMethodType: VerificationMethodType,
    verificationMaterial: PublicKeyBase58OrMultibase,
    metadata: option(PublicKeyMetadata),
  };

  isOffchain() {
    return !(
      this.verificationMethodType instanceof Ed25519Verification2018Method
      || this.verificationMethodType instanceof Ed25519Verification2020Method
      || this.verificationMethodType instanceof JsonWebKey2020Method
    );
  }

  toVerificationMethod(VerMethod = VerificationMethod) {
    // Convert the Cheqd-specific ID to a generic VerificationMethodRef
    const isJsonWebKey = this.verificationMethodType instanceof JsonWebKey2020Method;
    const genericId = VerificationMethodRef.from(this.id.toJSON());
    const isMultibaseKey = !isJsonWebKey && isMultibaseBytes(this.verificationMaterial);
    return new (ensureEqualToOrPrototypeOf(VerificationMethod, VerMethod))(
      genericId,
      this.verificationMethodType,
      this.controller,
      (!isMultibaseKey && !isJsonWebKey) ? this.verificationMaterial : void 0,
      void 0,
      isMultibaseKey ? this.verificationMaterial : void 0,
      isJsonWebKey ? TypedJsonWebKey.fromBytes(this.verificationMaterial) : void 0,
      void 0,
      this.metadata,
    );
  }

  toJSON() {
    return filterObj(super.toJSON(), (_, value) => value != null);
  }

  // eslint-disable-next-line sonarjs/no-identical-functions
  toCheqdPayload() {
    const payload = filterObj(
      this.apply(maybeToCheqdPayloadOrJSON),
      (_, value) => value != null,
    );

    if (this.verificationMethodType instanceof JsonWebKey2020Method) {
      payload.verificationMaterial = u8aToString(this.verificationMaterial);
    }

    return payload;
  }
}

class CheqdVerificationMethodAssertion extends withProp(
  VerificationMethod,
  'metadata',
  option(CheqdPublicKeyMetadata),
) {
  toCheqdPayload() {
    return JSON.stringify(JSON.stringify(super.toCheqdPayload()));
  }

  static from(obj) {
    return typeof obj === 'string'
      ? super.fromJSON(JSON.parse(JSON.parse(obj)))
      : super.from(obj);
  }
}

class CheqdTestnetVerificationMethod extends withProps(
  CheqdVerificationMethod,
  {
    id: CheqdTestnetVerificationMethodRef,
    controller: CheqdTestnetDid,
    metadata: option(CheqdTestnetPublicKeyMetadata),
  },
) {}

class CheqdMainnetVerificationMethod extends withProps(
  CheqdVerificationMethod,
  {
    id: CheqdMainnetVerificationMethodRef,
    controller: CheqdMainnetDid,
    metadata: option(CheqdMainnetPublicKeyMetadata),
  },
) {}

class CheqdMainnetVerificationMethodAssertion extends withProps(
  CheqdVerificationMethodAssertion,
  {
    id: CheqdMainnetVerificationMethodRef,
    controller: CheqdMainnetDid,
    metadata: option(CheqdMainnetPublicKeyMetadata),
  },
) {}

class CheqdTestnetVerificationMethodAssertion extends withProps(
  CheqdVerificationMethodAssertion,
  {
    id: CheqdTestnetVerificationMethodRef,
    controller: CheqdTestnetDid,
    metadata: option(CheqdTestnetPublicKeyMetadata),
  },
) {}

class CheqdVerificationMethodAssertionLegacy extends withFrom(
  CheqdVerificationMethod,
  (value, from) => {
    const self = from(value);
    const verMethod = self.toVerificationMethod();

    if (verMethod.isOffchain()) {
      let json;
      try {
        json = JSON.parse(u8aToString(verMethod.publicKeyBytes()));
      } catch {
        return self;
      }

      const pk = verMethod.publicKeyClass().from(json);
      self.verificationMaterial = valueBytes(pk.bytes);
      self.metadata = pk.value;
    }

    return self;
  },
) {
  toCheqdPayload() {
    const payload = filterObj(
      this.apply(maybeToCheqdPayloadOrJSON),
      (key, value) => key !== 'metadata' && value != null,
    );

    const verMethod = this.toVerificationMethod();

    if (verMethod.isOffchain()) {
      const pk = verMethod.publicKey();

      if (pk.value.paramsRef != null || pk.value.participantId != null) {
        payload.verificationMaterial = encodeAsBase58(pk.toJSONStringBytes());
      }
    }

    return JSON.stringify(JSON.stringify(payload));
  }

  // eslint-disable-next-line sonarjs/no-identical-functions
  static from(obj) {
    return typeof obj === 'string'
      ? super.fromJSON(JSON.parse(JSON.parse(obj)))
      : super.from(obj);
  }
}

class CheqdMainnetVerificationMethodAssertionLegacy extends withProps(
  CheqdVerificationMethodAssertionLegacy,
  {
    id: CheqdMainnetVerificationMethodRef,
    controller: CheqdMainnetDid,
    metadata: option(CheqdMainnetPublicKeyMetadata),
  },
) {}

class CheqdTestnetVerificationMethodAssertionLegacy extends withProps(
  CheqdVerificationMethodAssertionLegacy,
  {
    id: CheqdTestnetVerificationMethodRef,
    controller: CheqdTestnetDid,
    metadata: option(CheqdTestnetPublicKeyMetadata),
  },
) {}

export { CheqdMainnetPublicKeyMetadata, CheqdMainnetVerificationMethod, CheqdMainnetVerificationMethodAssertion, CheqdMainnetVerificationMethodAssertionLegacy, CheqdPublicKeyMetadata, CheqdTestnetPublicKeyMetadata, CheqdTestnetVerificationMethod, CheqdTestnetVerificationMethodAssertion, CheqdTestnetVerificationMethodAssertionLegacy, CheqdVerificationMethod, CheqdVerificationMethodAssertion, CheqdVerificationMethodAssertionLegacy, DockPublicKeyMetadata, PublicKeyBase58, PublicKeyBase58OrMultibase, PublicKeyBase64, PublicKeyMetadata, PublicKeyMultibase, TypedJsonWebKey, VerificationMethod };
//# sourceMappingURL=verification-method.js.map
