import { makeBytes } from "./bytes.js";
/**
 * Creates unprefixed hex string from wide range of data.
 *
 * TODO: Make Length mandatory: https://github.com/ethersphere/bee-js/issues/208
 *
 * @param input
 * @param len of the resulting HexString WITHOUT prefix!
 */

export function makeHexString(input, len) {
  if (typeof input === 'number') {
    return intToHex(input, len);
  }

  if (input instanceof Uint8Array) {
    return bytesToHex(input, len);
  }

  if (typeof input === 'string') {
    if (isPrefixedHexString(input)) {
      const hex = input.slice(2);

      if (len && hex.length !== len) {
        throw new TypeError(`Length mismatch for valid hex string. Expecting length ${len}: ${hex}`);
      }

      return hex;
    } else {
      // We use assertHexString() as there might be more reasons why a string is not valid hex string
      // and usage of isHexString() would not give enough information to the user on what is going
      // wrong.
      assertHexString(input, len);
      return input;
    }
  }

  throw new TypeError('Not HexString compatible type!');
}
/**
 * Converts a hex string to Uint8Array
 *
 * @param hex string input without 0x prefix!
 */

export function hexToBytes(hex) {
  assertHexString(hex);
  const bytes = makeBytes(hex.length / 2);

  for (let i = 0; i < bytes.length; i++) {
    const hexByte = hex.substr(i * 2, 2);
    bytes[i] = parseInt(hexByte, 16);
  }

  return bytes;
}
/**
 * Converts array of number or Uint8Array to HexString without prefix.
 *
 * @param bytes   The input array
 * @param len     The length of the non prefixed HexString
 */

export function bytesToHex(bytes, len) {
  const hexByte = n => n.toString(16).padStart(2, '0');

  const hex = Array.from(bytes, hexByte).join(''); // TODO: Make Length mandatory: https://github.com/ethersphere/bee-js/issues/208

  if (len && hex.length !== len) {
    throw new TypeError(`Resulting HexString does not have expected length ${len}: ${hex}`);
  }

  return hex;
}
/**
 * Converts integer number to hex string.
 *
 * Optionally provides '0x' prefix or padding
 *
 * @param int         The positive integer to be converted
 * @param len     The length of the non prefixed HexString
 */

export function intToHex(int, len) {
  if (!Number.isInteger(int)) throw new TypeError('the value provided is not integer');
  if (int > Number.MAX_SAFE_INTEGER) throw new TypeError('the value provided exceeds safe integer');
  if (int < 0) throw new TypeError('the value provided is a negative integer');
  const hex = int.toString(16); // TODO: Make Length mandatory: https://github.com/ethersphere/bee-js/issues/208

  if (len && hex.length !== len) {
    throw new TypeError(`Resulting HexString does not have expected length ${len}: ${hex}`);
  }

  return hex;
}
/**
 * Type guard for HexStrings.
 * Requires no 0x prefix!
 *
 * TODO: Make Length mandatory: https://github.com/ethersphere/bee-js/issues/208
 *
 * @param s string input
 * @param len expected length of the HexString
 */

export function isHexString(s, len) {
  return typeof s === 'string' && /^[0-9a-f]+$/i.test(s) && (!len || s.length === len);
}
/**
 * Type guard for PrefixedHexStrings.
 * Does enforce presence of 0x prefix!
 *
 * @param s string input
 */

export function isPrefixedHexString(s) {
  return typeof s === 'string' && /^0x[0-9a-f]+$/i.test(s);
}
/**
 * Verifies if the provided input is a HexString.
 *
 * TODO: Make Length mandatory: https://github.com/ethersphere/bee-js/issues/208
 *
 * @param s string input
 * @param len expected length of the HexString
 * @param name optional name for the asserted value
 * @returns HexString or throws error
 */

export function assertHexString(s, len, name = 'value') {
  if (!isHexString(s, len)) {
    if (isPrefixedHexString(s)) {
      throw new TypeError(`${name} not valid non prefixed hex string (has 0x prefix): ${s}`);
    } // Don't display length error if no length specified in order not to confuse user


    const lengthMsg = len ? ` of length ${len}` : '';
    throw new TypeError(`${name} not valid hex string${lengthMsg}: ${s}`);
  }
}
/**
 * Verifies if the provided input is a PrefixedHexString.
 *
 * @param s string input
 * @param len expected length of the HexString
 * @param name optional name for the asserted value
 * @returns HexString or throws error
 */

export function assertPrefixedHexString(s, name = 'value') {
  if (!isPrefixedHexString(s)) {
    throw new TypeError(`${name} not valid prefixed hex string: ${s}`);
  }
}