function toEthSignedMessageHash(messageHex) {
  const messageBuffer = Buffer.from(messageHex.substring(2), 'hex');
  const prefix = Buffer.from(`\u0019Ethereum Signed Message:\n${messageBuffer.length}`);
  return web3.utils.sha3(Buffer.concat([prefix, messageBuffer]));
}

/**
 * Create a signed data with intended validator according to the version 0 of EIP-191
 * @param validatorAddress The address of the validator
 * @param dataHex The data to be concatenated with the prefix and signed
 */
function toDataWithIntendedValidatorHash(validatorAddress, dataHex) {
  const validatorBuffer = Buffer.from(web3.utils.hexToBytes(validatorAddress));
  const dataBuffer = Buffer.from(web3.utils.hexToBytes(dataHex));
  const preambleBuffer = Buffer.from('\x19');
  const versionBuffer = Buffer.from('\x00');
  const ethMessage = Buffer.concat([preambleBuffer, versionBuffer, validatorBuffer, dataBuffer]);

  return web3.utils.sha3(ethMessage);
}

/**
 * Create a signer between a contract and a signer for a voucher of method, args, and redeemer
 * Note that `method` is the web3 method, not the truffle-contract method
 * @param contract TruffleContract
 * @param signer address
 * @param redeemer address
 * @param methodName string
 * @param methodArgs any[]
 */
const getSignFor =
  (contract, signer) =>
  (redeemer, methodName, methodArgs = []) => {
    const parts = [contract.address, redeemer];

    const REAL_SIGNATURE_SIZE = 2 * 65; // 65 bytes in hexadecimal string length
    const PADDED_SIGNATURE_SIZE = 2 * 96; // 96 bytes in hexadecimal string length
    const DUMMY_SIGNATURE = `0x${web3.utils.padLeft('', REAL_SIGNATURE_SIZE)}`;

    // if we have a method, add it to the parts that we're signing
    if (methodName) {
      if (methodArgs.length > 0) {
        parts.push(
          contract.contract.methods[methodName](...methodArgs.concat([DUMMY_SIGNATURE]))
            .encodeABI()
            .slice(0, -1 * PADDED_SIGNATURE_SIZE),
        );
      } else {
        const abi = contract.abi.find(abi => abi.name === methodName);
        parts.push(abi.signature);
      }
    }

    // return the signature of the "Ethereum Signed Message" hash of the hash of `parts`
    const messageHex = web3.utils.soliditySha3(...parts);
    return web3.eth.sign(messageHex, signer);
  };

module.exports = {
  toEthSignedMessageHash,
  toDataWithIntendedValidatorHash,
  getSignFor,
};
