const REAL_SIGNATURE_SIZE = 2 * 65; // 65 bytes in hexadecimal string legnth
const PADDED_SIGNATURE_SIZE = 2 * 96; // 96 bytes in hexadecimal string length

const DUMMY_SIGNATURE = `0x${web3.utils.padLeft('', REAL_SIGNATURE_SIZE)}`;

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]));
}

// signs message in node (ganache auto-applies "Ethereum Signed Message" prefix)
const signMessage = (signer, messageHex = '0x') => {
  return web3.eth.sign(messageHex, signer);
};

/**
 * 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,
  ];

  // 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 signMessage(signer, messageHex);
};

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