import elliptic from 'elliptic';
import { sha256 } from 'js-sha256';
import '../types/public-keys/public-key.js';
import '../types/public-keys/public-key-sr25519-value.js';
import '../types/public-keys/public-key-ed25519-value.js';
import '../types/public-keys/public-key-secp256k1-value.js';
import '../types/public-keys/public-key-x25519-value.js';
import '../types/signatures/signature-value.js';
import { SignatureSecp256k1 } from '../types/signatures/signature.js';
import { EcdsaSecp256k1VerKeyName } from '../vc/crypto/constants.js';
import DockKeypair from './dock-keypair.js';
import '../utils/types/array.js';
import { normalizeToU8a, hexToU8a, valueBytes } from '../utils/types/bytes.js';
import 'bs58';
import 'multiformats/bases/base58';
import 'varint';
import '../utils/encoding/ss-58.js';
import '../types/generic/typed-enum.js';
import '../utils/types/iterable.js';
import '../utils/types/object.js';
import 'crypto';
import '../utils/interfaces.js';
import '../utils/assert.js';
import '../utils/error.js';
import '../utils/inheritance.js';
import '../utils/types/set.js';
import '../types/generic/with-base.js';
import '../types/generic/with-catch-null.js';
import '../types/generic/with-eq.js';
import '../types/generic/any-of.js';
import '../types/generic/with-from.js';
import '../types/generic/with-from-json-bytes.js';
import '../types/generic/typed-null.js';
import '../types/generic/create-placeholder.js';
import 'blake2b';
import '../utils/types/string.js';
import '../types/generic/typed-bytes.js';
import '../types/generic/typed-string.js';
import '../types/generic/typed-array.js';
import '../types/generic/typed-tuple.js';
import '../types/generic/typed-number.js';
import '../types/generic/typed-set.js';
import '../types/generic/typed-map.js';
import '../types/generic/typed-struct.js';
import 'uuid';
import '../types/generic/sized.js';
import '../utils/types/number.js';
import '../types/signatures/signature-ed25519-value.js';
import '../types/signatures/signature-secp256k1-value.js';
import '../types/signatures/signature-sr25519-value.js';

const EC = elliptic.ec;
const secp256k1Curve = new EC('secp256k1');

function encodeDERInt(intBytes) {
  // Remove leading zeros
  let i = 0;
  while (i < intBytes.length && intBytes[i] === 0) {
    i++;
  }

  let trimmedBytes = intBytes.slice(i);

  // Ensure the integer is positive by adding a leading zero if needed
  // eslint-disable-next-line no-bitwise
  if (trimmedBytes[0] & 0x80) {
    trimmedBytes = Uint8Array.of(0, ...trimmedBytes);
  }

  const { length } = trimmedBytes;
  return [0x02, length, ...trimmedBytes];
}

class Secp256k1Keypair extends DockKeypair {
  static Signature = SignatureSecp256k1;

  static VerKeyType = EcdsaSecp256k1VerKeyName;

  static SeedSize = 32;

  /**
   *
   * Instantiates new `Secp256k1Keypair` from the provided source.
   * It can have one of two types: "entropy" or "private".
   *
   * @param {Uint8Array} entropyOrPrivate
   * @param {"entropy"|"private"} sourceType
   */
  constructor(entropyOrPrivate, sourceType = 'entropy') {
    let kp;
    switch (sourceType) {
      case 'entropy':
        if (entropyOrPrivate == null) {
          throw new Error('Entropy must be provided');
        }

        kp = secp256k1Curve.genKeyPair({
          entropy: entropyOrPrivate,
        });
        break;
      case 'private':
        kp = secp256k1Curve.keyFromPrivate(normalizeToU8a(entropyOrPrivate));
        break;
      default:
        throw new Error(
          `Unknown source type: \`${sourceType}\`, it must be either "entropy" or "private"`,
        );
    }

    super(kp);
  }

  _publicKey() {
    // public key is in hex but doesn't contain a leading zero
    return hexToU8a(`0x${this.keyPair.getPublic(true, 'hex')}`);
  }

  privateKey() {
    return hexToU8a(`0x${this.keyPair.getPrivate('hex')}`);
  }

  _sign(message) {
    const hash = this.constructor.hash(message);
    const sig = this.keyPair.sign(hash, {
      canonical: true,
    });

    // The signature is recoverable in 65-byte { R | S | index } format
    const r = sig.r.toString('hex', 32);
    const s = sig.s.toString('hex', 32);
    const i = sig.recoveryParam.toString(16).padStart(2, '0');

    // Make it proper hex
    return hexToU8a(`0x${r}${s}${i}`);
  }

  static signatureToDER(signature) {
    const sigBytes = valueBytes(signature);
    if (sigBytes.length !== 65) {
      throw new Error(
        `Invalid signature length. Expected is 65 bytes, received ${sigBytes.length}`,
      );
    }
    // Extract R, S components
    const r = sigBytes.slice(0, 32); // First 32 bytes
    const s = sigBytes.slice(32, 64); // Next 32 bytes

    // Encode R and S into DER format
    const rEncoded = encodeDERInt(r);
    const sEncoded = encodeDERInt(s);

    // Calculate total length
    const totalLength = rEncoded.length + sEncoded.length;

    // Create Uint8Array for DER encoding
    const der = new Uint8Array(2 + totalLength); // 2 bytes for tag and length + total payload
    der[0] = 0x30; // DER sequence tag
    der[1] = totalLength; // Total length of R + S encoded

    // Copy encoded R and S into DER array
    der.set(rEncoded, 2);
    der.set(sEncoded, 2 + rEncoded.length);

    return der;
  }

  static hash(message) {
    return sha256.digest(message);
  }

  static verify(message, signature, publicKey) {
    let bytes = valueBytes(signature);
    if (bytes.length === 65) {
      bytes = this.signatureToDER(bytes);
    }

    return secp256k1Curve.verify(
      this.hash(message),
      bytes,
      valueBytes(publicKey),
    );
  }
}

export { Secp256k1Keypair as default };
//# sourceMappingURL=keypair-secp256k1.js.map
