var __awaiter = this && this.__awaiter || function (thisArg, _arguments, P, generator) {
  function adopt(value) {
    return value instanceof P ? value : new P(function (resolve) {
      resolve(value);
    });
  }

  return new (P || (P = Promise))(function (resolve, reject) {
    function fulfilled(value) {
      try {
        step(generator.next(value));
      } catch (e) {
        reject(e);
      }
    }

    function rejected(value) {
      try {
        step(generator["throw"](value));
      } catch (e) {
        reject(e);
      }
    }

    function step(result) {
      result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
    }

    step((generator = generator.apply(thisArg, _arguments || [])).next());
  });
}; // For ESM compatibility


import pkg from 'elliptic';
const {
  ec
} = pkg;
import { BeeError } from "../utils/error.js";
import { isBytes, assertBytes, wrapBytesWithHelpers } from "../utils/bytes.js";
import { keccak256Hash } from "../utils/hash.js";
import { hexToBytes, makeHexString } from "../utils/hex.js";
import { SIGNATURE_BYTES_LENGTH, SIGNATURE_HEX_LENGTH } from "../types/index.js";
import { isStrictlyObject } from "../utils/type.js";
const UNCOMPRESSED_RECOVERY_ID = 27;

function hashWithEthereumPrefix(data) {
  const ethereumSignedMessagePrefix = `\x19Ethereum Signed Message:\n${data.length}`;
  const prefixBytes = new TextEncoder().encode(ethereumSignedMessagePrefix);
  return keccak256Hash(prefixBytes, data);
}
/**
 * The default signer function that can be used for integrating with
 * other applications (e.g. wallets).
 *
 * @param data      The data to be signed
 * @param privateKey  The private key used for signing the data
 */


export function defaultSign(data, privateKey) {
  const curve = new ec('secp256k1');
  const keyPair = curve.keyFromPrivate(privateKey);
  const hashedDigest = hashWithEthereumPrefix(data);
  const sigRaw = curve.sign(hashedDigest, keyPair, {
    canonical: true,
    pers: undefined
  });

  if (sigRaw.recoveryParam === null) {
    throw new BeeError('signDigest recovery param was null');
  }

  const signature = new Uint8Array([...sigRaw.r.toArray('be', 32), ...sigRaw.s.toArray('be', 32), sigRaw.recoveryParam + UNCOMPRESSED_RECOVERY_ID]);
  return signature;
}

function publicKeyToAddress(pubKey) {
  const pubBytes = pubKey.encode('array', false);
  return keccak256Hash(pubBytes.slice(1)).slice(12);
}
/**
 * Recovers the ethereum address from a given signature.
 *
 * Can be used for verifying a piece of data when the public key is
 * known.
 *
 * @param signature The signature
 * @param digest    The digest of the data
 *
 * @returns the recovered address
 */


export function recoverAddress(signature, digest) {
  const curve = new ec('secp256k1');
  const sig = {
    r: signature.slice(0, 32),
    s: signature.slice(32, 64)
  };
  const recoveryParam = signature[64] - UNCOMPRESSED_RECOVERY_ID;
  const hash = hashWithEthereumPrefix(digest);
  const recPubKey = curve.recoverPubKey(hash, sig, recoveryParam);
  return publicKeyToAddress(recPubKey);
}
/**
 * Creates a singer object that can be used when the private key is known.
 *
 * @param privateKey The private key
 */

export function makePrivateKeySigner(privateKey) {
  const curve = new ec('secp256k1');
  const keyPair = curve.keyFromPrivate(privateKey);
  const address = publicKeyToAddress(keyPair.getPublic());
  return {
    sign: digest => defaultSign(digest, privateKey),
    address
  };
}
export function assertSigner(signer) {
  if (!isStrictlyObject(signer)) {
    throw new TypeError('Signer must be an object!');
  }

  const typedSigner = signer;

  if (!isBytes(typedSigner.address, 20)) {
    throw new TypeError("Signer's address must be Uint8Array with 20 bytes!");
  }

  if (typeof typedSigner.sign !== 'function') {
    throw new TypeError('Signer sign property needs to be function!');
  }
}
export function makeSigner(signer) {
  if (typeof signer === 'string') {
    const hexKey = makeHexString(signer, 64);
    const keyBytes = hexToBytes(hexKey); // HexString is verified for 64 length => 32 is guaranteed

    return makePrivateKeySigner(keyBytes);
  } else if (signer instanceof Uint8Array) {
    assertBytes(signer, 32);
    return makePrivateKeySigner(signer);
  }

  assertSigner(signer);
  return signer;
}
export function sign(signer, data) {
  return __awaiter(this, void 0, void 0, function* () {
    const result = yield signer.sign(wrapBytesWithHelpers(data));

    if (typeof result === 'string') {
      const hexString = makeHexString(result, SIGNATURE_HEX_LENGTH);
      return hexToBytes(hexString);
    }

    if (result instanceof Uint8Array) {
      assertBytes(result, SIGNATURE_BYTES_LENGTH);
      return result;
    }

    throw new TypeError('Invalid output of sign function!');
  });
}