import {HttpClient} from "@angular/common/http";
import {Injectable} from "@angular/core";
import * as bitcoreLib from "@tecracoin/bitcore-lib";
import * as bitcoreLibCash from "@tecracoin/bitcore-lib-cash";
// import * as bitcoreLibTcr from "@tecracoin/bitcore-lib-tcr";
import * as _ from "lodash";
import {Observable} from "rxjs";
import {map} from "rxjs/operators";
import {ApiProvider, ChainNetwork} from "../api/api";

@Injectable()
export class SearchProvider {
  private config: ChainNetwork;
  private apiURL: string;

  constructor(
    private apiProvider: ApiProvider,
    private httpClient: HttpClient
  ) {
  }

  public search(
    input: string,
    type: string,
    chainNetwork: ChainNetwork
  ): Observable<any> {
    this.apiURL = `${this.apiProvider.getUrlPrefix()}/${chainNetwork.chain}/${
      chainNetwork.network
    }`;

    switch (type) {
      case 'blockOrTx':
        return Observable.forkJoin(
          this.searchBlock(input).catch(err => Observable.of(err)),
          this.searchTx(input).catch(err => Observable.of(err))
        );
      case 'addr':
        return this.searchAddr(input);
    }
  }

  private searchBlock(block: string): Observable<{ block: any }> {
    return this.httpClient
      .get<{ block: any }>(`${this.apiURL}/block/${block}`)
      .pipe(map(res => ({block: res})));
  }

  private searchTx(txid: string): Observable<{ tx: any }> {
    return this.httpClient
      .get<{ tx: any }>(`${this.apiURL}/tx/${txid}`)
      .pipe(map(res => ({tx: res})));
  }

  private searchAddr(addr: string): Observable<{ addr: any }> {
    const address = this.extractAddress(addr);
    return this.httpClient
      .get<{ addr: any }>(`${this.apiURL}/address/${address}`)
      .pipe(map(res => ({addr: res})));
  }

  public isInputValid(inputValue) {
    if (this.isValidBlockOrTx(inputValue)) {
      return {isValid: true, type: 'blockOrTx'};
    } else if (this.isValidAddress(inputValue)) {
      return {isValid: true, type: 'addr'};
    } else if (this.isValidBlockIndex(inputValue)) {
      return {isValid: true, type: 'blockOrTx'};
    } else {
      return {isValid: false, type: 'invalid'};
    }
  }

  private isValidBlockOrTx(inputValue): boolean {
    const regexp = /^[0-9a-fA-F]{64}$/;
    if (regexp.test(inputValue)) {
      return true;
    } else {
      return false;
    }
  }

  private isValidAddress(inputValue): boolean {
    this.config = this.apiProvider.getConfig();
    const coin = this.config.chain;
    const network = this.config.network;
    const addr = this.extractAddress(inputValue);
    if (addr.charAt(0) === 'C' || addr.charAt(0) === 'H') {
      return false;
    } else if (coin.toLowerCase() === 'btc' && network === 'mainnet') {
      return this.isValidBitcoinMainnetAddress(addr);
    } else if (coin.toLowerCase() === 'btc' && network === 'testnet') {
      return this.isValidBitcoinTestnetAddress(addr);
    } else if (coin.toLowerCase() === 'bch' && network === 'mainnet') {
      return (
        this.isValidBitcoinCashMainnetAddress(addr) ||
        this.isValidBitcoinCashLegacyMainnetAddress(addr)
      );
    } else if (coin.toLowerCase() === 'tcr' && network === 'mainnet') {
      return this.isValidTcrMainnetAddress(addr);
    } else if (coin.toLowerCase() === 'tcr' && network === 'testnet') {
      return this.isValidTcrTestnetAddress(addr);
    }
  }

  private isValidBitcoinMainnetAddress(data: string): boolean {
    return !!bitcoreLib.Address.isValid(data, 'mainnet');
  }

  private isValidBitcoinTestnetAddress(data: string): boolean {
    return !!bitcoreLib.Address.isValid(data, 'testnet');
  }

  private isValidBitcoinCashLegacyMainnetAddress(data: string): boolean {
    return !!bitcoreLib.Address.isValid(data, 'mainnet');
  }

  private isValidBitcoinCashMainnetAddress(data: string): boolean {
    return !!bitcoreLibCash.Address.isValid(data, 'mainnet');
  }

  private isValidTcrMainnetAddress(data: string): boolean {
    // We dont check addresses validity on frontend
    return true; // !!bitcoreLibTcr.Address.isValid(data, 'mainnet');
  }

  private isValidTcrTestnetAddress(data: string): boolean {
    // We dont check addresses validity on frontend
    return true; // !!bitcoreLibTcr.Address.isValid(data, 'testnet');
  }

  private isValidBlockIndex(inputValue): boolean {
    return isFinite(inputValue);
  }

  private extractAddress(address: string): string {
    const extractedAddress = address
      .replace(/^(bitcoincash:|bchtest:|bitcoin:|tecracoin:)/i, '')
      .replace(/\?.*/, '');
    return extractedAddress || address;
  }
}
