const { web3 } = require('@openzeppelin/test-environment');

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

function fixSignature (signature) {
  // in geth its always 27/28, in ganache its 0/1. Change to 27/28 to prevent
  // signature malleability if version is 0/1
  // see https://github.com/ethereum/go-ethereum/blob/v1.8.23/internal/ethapi/api.go#L465
  let v = parseInt(signature.slice(130, 132), 16);
  if (v < 27) {
    v += 27;
  }
  const vHex = v.toString(16);
  return signature.slice(0, 130) + vHex;
}

// signs message in node (ganache auto-applies "Ethereum Signed Message" prefix)
async function signMessage (signer, messageHex = '0x') {
  return fixSignature(await 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,
  ];

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

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