import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { App, Events, NavController } from 'ionic-angular';
import { Logger } from '../../providers/logger/logger';

// providers
import { AppProvider } from '../app/app';
import { BwcProvider } from '../bwc/bwc';
import { PayproProvider } from '../paypro/paypro';
import { PopupProvider } from '../popup/popup';
import { ScanProvider } from '../scan/scan';

// pages
import { ImportWalletPage } from '../../pages/add/import-wallet/import-wallet';
import { JoinWalletPage } from '../../pages/add/join-wallet/join-wallet';
import { BitPayCardIntroPage } from '../../pages/integrations/bitpay-card/bitpay-card-intro/bitpay-card-intro';
import { CoinbasePage } from '../../pages/integrations/coinbase/coinbase';
import { GlideraPage } from '../../pages/integrations/glidera/glidera';
import { AmountPage } from '../../pages/send/amount/amount';
import { ConfirmPage } from '../../pages/send/confirm/confirm';

@Injectable()
export class IncomingDataProvider {
  private navCtrl: NavController;
  constructor(
    private app: App,
    private events: Events,
    private bwcProvider: BwcProvider,
    private payproProvider: PayproProvider,
    private scanProvider: ScanProvider,
    private popupProvider: PopupProvider,
    private logger: Logger,
    private appProvider: AppProvider,
    private translate: TranslateService
  ) {
    this.logger.info('IncomingDataProvider initialized.');
  }

  public showMenu(data: any): void {
    this.events.publish('incomingDataMenu.showMenu', data);
  }

  public redir(data: string): boolean {
    // TODO Injecting NavController in constructor of service fails with no provider error
    this.navCtrl = this.app.getActiveNav();

    // data extensions for Payment Protocol with non-backwards-compatible request
    if ((/^bitcoin(cash)?:\?r=[\w+]/).exec(data)) {
      let coin = 'btc';
      if (data.indexOf('bitcoincash') === 0) coin = 'bch';

      data = decodeURIComponent(data.replace(/bitcoin(cash)?:\?r=/, ''));

      this.payproProvider.getPayProDetails(data, coin).then((details) => {
        this.handlePayPro(details, coin);
      }).catch((err) => {
        this.popupProvider.ionicAlert(this.translate.instant('Error'), err);
      });

      return true;
    }

    data = this.sanitizeUri(data);
    let amount: string;
    let message: string;
    let addr: string;
    let parsed: any;
    let coin: string;

    // Bitcoin  URL
    if (this.bwcProvider.getBitcore().URI.isValid(data)) {
      this.logger.debug('Handling Bitcoin URI');
      coin = 'btc';
      parsed = this.bwcProvider.getBitcore().URI(data);
      addr = parsed.address ? parsed.address.toString() : '';
      message = parsed.message;
      amount = parsed.amount ? parsed.amount : '';

      if (parsed.r) {
        this.payproProvider.getPayProDetails(parsed.r, coin).then((details) => {
          this.handlePayPro(details, coin);
        }).catch((err: string) => {
          if (addr && amount) this.goSend(addr, amount, message, coin);
          else this.popupProvider.ionicAlert(this.translate.instant('Error'), err);
        });
      } else {
        this.goSend(addr, amount, message, coin);
      }
      return true;
      // Cash URI
    } else if (this.bwcProvider.getBitcoreCash().URI.isValid(data)) {
      this.logger.debug('Handling Bitcoin Cash URI');
      coin = 'bch';
      parsed = this.bwcProvider.getBitcoreCash().URI(data);
      addr = parsed.address ? parsed.address.toString() : '';

      // keep address in original format
      if (parsed.address && data.indexOf(addr) < 0) {
        addr = parsed.address.toCashAddress();
      };

      message = parsed.message;
      amount = parsed.amount ? parsed.amount : '';

      // paypro not yet supported on cash
      if (parsed.r) {
        this.payproProvider.getPayProDetails(parsed.r, coin).then((details: any) => {
          this.handlePayPro(details, coin);
        }).catch((err: string) => {
          if (addr && amount)
            this.goSend(addr, amount, message, coin);
          else
            this.popupProvider.ionicAlert(this.translate.instant('Error'), err);
        });
      } else {
        this.goSend(addr, amount, message, coin);
      }
      return true;

      // Cash URI with bitcoin core address version number?
    } else if (this.bwcProvider.getBitcore().URI.isValid(data.replace(/^bitcoincash:/, 'bitcoin:'))) {
      this.logger.debug('Handling bitcoincash URI with legacy address');
      coin = 'bch';
      parsed = this.bwcProvider.getBitcore().URI(data.replace(/^bitcoincash:/, 'bitcoin:'));

      let oldAddr = parsed.address ? parsed.address.toString() : '';
      if (!oldAddr) return false;

      addr = '';

      let a = this.bwcProvider.getBitcore().Address(oldAddr).toObject();
      addr = this.bwcProvider.getBitcoreCash().Address.fromObject(a).toString();

      // Translate address
      this.logger.debug('address transalated to:' + addr);
      let title = this.translate.instant('Bitcoin cash Payment');
      let msg = this.translate.instant('Payment address was translated to new Bitcoin Cash address format: {{addr}}', { addr });
      this.popupProvider.ionicConfirm(title, msg).then((res: boolean) => {
        if (!res) return false;

        message = parsed.message;
        amount = parsed.amount ? parsed.amount : '';

        // paypro not yet supported on cash
        if (parsed.r) {
          this.payproProvider.getPayProDetails(parsed.r, coin).then((details) => {
            this.handlePayPro(details, coin);
          }).catch((err) => {
            if (addr && amount)
              this.goSend(addr, amount, message, coin);
            else
              this.popupProvider.ionicAlert(this.translate.instant('Error'), err);
          });
        } else {
          this.goSend(addr, amount, message, coin);
        }
      });
      return true;
    } else if (/^https?:\/\//.test(data)) {
      // Plain URL
      this.logger.debug('Handling Plain URL');

      let coin = 'btc'; // Assume BTC

      this.payproProvider.getPayProDetails(data, coin).then((details) => {
        this.handlePayPro(details, coin);
        return true;
      }).catch(() => {
        this.showMenu({
          data,
          type: 'url'
        });
        return;
      });
      // Plain Address
    } else if (this.bwcProvider.getBitcore().Address.isValid(data, 'livenet') || this.bwcProvider.getBitcore().Address.isValid(data, 'testnet')) {
      this.logger.debug('Handling Bitcoin Plain Address');
      if (this.navCtrl.getActive().name === 'ScanPage') {
        this.showMenu({
          data,
          type: 'bitcoinAddress',
          coin: 'btc'
        });
      } else {
        let coin = 'btc';
        this.goToAmountPage(data, coin);
      }
    } else if (this.bwcProvider.getBitcoreCash().Address.isValid(data, 'livenet')) {
      this.logger.debug('Handling Bitcoin Cash Plain Address');
      if (this.navCtrl.getActive().name === 'ScanPage') {
        this.showMenu({
          data,
          type: 'bitcoinAddress',
          coin: 'bch',
        });
      } else {
        let coin = 'bch';
        this.goToAmountPage(data, coin);
      }
    } else if (data && data.indexOf(this.appProvider.info.name + '://glidera') === 0) {

      let code = this.getParameterByName('code', data);
      this.navCtrl.push(GlideraPage, { code });

      this.logger.debug('Glidera TODO');
      return true;
    } else if (data && data.indexOf(this.appProvider.info.name + '://coinbase') === 0) {

      let code = this.getParameterByName('code', data);
      this.navCtrl.push(CoinbasePage, { code });

      this.logger.debug('Coinbase TODO');
      return true;
      // BitPayCard Authentication
    } else if (data && data.indexOf(this.appProvider.info.name + '://') === 0) {

      // Disable BitPay Card
      if (!this.appProvider.info._enabledExtensions.debitcard) return false;

      // For BitPay card binding
      let secret = this.getParameterByName('secret', data);
      let email = this.getParameterByName('email', data);
      let otp = this.getParameterByName('otp', data);
      let reason = this.getParameterByName('r', data);
      switch (reason) {
        default:
        case '0':
          /* For BitPay card binding */
          this.navCtrl.parent.select(0);
          this.navCtrl.push(BitPayCardIntroPage, { secret, email, otp });
          break;
      }
      return true;

      // Join
    } else if (data && data.match(/^copay:[0-9A-HJ-NP-Za-km-z]{70,80}$/)) {
      this.navCtrl.push(JoinWalletPage, { url: data, fromScan: true })
      return true;
      // Old join
    } else if (data && data.match(/^[0-9A-HJ-NP-Za-km-z]{70,80}$/)) {
      this.navCtrl.push(JoinWalletPage, { url: data, fromScan: true })
      return true;
    } else if (data && (data.substring(0, 2) == '6P' || this.checkPrivateKey(data))) {
      this.logger.debug('Handling private key');
      this.showMenu({
        data,
        type: 'privateKey'
      });
    } else if (data && ((data.substring(0, 2) == '1|') || (data.substring(0, 2) == '2|') || (data.substring(0, 2) == '3|'))) {
      this.navCtrl.push(ImportWalletPage, { code: data, fromScan: true })
      return true;

    } else {

      if (this.navCtrl.getActive().name === 'ScanPage') {
        this.logger.debug('Handling plain text');
        this.showMenu({
          data,
          type: 'text'
        });
      }
    }
    return false;
  }

  private sanitizeUri(data: any): string {
    // Fixes when a region uses comma to separate decimals
    let regex = /[\?\&]amount=(\d+([\,\.]\d+)?)/i;
    let match = regex.exec(data);
    if (!match || match.length === 0) {
      return data;
    }
    let value = match[0].replace(',', '.');
    let newUri = data.replace(regex, value);

    // mobile devices, uris like copay://glidera
    newUri.replace('://', ':');

    return newUri;
  }

  private getParameterByName(name: string, url: string): string {
    if (!url) return;
    name = name.replace(/[\[\]]/g, "\\$&");
    let regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
      results = regex.exec(url);
    if (!results) return null;
    if (!results[2]) return '';
    return decodeURIComponent(results[2].replace(/\+/g, " "));
  }

  private checkPrivateKey(privateKey: string): boolean {
    try {
      this.bwcProvider.getBitcore().PrivateKey(privateKey, 'livenet');
    } catch (err) {
      return false;
    }
    return true;
  }

  private goSend(addr: string, amount: string, message: string, coin: string): void {
    if (amount) {
      this.navCtrl.push(ConfirmPage, {
        amount,
        toAddress: addr,
        description: message,
        coin
      });
    } else {
      this.navCtrl.push(AmountPage, {
        toAddress: addr,
        coin
      });
    }
  }

  private goToAmountPage(toAddress: string, coin: string) {
    this.navCtrl.push(AmountPage, {
      toAddress,
      coin
    });
  }

  private handlePayPro(payProDetails: any, coin?: string): void {
    let stateParams = {
      amount: payProDetails.amount,
      toAddress: payProDetails.toAddress,
      description: payProDetails.memo,
      paypro: payProDetails,
      coin
    };
    this.scanProvider.pausePreview();
    this.navCtrl.push(ConfirmPage, stateParams);
  }

}
