import { HttpClient } from '@angular/common/http';
import { catchError, map, mergeMap, tap } from 'rxjs/operators';
import { BehaviorSubject, interval, Observable, of, timer } from 'rxjs';
import { Injectable } from '@angular/core';
import { GasPrice, GasPriceBN, GasPriceOneInch, GasPricePoaNetwork, GasPriceUpVest } from './gas-price.api.dto';
import { ethers } from 'ethers';

const badResponse = {
  success: false,
  fast: 0,
  instant: 0,
  standard: 0,
  slow: 0
};

@Injectable({
  providedIn: 'root'
})
export class GasPriceApiService {

  private GAS_PRICE_URL = 'http://gas-price.1inch.exchange';
  private GAS_PRICE_URL2 = 'https://gasprice.poa.network';
  private GAS_PRICE_URL3 = 'https://fees.upvest.co/estimate_eth_fees';
  private CORS_PROXY_URL = 'https://corsproxy.1inch.exchange/';

  public gasPrice = new BehaviorSubject<GasPriceBN>({
    fast: new ethers.utils.BigNumber(Math.trunc(6 * 100)).mul(1e7),
    standard: new ethers.utils.BigNumber(Math.trunc(11 * 100)).mul(1e7),
    instant: new ethers.utils.BigNumber(Math.trunc(21 * 100)).mul(1e7)
  });

  constructor(private http: HttpClient) {
    timer(0, 30000).pipe(
      mergeMap(() => this.getGasPrice()),
      tap((gasPrice: GasPrice) => {

        const fast = formatGasPrice(gasPrice.fast);
        const instant = formatGasPrice(gasPrice.instant);
        const standard = formatGasPrice(gasPrice.standard);

        this.gasPrice.next({
          fast: ethers.utils.bigNumberify(Math.trunc(fast)),
          standard: ethers.utils.bigNumberify(Math.trunc(standard)),
          instant: ethers.utils.bigNumberify(Math.trunc(instant))
        });
      })
    ).subscribe();
  }

  private getGasPrice(): Observable<GasPrice> {

    const upVest$ = this.getGasPriceUpVest$();
    const oneInch$ = this.getGasPriceOneInch$();
    const poa$ = this.getGasPricePoa$();

    return upVest$.pipe(
      mergeMap((data: GasPrice) => {

        if (data.success) {
          return of(data);
        }

        return oneInch$.pipe(
          mergeMap((oneInchData: GasPrice) => (
            data.success ? of(oneInchData) : poa$
          ))
        );
      })
    );
  }

  private getGasPriceOneInch$(): Observable<GasPrice> {
    return this.http.get<GasPriceOneInch>(this.CORS_PROXY_URL + this.GAS_PRICE_URL).pipe(
      map((data: GasPriceOneInch) => {

        if (!data.health) {
          return { ...badResponse };
        }
        return {
          success: true,
          fast: data.fast,
          instant: data.instant,
          standard: data.standard,
          slow: data.slow
        };
      }),
      catchError(() => of({ ...badResponse }))
    );
  }

  private getGasPricePoa$(): Observable<GasPrice> {
    return this.http.get<GasPricePoaNetwork>(this.GAS_PRICE_URL2).pipe(
      map((data: GasPricePoaNetwork) => {

        if (!data.health) {
          return { ...badResponse };
        }
        return {
          success: true,
          fast: data.fast,
          instant: data.instant,
          standard: data.standard,
          slow: data.slow
        };
      }),
      catchError(() => of({ ...badResponse }))
    );
  }

  private getGasPriceUpVest$(): Observable<GasPrice> {
    return this.http.get<GasPriceUpVest>(this.CORS_PROXY_URL + this.GAS_PRICE_URL3).pipe(
      map((data: GasPriceUpVest) => {

        if (!data.success) {
          return { ...badResponse };
        }
        return {
          success: true,
          fast: data.estimates.fast,
          instant: data.estimates.fastest,
          standard: data.estimates.medium,
          slow: data.estimates.slow
        };
      }),
      catchError(() => of({ ...badResponse }))
    );
  }
}

function formatGasPrice(gasPrice: number): number {
  return gasPrice * 100 * 1e9 / 100;
}