'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var _defineProperty = require('@babel/runtime/helpers/defineProperty');
var bytes_js = require('ethereumjs-util/dist/bytes.js');
var minterjsTx = require('minterjs-tx');
var minterjsUtil = require('minterjs-util');
var utils = require('./utils.js');
var index = require('./tx-decorator/index.js');
var index$1 = require('./tx-data/index.js');

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }

/**
 * @typedef {object} TxParams
 * @property {number} [nonce]
 * @property {number} [chainId=1]
 * @property {number} [gasPrice=1]
 * @property {number|string} [gasCoin='0']
 * @property {string|Buffer|TX_TYPE} type
 * @property {string|Buffer|TX_TYPE} [txType] - deprecated
 * @property {Buffer|TxData|object} data
 * @property {Buffer|TxData|object} [txData] - deprecated
 * @property {string} [payload]
 * @property {string} [message] - deprecated
 * @property {number} [signatureType]
 * @property {ByteArray|{multisig: ByteArray, signatures: Array<ByteArray>}} [signatureData]
 */

/**
 * @typedef {object} TxOptions
 * @property {string} [seedPhrase] - to sign tx or get nonce or to make proof for redeemCheck tx
 * @property {ByteArray} [privateKey] - alternative to seedPhrase
 * @property {ByteArray} [address] - to get nonce (useful for multisignatures) or to make proof for redeemCheck tx
 * @property {ByteArray} [password] - to make proof for RedeemCheckTxData
 * @property {boolean} [disableValidation] - to disable validation of Tx or TxData constructors
 * @property {boolean} [disableDecorationParams] - to disable TxParams decoration (in `prepareTx`)
 */

/**
 * @typedef {Buffer|Uint8Array|string} ByteArray
 */

/**
 * @param {TxParams} txParams
 * @param {TxOptions} [options]
 * @return {Tx}
 */
function prepareSignedTx() {
  var txParams = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  if (!options.privateKey && txParams.privateKey) {
    options.privateKey = txParams.privateKey;
    // eslint-disable-next-line no-console
    console.warn('privateKey field in tx params is deprecated, pass it to the second argument');
  }
  if (utils.toInteger(txParams.signatureType) === '2') {
    throw new Error('prepareSignedTx doesn\'t support multi signatures');
  }
  var tx = prepareTx(_objectSpread(_objectSpread({}, txParams), {}, {
    signatureType: 1
  }), options);
  return tx;
}

/**
 * @param {TxParams} txParams
 * @param {TxOptions} [options]
 * @return {Tx}
 */
function prepareTx() {
  var txParams = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  txParams = _objectSpread(_objectSpread({}, txParams), {}, {
    data: txParams.data || txParams.txData,
    type: minterjsUtil.normalizeTxType(txParams.type || txParams.txType),
    payload: txParams.payload || txParams.message
  });
  if (!options.disableDecorationParams) {
    txParams = index(txParams);
  }
  var _txParams = txParams,
    nonce = _txParams.nonce,
    _txParams$chainId = _txParams.chainId,
    chainId = _txParams$chainId === void 0 ? 1 : _txParams$chainId,
    _txParams$gasPrice = _txParams.gasPrice,
    gasPrice = _txParams$gasPrice === void 0 ? 1 : _txParams$gasPrice,
    _txParams$gasCoin = _txParams.gasCoin,
    gasCoin = _txParams$gasCoin === void 0 ? 0 : _txParams$gasCoin,
    txType = _txParams.type,
    signatureType = _txParams.signatureType,
    signatureData = _txParams.signatureData;
  var _txParams2 = txParams,
    payload = _txParams2.payload,
    txData = _txParams2.data;
  if (!options.disableValidation) {
    utils.validateUint(nonce, 'nonce');
    utils.validateUint(chainId, 'chainId');
    utils.validateUint(gasPrice, 'gasPrice');
    utils.validateUint(gasCoin, 'gasCoin');
    if (!txType && typeof txType !== 'number') {
      throw new Error('Falsy tx type specified, tx can\'t be prepared');
    }
    if (!signatureType && typeof signatureType !== 'number') {
      throw new Error('Falsy signatureType specified, tx can\'t be prepared');
    }
  }
  txData = index$1.ensureBufferData(txData, txType, options);
  var txProps = {
    nonce: utils.integerToHexString(nonce || 0),
    chainId: utils.integerToHexString(chainId),
    gasPrice: utils.integerToHexString(gasPrice),
    gasCoin: utils.integerToHexString(gasCoin),
    type: txType,
    data: txData,
    signatureType: utils.integerToHexString(signatureType || 0),
    signatureData: ensureBufferSignature(signatureData, signatureType)
  };
  if (payload) {
    if (typeof payload === 'string') {
      payload = Buffer.from(payload, 'utf8');
    }
    txProps.payload = payload;
  }
  var tx = new minterjsTx.Tx(txProps);
  var privateKey = options.seedPhrase && !options.privateKey ? utils.getPrivateKeyFromSeedPhrase(options.seedPhrase) : options.privateKey;
  if (utils.toInteger(signatureType) === '1' && privateKey) {
    tx.signatureData = makeSignature(tx, privateKey);
  }
  return tx;
}

/**
 * @param {Tx} tx
 * @param {ByteArray} privateKey
 */
function makeSignature(tx, privateKey) {
  // @TODO asserts
  var privateKeyBuffer = bytes_js.toBuffer(privateKey);
  return new minterjsTx.TxSignature().sign(tx.hash(false), privateKeyBuffer).serialize();
}

/**
 * @param {string|ByteArray} txRlp
 * @param {boolean} [decodeCheck]
 * @return {TxParams}
 */
function decodeTx(txRlp) {
  var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
    decodeCheck = _ref.decodeCheck;
  var tx = new minterjsTx.Tx(txRlp);
  var txType = minterjsUtil.normalizeTxType(tx.type);
  var txData = index$1.decodeTxData(tx.type, tx.data, {
    decodeCheck: decodeCheck
  });
  return {
    nonce: tx.nonce.length > 0 ? utils.bufferToInteger(tx.nonce) : undefined,
    chainId: tx.chainId.length > 0 ? utils.bufferToInteger(tx.chainId) : undefined,
    gasPrice: tx.gasPrice.length > 0 ? utils.bufferToInteger(tx.gasPrice) : undefined,
    gasCoin: utils.bufferToInteger(tx.gasCoin),
    type: txType,
    data: txData,
    payload: tx.payload.toString('utf8'),
    signatureType: tx.signatureType.length > 0 ? utils.bufferToInteger(tx.signatureType) : undefined,
    signatureData: tx.signatureData.length > 0 ? "0x".concat(tx.signatureData.toString('hex')) : ''
  };
}

/**
 * @param {Buffer|TxMultisignature|object} signatureData
 * @param {number} signatureType
 * @return {Buffer}
 */
function ensureBufferSignature(signatureData, signatureType) {
  if (!signatureData) {
    return signatureData;
  }
  // serialize, if it TxMultisignature
  if (signatureData && utils.toInteger(signatureType) === '2' && typeof signatureData.serialize === 'function') {
    signatureData = signatureData.serialize();
  }

  // make buffer from object
  if (signatureData.length === undefined) {
    signatureData = new minterjsTx.TxMultisignature(signatureData);
    signatureData = signatureData.serialize();
  }
  return signatureData;
}

exports.decodeTx = decodeTx;
exports.default = prepareSignedTx;
exports.ensureBufferSignature = ensureBufferSignature;
exports.makeSignature = makeSignature;
exports.prepareTx = prepareTx;
