/**
 * @fileOverview this is the main action module where all actions are initilized
 * by injecting dependencies and then triggering startup actions when certain
 * flags on the global store are set to true.
 */

import { PREFIX_URI } from '../config';
import { when } from 'mobx';
import { Clipboard } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { nap, checkHttpStatus, toSatoshis, signMessage } from '../helper';
import store from '../store';
import AppStorage from './app-storage';
import IpcAction from './ipc';
import GrpcAction from './grpc';
import NavAction from './nav';
import WalletAction from './wallet';
import LogAction from './log';
import InfoAction from './info';
import NotificationAction from './notification';
import ChannelAction from './channel';
import InvestmentAction from './investment';
import TransactionAction from './transaction';
import DaoAction from './dao';
import PoolAction from './pool';
import PromotionAction from './promotion';
import PaymentAction from './payment';
import PoolAddLiqAction from './pool-add-liquid';
import PoolRemoveLiqAction from './pool-remove-liquid';
import PoolRedeemLiqAction from './pool-redeem-liquid';
import InvoiceAction from './invoice';
import SettingAction from './setting';
import AtplAction from './autopilot';
import Algorand from '../chains/algo/algo.js';
//import Solana  from '../chains/sol/sol.js';

//
// Inject dependencies
//

window.store = store;

store.init(); // initialize computed values

export const ipc = new IpcAction(window.electron.ipcRenderer);
export const db = new AppStorage(store, AsyncStorage);
export const log = new LogAction(store, ipc);
export const nav = new NavAction(store);
export const grpc = new GrpcAction(store, ipc);
export const notify = new NotificationAction(store, nav);
export const wallet = new WalletAction(store, grpc, db, nav, notify);
export const info = new InfoAction(store, grpc, nav, notify);
export const pool = new PoolAction(store, grpc, nav, notify);
export const dao = new DaoAction(store, grpc, nav, notify);
export const promotion = new PromotionAction(store, grpc, nav, notify);
export const investment = new InvestmentAction(store, grpc, nav, notify);
export const transaction = new TransactionAction(store, grpc, nav, notify);
export const channel = new ChannelAction(store, grpc, nav, notify);
export const invoice = new InvoiceAction(store, grpc, nav, notify, Clipboard);
export const payment = new PaymentAction(store, grpc, nav, notify, Clipboard);
export const pooladdliq = new PoolAddLiqAction(
  store,
  grpc,
  nav,
  notify,
  Clipboard
);
export const poolremoveliq = new PoolRemoveLiqAction(
  store,
  grpc,
  nav,
  notify,
  Clipboard
);
export const poolredeemliq = new PoolRedeemLiqAction(
  store,
  grpc,
  nav,
  notify,
  Clipboard
);
export const setting = new SettingAction(store, wallet, db, ipc);
export const autopilot = new AtplAction(store, grpc, db, notify);

payment.listenForUrl(ipc); // enable incoming url handler

//
// Init actions
//

db.restore(); // read user settings from disk

/**
 * Triggered after user settings are restored from disk.
 */
when(
  () => store.loaded,
  () => grpc.initUnlocker()
);

/**
 * Triggered after the wallet unlocker grpc client is initialized.
 */
when(
  () => store.unlockerReady,
  () => wallet.init()
);

/**
 * Triggered the first time the app was started e.g. to set the
 * local fiat currency only once.
 */
when(
  () => store.firstStart,
  () => setting.detectLocalCurrency()
);

/**
 * Triggered after the user's password has unlocked the wallet
 * or a user's password has been successfully reset.
 */
when(
  () => store.walletUnlocked,
  async () => {
    await nap();
    await grpc.closeUnlocker();
    await grpc.initLnd();
    await grpc.initAutopilot();
  }
);

/**
 * Triggered once the main lnd grpc client is initialized. This is when
 * the user can really begin to interact with the application and calls
 * to and from lnd can be done. The display the current state of the
 * lnd node all balances, channels and transactions are fetched.
 */
when(
  () => store.lndReady,
  () => {
    wallet.pollBalances();
    wallet.pollExchangeRate();
    channel.pollChannels();
    transaction.update();
    transaction.subscribeTransactions();
    transaction.subscribeInvoices();
    info.pollInfo();
  }
);

/**
 * Triggered once the main lnd grpc client is initialized. This is when
 * the user can really begin to interact with the application and calls
 * to and from lnd can be done. The contract chains are implemented
 */

function setChainAccount(chain, account) {
  if (!store.settings.chains[chain]) {
    throw new Error(`Invalid chain: ` + chain);
  }
  if (!account) {
    throw new Error(`Invalid seed: ` + account);
  }
  store.settings.chains[chain].account = account;
  db.save();
  console.log('INFO! ' + chain + ' account ' + account.address);
}
/*
function setChainAddress(chain, addr) {
  
    if (!store.settings.chains[chain]) {
      throw new Error(`Invalid chain: `+chain);
    }
    if (!addr) {
      throw new Error(`Invalid address: `+addr);
    }
    store.settings.chains[chain].addresses.push(addr);
    db.save();
}
*/

when(
  () => store.lndReady,
  async () => {
    console.log(store.settings.chains);
    if (!store.settings.chains.algo.account) {
      var algoAccount = await Algorand.createWallet();
      //var algoAccountInfo = await Algorand.getAccount('testnet',store.chains.algo.account.address);
      setChainAccount('algo', algoAccount);
      console.log(
        'new algo account! ' + store.settings.chains.algo.account.address
      );
    } else {
      console.log(
        'old algo account! ' + store.settings.chains.algo.account.address
      );
    }
    var accountInfo = await Algorand.getAccount(
      store.settings.chains.algo.chain,
      store.settings.chains.algo.account.address
    );
    console.log('algo account! ' + accountInfo);
    await signMessage('habari kwa dunia', grpc, notify);
    /*
    //var poolStats = await grpc.initPyface(['/data/lightning-bits/src/chains/algo/pools.py', 'bootstrap', 70232824, 0, '289:BITS', 'ALGO', 'TTNTINGI6AKN6JBHCAOXVFZN72BAMDNBTHZ3ZRQC3ILAMWONKN7XRRQ6GM' ]);
    var poolStats = await grpc.initPyface([
      '/data/lightning-bits/src/chains/algo/pools.py',
      'accState',
      'TTNTINGI6AKN6JBHCAOXVFZN72BAMDNBTHZ3ZRQC3ILAMWONKN7XRRQ6GM',
    ]);
    poolStats = poolStats
      .replace(/'/g, '"')
      .replace(/"{/g, '{')
      .slice(0, -3);
    console.log(poolStats);
    store.settings.chains.algo.account.balance =
      JSON.parse(poolStats).amount / 1000000 + ' ALGOS';
    
    if(!store.chains.sol.account){
    
    var solAccount = await Solana.createWallet();
    //var solAccountInfo = await Solana.getAccount('testnet',store.chains.sol.account.address);
    setChainAccount({chain:'sol', account:solAccount});
    
    }
    */
  }
);

/**
 * Initialize autopilot after syncing is finished and the grpc client
 * is ready
 */
/**
 * Verify that the user has a channel with enough receive capacity to
 * receive the given amount.
 * @param  {number} options.satAmount The amount to receive.
 * @return {undefined}
 */
function checkAmount({ satAmount }) {
  const { channels } = store;
  const hasInbound = channels.find(c => c.remoteBalance >= satAmount);
  if (hasInbound) {
    return;
  }
  notify.display({
    msg: "You don't have enough inbound capacity to receive this payment.",
  });
}

async function generateWebUri(amount, expiry) {
  try {
    const { settings } = store;
    const satAmount = toSatoshis(amount, settings);
    checkAmount({ satAmount });
    const response = await grpc.sendCommand('addInvoice', {
      value: satAmount,
      memo: 'web invoice v1',
      expiry: expiry,
      private: true,
    });
    var res = {};
    res.encoded = response.paymentRequest;
    res.uri = `${PREFIX_URI}${invoice.encoded}`;
    return res;
  } catch (err) {
    notify.display({ msg: 'Creating web invoice failed!', err });
  }
}

when(
  () => store.syncedToChain && store.network && store.autopilotReady,
  async () => {
    await nap();
    autopilot.init();
    if (store.balanceSatoshis < 60000) nav.goDeposit();
    if (
      store.balanceSatoshis > 25000 &&
      store.channelBalanceSatoshis < store.balanceSatoshis * 0.2 &&
      store.pendingChannels.length == 0 &&
      store.channelBalanceSatoshis < 20000
    )
      channel.connectAndOpen();
    // initialize soko timers if enabled
    if (store.settings.soko.enabled) {
      // invoice generation interval and count for multiple paypoints
      // every hour
      const webInvInt = 600000;
      const webInvCount = 2;
      const sokoInt = setInterval(async function() {
        if (!store.settings.soko.enabled) {
          clearInterval(sokoInt);
        }
        var invList = [];

        for (let i = 0; i < webInvCount; i++) {
          try {
            var inv = await generateWebUri('100', webInvInt);
            invList.push(inv.encoded);
          } catch (e) {
            console.warn(e);
            continue;
          }
        }
        const uri =
          'https://' +
          store.settings.soko.domain +
          '/updateINV/?invIDs=' +
          JSON.stringify(invList);
        try {
          var response = checkHttpStatus(await fetch(uri));
          var r = await response.json();
          console.log(r);
        } catch (err) {
          console.warn(err);
          notify.display({
            msg: 'Store offline! web invoice not updated. Disable?',
          });
          return;
        }
        //console.log(r);
        notify.display({ msg: 'SOKO Web invoice updated!' });
      }, webInvInt);
    }
  }
);
