import { Component } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { ModalController, NavController, NavParams } from 'ionic-angular';
import * as _ from 'lodash';
import * as moment from 'moment';
import { Logger } from '../../../../providers/logger/logger';

// Pages
import { FinishModalPage } from '../../../finish/finish';
import { ShapeshiftPage } from '../shapeshift';

// Providers
import { BwcErrorProvider } from '../../../../providers/bwc-error/bwc-error';
import { BwcProvider } from '../../../../providers/bwc/bwc';
import { ConfigProvider } from '../../../../providers/config/config';
import { ExternalLinkProvider } from '../../../../providers/external-link/external-link';
import { OnGoingProcessProvider } from "../../../../providers/on-going-process/on-going-process";
import { PlatformProvider } from '../../../../providers/platform/platform';
import { PopupProvider } from '../../../../providers/popup/popup';
import { ProfileProvider } from '../../../../providers/profile/profile';
import { ShapeshiftProvider } from '../../../../providers/shapeshift/shapeshift';
import { TxFormatProvider } from '../../../../providers/tx-format/tx-format';
import { WalletProvider } from '../../../../providers/wallet/wallet';

@Component({
  selector: 'page-shapeshift-confirm',
  templateUrl: 'shapeshift-confirm.html',
})
export class ShapeshiftConfirmPage {

  private amount: number;
  private currency: string;
  private rateUnit: number;
  private fromWalletId: string;
  private toWalletId: string;
  private createdTx: any;
  private message: string;
  private configWallet: any;
  private bitcore: any;
  private bitcoreCash: any;
  private useSendMax: boolean;

  public currencyIsoCode: string;
  public isCordova: boolean;
  public toWallet: any;
  public fromWallet: any;
  public fiatWithdrawal: number;
  public fiatAmount: number;
  public fiatFee: number;
  public fiatTotalAmount: number;
  public shapeInfo: any;
  public amountUnitStr: string;
  public feeRatePerStr: string;
  public amountStr: string;
  public withdrawalStr: string;
  public feeStr: string;
  public totalAmountStr: string;
  public txSent: any;
  public network: string;

  constructor(
    private bwcProvider: BwcProvider,
    private bwcErrorProvider: BwcErrorProvider,
    private configProvider: ConfigProvider,
    private externalLinkProvider: ExternalLinkProvider,
    private onGoingProcessProvider: OnGoingProcessProvider,
    private logger: Logger,
    private navCtrl: NavController,
    private navParams: NavParams,
    private platformProvider: PlatformProvider,
    private popupProvider: PopupProvider,
    private profileProvider: ProfileProvider,
    private shapeshiftProvider: ShapeshiftProvider,
    private txFormatProvider: TxFormatProvider,
    private walletProvider: WalletProvider,
    private modalCtrl: ModalController,
    private translate: TranslateService
  ) {
    this.configWallet = this.configProvider.get().wallet;
    this.currencyIsoCode = 'USD';  // Only USD
    this.isCordova = this.platformProvider.isCordova;
    this.bitcore = this.bwcProvider.getBitcore();
    this.bitcoreCash = this.bwcProvider.getBitcoreCash();

    this.useSendMax = this.navParams.data.useSendMax ? true : false;

    this.amount = this.navParams.data.amount;
    this.currency = this.navParams.data.currency;
    this.fromWalletId = this.navParams.data.id;
    this.toWalletId = this.navParams.data.toWalletId;

    this.network = this.shapeshiftProvider.getNetwork();
    this.fromWallet = this.profileProvider.getWallet(this.fromWalletId);
    this.toWallet = this.profileProvider.getWallet(this.toWalletId);

    if (_.isEmpty(this.fromWallet) || _.isEmpty(this.toWallet)) {
      this.showErrorAndBack(null, this.translate.instant('No wallet found'));
      return;
    }

    this.shapeshiftProvider.getLimit(this.getCoinPair(), (err: any, lim: any) => {
      let min = Number(lim.min);
      let max = Number(lim.limit);

      if (this.useSendMax) this.amount = max;

      let amountNumber = Number(this.amount);

      if (amountNumber < min) {
        let message = 'Minimum amount required is ' + min; // TODO: translate
        this.showErrorAndBack(null, message);
        return;
      }
      if (amountNumber > max) {
        let message = 'Maximum amount allowed is ' + max; // TODO: translate
        this.showErrorAndBack(null, message);
        return;
      }
      this.createShift();
    });
  }

  ionViewDidLoad() {
    this.logger.info('ionViewDidLoad ShapeshiftConfirmPage');
  }

  public openExternalLink(url: string) {
    this.externalLinkProvider.open(url);
  };

  private showErrorAndBack(title: string, msg: any) {
    title = title ? title : this.translate.instant('Error');
    this.logger.error(msg);
    msg = (msg && msg.errors) ? msg.errors[0].message : msg;
    this.popupProvider.ionicAlert(title, msg).then(() => {
      this.navCtrl.pop();
    });
  };

  private publishAndSign(wallet: any, txp: any): Promise<any> {
    return new Promise((resolve, reject) => {
      if (!wallet.canSign() && !wallet.isPrivKeyExternal()) {
        let err = this.translate.instant('No signing proposal: No private key');
        this.logger.info(err);
        return reject(err);
      }

      this.walletProvider.publishAndSign(wallet, txp).then((txp: any) => {
        this.onGoingProcessProvider.clear();
        return resolve(txp);
      }).catch((err: any) => {
        this.onGoingProcessProvider.clear();

        return reject(err);
      });
    });
  }

  private satToFiat(coin: string, sat: number, isoCode: string): Promise<any> {
    return new Promise((resolve, reject) => {
      this.txFormatProvider.toFiat(coin, sat, isoCode).then((value: any) => {
        return resolve(value);
      });
    });
  }

  private setFiatTotalAmount(amountSat: number, feeSat: number, withdrawalSat: number) {
    this.satToFiat(this.toWallet.coin, withdrawalSat, this.currencyIsoCode, ).then((w: any) => {
      this.fiatWithdrawal = Number(w);

      this.satToFiat(this.fromWallet.coin, amountSat, this.currencyIsoCode, ).then((a: any) => {
        this.fiatAmount = Number(a);

        this.satToFiat(this.fromWallet.coin, feeSat, this.currencyIsoCode, ).then((i: any) => {
          this.fiatFee = Number(i);

          this.fiatTotalAmount = this.fiatAmount + this.fiatFee;
        });
      });
    });
  }

  private saveShapeshiftData(): void {
    let address = this.shapeInfo.deposit;
    let withdrawal = this.shapeInfo.withdrawal;
    let now = moment().unix() * 1000;

    this.shapeshiftProvider.getStatus(address, (err: any, st: any) => {
      let newData = {
        address,
        withdrawal,
        date: now,
        amount: this.amountStr,
        rate: this.rateUnit + ' ' + this.toWallet.coin.toUpperCase() + ' per ' + this.fromWallet.coin.toUpperCase(),
        title: this.fromWallet.coin.toUpperCase() + ' to ' + this.toWallet.coin.toUpperCase(),
        // From ShapeShift
        status: st.status,
        transaction: st.transaction || null, // Transaction ID of coin sent to withdrawal address
        incomingCoin: st.incomingCoin || null, // Amount deposited
        incomingType: st.incomingType || null, // Coin type of deposit
        outgoingCoin: st.outgoingCoin || null, // Amount sent to withdrawal address
        outgoingType: st.outgoingType || null, // Coin type of withdrawal
      };

      this.shapeshiftProvider.saveShapeshift(newData, null, (err: any) => {
        this.logger.debug("Saved shift with status: " + newData.status);
        this.openFinishModal();
      });
    });
  }

  private createTx(wallet: any, toAddress: string): Promise<any> {
    return new Promise((resolve, reject) => {
      let parsedAmount = this.txFormatProvider.parseAmount(wallet.coin, this.amount, this.currency);
      this.amountUnitStr = parsedAmount.amountUnitStr;

      this.message = 'ShapeShift: ' + this.fromWallet.coin.toUpperCase() + ' to ' + this.toWallet.coin.toUpperCase();
      let outputs = [];

      outputs.push({
        'toAddress': toAddress,
        'amount': parsedAmount.amountSat,
        'message': this.message
      });

      let txp = {
        toAddress,
        amount: parsedAmount.amountSat,
        outputs,
        message: this.message,
        excludeUnconfirmedUtxos: this.configWallet.spendUnconfirmed ? false : true,
        feeLevel: this.configWallet.settings.feeLevel || 'normal',
        customData: {
          'shapeShift': toAddress
        }
      };

      this.walletProvider.createTx(wallet, txp).then((ctxp: any) => {
        return resolve(ctxp);
      }).catch((err: any) => {
        return reject({
          title: this.translate.instant('Could not create transaction'),
          message: this.bwcErrorProvider.msg(err)
        });
      });
    });
  }

  private getLegacyAddressFormat(addr: string, coin: string): string {
    if (coin == 'btc') return addr;
    let a = this.bitcoreCash.Address(addr).toObject();
    return this.bitcore.Address.fromObject(a).toString();
  }

  private getNewAddressFormat(addr: string, coin: string): string {
    if (coin == 'btc') return addr;
    let a = this.bitcore.Address(addr).toObject();
    return this.bitcoreCash.Address.fromObject(a).toString();
  }

  private getCoinPair(): string {
    return this.fromWallet.coin + '_' + this.toWallet.coin;
  }

  private createShift(): void {
    this.onGoingProcessProvider.set('connectingShapeshift');

    this.walletProvider.getAddress(this.toWallet, false).then((withdrawalAddress: string) => {
      withdrawalAddress = this.getLegacyAddressFormat(withdrawalAddress, this.toWallet.coin);

      this.walletProvider.getAddress(this.fromWallet, false).then((returnAddress: string) => {
        returnAddress = this.getLegacyAddressFormat(returnAddress, this.fromWallet.coin);

        let data = {
          withdrawal: withdrawalAddress,
          pair: this.getCoinPair(),
          returnAddress
        }
        this.shapeshiftProvider.shift(data, (err: any, shapeData: any) => {
          if (err || shapeData.error) {
            this.onGoingProcessProvider.clear();
            this.showErrorAndBack(null, err || shapeData.error);
            return;
          }

          let toAddress = this.getNewAddressFormat(shapeData.deposit, this.fromWallet.coin);

          this.createTx(this.fromWallet, toAddress).then((ctxp: any) => {
            // Save in memory
            this.createdTx = ctxp;
            this.shapeInfo = shapeData;

            this.shapeshiftProvider.getRate(this.getCoinPair(), (err: any, r: any) => {
              this.onGoingProcessProvider.clear();
              this.rateUnit = r.rate;
              let amountUnit = this.txFormatProvider.satToUnit(ctxp.amount);
              let withdrawalSat = Number((this.rateUnit * amountUnit * 100000000).toFixed());

              // Fee rate
              let per = (ctxp.fee / (ctxp.amount + ctxp.fee) * 100);
              this.feeRatePerStr = per.toFixed(2) + '%';

              // Amount + Unit
              this.amountStr = this.txFormatProvider.formatAmountStr(this.fromWallet.coin, ctxp.amount);
              this.withdrawalStr = this.txFormatProvider.formatAmountStr(this.toWallet.coin, withdrawalSat);
              this.feeStr = this.txFormatProvider.formatAmountStr(this.fromWallet.coin, ctxp.fee);
              this.totalAmountStr = this.txFormatProvider.formatAmountStr(this.fromWallet.coin, ctxp.amount + ctxp.fee);

              // Convert to fiat
              this.setFiatTotalAmount(ctxp.amount, ctxp.fee, withdrawalSat);
            });
          }).catch((err: any) => {
            this.onGoingProcessProvider.clear();
            this.showErrorAndBack(err.title, err.message);
            return;
          });
        });
      }).catch((err: any) => {
        this.onGoingProcessProvider.clear();
        this.showErrorAndBack(null, 'Could not get address');
        return;
      });
    }).catch((err: any) => {
      this.onGoingProcessProvider.clear();
      this.showErrorAndBack(null, 'Could not get address');
      return;
    });
  }

  public confirmTx(): void {
    if (!this.createdTx) {
      this.showErrorAndBack(null, this.translate.instant('Transaction has not been created'));
      return;
    }
    let fromCoin = this.fromWallet.coin.toUpperCase();
    let toCoin = this.toWallet.coin.toUpperCase();
    let title = 'Confirm to shift ' + fromCoin + ' to ' + toCoin; // TODO: translate
    let okText = this.translate.instant('OK');
    let cancelText = this.translate.instant('Cancel');
    this.popupProvider.ionicConfirm(title, '', okText, cancelText).then((ok: any) => {
      if (!ok) {
        return;
      }

      this.publishAndSign(this.fromWallet, this.createdTx).then((txSent: any) => {
        this.txSent = txSent;
        this.saveShapeshiftData();
      }).catch((err: any) => {
        this.logger.error(err);
        this.showErrorAndBack(null, this.translate.instant('Could not send transaction'));
        return;
      });
    });
  };

  private openFinishModal(): void {
    let finishText = 'Transaction Sent';
    let modal = this.modalCtrl.create(FinishModalPage, { finishText }, { showBackdrop: true, enableBackdropDismiss: false });
    modal.present();
    modal.onDidDismiss(() => {
      this.navCtrl.popToRoot({ animate: false });
      this.navCtrl.push(ShapeshiftPage);
    });
  }

}
