/**
 * @fileOverview actions to set transactions state within the app and to
 * call the corresponding GRPC apis for listing transactions.
 */

import * as log from './log';
/*
import {
  parseDate,
  toHex,
  checkHttpStatus,
  addAsaQuote,
} from '../helper';
*/
import { parseDate, toHex, checkHttpStatus, addAsaQuote } from '../helper';

var hii;

class PoolAction {
  constructor(store, grpc, nav, notification) {
    this._store = store;
    this._grpc = grpc;
    this._nav = nav;
    this._notification = notification;
    hii = this;
  }

  /**
   * Initiate the transaction list view by navigating to the view and updating
   * the app's transaction state by calling all necessary grpc apis.
   * @return {undefined}
   */
  async init() {
    var nav = this._nav;
    this.update().then(function() {
      console.log('INFO! loaded pools');
      //nav.goPools();
    });
    nav.goPools();
    //nav.goWait();
  }

  /**
   * Select a transaction item from the transaction list view and then navigate
   * to the detail view to list transaction parameters.
   * @param  {Object} options.item The selected transaction object
   * @return {Promise<undefined>}
   */
  async select({ item }) {
    this._store.selectedPool = item;
    if (item.paymentRequest) {
      item.memo = await this.decodeMemo({ payReq: item.paymentRequest });
    }
    this._nav.goPoolDetail();
    //this.update();
  }

  /**
   * Update the on-chain transactions, invoice, and lighting payments in the
   * app state by querying all required grpc apis.
   * @return {Promise<undefined>}
   */
  async update() {
    await Promise.all([
      await this.getPools(),
      //this.getInvoices(),
      //this.getPayments(),
    ]);
  }

  async poolsToPool(pool) {
    var grpc = hii._grpc;
    var store = hii._store;
    try {
      var pDat = await addAsaQuote(grpc, store, pool.poolID.id, 0);
    } catch (er) {
      throw pDat;
    }
    //TEMORARY HACK!!
    pool.active = [true, false]
      .sort(() => Math.random() - Math.random())
      .slice(0, 1)[0];
    var ret = {
      id: pool.id,
      cat: 'micro',
      type: 'algorand',
      icon: pool.bannerPath,
      quote: pDat.q,
      assetID: pool.poolID.id,
      banner: pool.bannerPath,
      url: 'https://demo1.bitsoko.org',
      title: pool.name,
      desc: pool.description,
      amount: pDat.b / Math.pow(10, 6),
      fee: 0,
      confirmations: 5,
      status: pool.active ? 'open' : 'closed',
      date: parseDate(1604646525),
    };
    return ret;
  }
  /**
   * List the on-chain transactions by calling the respective grpc api and updating
   * the transactions array in the global store.
   * @return {Promise<undefined>}
   */
  async getPools() {
    try {
      const uri = 'https://dooca.bitsoko.org/getEnterprise/?servEntID=1';
      const response = checkHttpStatus(await fetch(uri));
      const svs = await response.json();
      var pools = svs.services;
      //const pools = (await response.json()).tickers;
      //const rate = tickers.find(t => t.ticker.toLowerCase() === fiat).rate;
      //var quote = await this.addQuote;
      var pooled = await Promise.allSettled(pools.map(this.poolsToPool));
      pooled = pooled.filter(function(pl) {
        return pl.status == 'fulfilled' && pl.value.quote != undefined;
      });
      console.log(pooled);
      this._store.pools = pooled.map(function(pl) {
        if (pl.status == 'fulfilled') {
          var ret = pl.value;
          return ret;
        }
      });
    } catch (err) {
      log.error('Listing transactions failed', err);
    }
  }

  /**
   * List the lightning invoices by calling the respective grpc api and updating
   * the invoices array in the global store.
   * @return {Promise<undefined>}
   */
  async getInvoices() {
    try {
      const { invoices } = await this._grpc.sendCommand('listInvoices');
      this._store.invoices = invoices.map(invoice => ({
        id: toHex(invoice.rHash),
        type: 'lightning',
        amount: invoice.value,
        status: invoice.settled ? 'complete' : 'in-progress',
        date: parseDate(invoice.creationDate),
        memo: invoice.memo,
      }));
    } catch (err) {
      log.error('Listing invoices failed', err);
    }
  }

  /**
   * List the lightning payments by calling the respective grpc api and updating
   * the payments array in the global store.
   * @return {Promise<undefined>}
   */
  async getPayments() {
    try {
      const { payments } = await this._grpc.sendCommand('listPayments');
      this._store.payments = payments.map(payment => ({
        id: payment.paymentHash,
        type: 'lightning',
        amount: -1 * payment.value,
        fee: payment.fee,
        status: 'complete',
        date: parseDate(payment.creationDate),
        preimage: payment.paymentPreimage,
        paymentRequest: payment.paymentRequest,
      }));
    } catch (err) {
      log.error('Listing payments failed', err);
    }
  }

  /**
   * Attempt to decode a lightning payment request using the lnd grpc api.
   * @param  {string} options.payReq  The input to be validated
   * @return {Promise<string>}       If the input is a valid invoice
   */
  async decodeMemo({ payReq }) {
    try {
      const { description } = await this._grpc.sendCommand('decodePayReq', {
        payReq,
      });
      return description;
    } catch (err) {
      log.info(`Decoding payment request failed: ${err.message}`);
    }
  }

  /**
   * Subscribe to incoming on-chain transactions using the grpc streaming api.
   * @return {Promise<undefined>}
   */
  async subscribeTransactions() {
    const stream = this._grpc.sendStreamCommand('subscribeTransactions');
    await new Promise((resolve, reject) => {
      stream.on('data', () => this.update());
      stream.on('end', resolve);
      stream.on('error', reject);
      stream.on('status', status => log.info(`Transactions update: ${status}`));
    });
  }

  /**
   * Subscribe to incoming invoice payments using the grpc streaming api.
   * @return {Promise<undefined>}
   */
  async subscribeInvoices() {
    const stream = this._grpc.sendStreamCommand('subscribeInvoices');
    await new Promise((resolve, reject) => {
      stream.on('data', invoice => this._receiveInvoice(invoice));
      stream.on('end', resolve);
      stream.on('error', reject);
      stream.on('status', status => log.info(`Invoices update: ${status}`));
    });
  }

  //
  // Helper functions
  //

  async _receiveInvoice(invoice) {
    await this.update();
    if (!invoice.settled) return;
    const { computedPools, unitLabel } = this._store;
    let inv = computedPools.find(tx => tx.id === toHex(invoice.rHash));
    this._notification.display({
      type: 'success',
      msg: `Invoice success: received ${inv.amountLabel} ${unitLabel || ''}`,
      handler: () => this.select({ item: inv }),
      handlerLbl: 'View details',
    });
  }
}

export default PoolAction;
