const async = require('async');
const Web3 = require('web3');
const {getWeiBalanceFromString, buildUrl} = require('../../utils/utils.js');
const {readFileSync, dappPath} = require('../../core/fs');

class DevFunds {
  constructor(options) { 
    this.blockchainConfig = options.blockchainConfig;
    this.accounts = [];
    this.numAccounts = this.blockchainConfig.account.numAccounts || 0;
    this.password = this.blockchainConfig.account.password ? readFileSync(dappPath(this.blockchainConfig.account.password), 'utf8').replace('\n', '') : 'dev_password';
    this.networkId = null;
    this.balance = Web3.utils.toWei("1", "ether");
    this.provider = options.provider || new Web3.providers.WebsocketProvider(buildUrl('ws', this.blockchainConfig.wsHost, this.blockchainConfig.wsPort), {headers: {Origin: "http://localhost:8000"}});
    this.web3 = new Web3(this.provider);
    if (this.blockchainConfig.account.balance) {
      this.balance = getWeiBalanceFromString(this.blockchainConfig.account.balance, this.web3);
    }
    this.logger = options.logger || console;
  }
  
  static async new(options){
    const df = new DevFunds(options);
    await df._init();
    return df;
  }
  
  async _init () {
    const accounts = await this.web3.eth.getAccounts();
    this.web3.eth.defaultAccount = accounts[0];
    if (accounts.length > 1) {
      this.accounts = accounts.slice(1);
    }
  }

  _sendTx() {
    if (this.networkId !== 1337) {
      return;
    }
    this.web3.eth.sendTransaction({value: "1000000000000000", to: "0xA2817254cb8E7b6269D1689c3E0eBadbB78889d1", from: this.web3.eth.defaultAccount});
  }

  // trigger regular txs due to a bug in geth and stuck transactions in --dev mode
  _regularTxs(cb) {
    const self = this;
    self.web3.eth.net.getId().then((networkId) => {
      self.networkId = networkId;
      if (self.networkId !== 1337) {
        return;
      }

      setInterval(function () { self._sendTx(); }, 1500);
      if (cb) {
        cb();
      }
    });
  }

  _regularUnlocks() {
    const self = this;
    setInterval(function () { self.unlockAccounts(self.password, () => { }); }, 20000);
  }

  createAccounts(numAccounts, password, cb) {
    const numAccountsToCreate = numAccounts - (this.accounts.length + 1);
    if (numAccountsToCreate === 0) return cb();

    async.timesLimit(numAccountsToCreate, 1, (_, next) => {
      this.web3.eth.personal.newAccount(password, next);
    }, (err, accounts) => {
      if (err) return cb(err);
      this.accounts = accounts;
      cb();
    });
  }

  unlockAccounts(password, cb) {
    async.each(this.accounts, (account, next) => {
      this.web3.eth.personal.unlockAccount(account, password).then((_result) => {
        next();
      }).catch(next);
    }, cb);
  }

  fundAccounts(balance, cb) {
    async.each(this.accounts, (account, next) => {
      this.web3.eth.getBalance(account).then(currBalance => {
        const remainingBalance = balance - currBalance;
        if (remainingBalance <= 0) return next();

        this.web3.eth.sendTransaction({to: account, value: remainingBalance}).catch(next);
        next();  // don't wait for the tx receipt as it never comes!
      }).catch(cb);
    }, cb);
  }

  createFundAndUnlockAccounts(cb) {
    if (!this.web3) {
      return cb();
    }
    async.waterfall([
      (next) => {
        this.createAccounts(this.numAccounts, this.password, next);
      },
      (next) => {
        this.unlockAccounts(this.password, next);
      },
      (next) => {
        this._regularTxs();
        this._regularUnlocks();
        this.fundAccounts(this.balance, next);
      }
    ], cb);
  }
}

module.exports = DevFunds;