
import BigNumber from 'bignumber.js';

import Promisify from '../../utils/Promisify';
import { getLatestABI } from '../../services/AbiService';
import Mutex from '../../utils/MutexChecker';
import { Derivative } from '../Derivative';
import Services from '../../Services';
import { IndexProductStatus, DerivativeProviders, DerivativeStatus } from '../../interfaces/Enums';

export class RebalanceService extends Services {
  private derivativeFunctions: Derivative;
  private productAddress;

  constructor(productAddress: string) {
    super();
    this.productAddress = productAddress;
    this.derivativeFunctions = new Derivative(productAddress);
  }

  public async canRebalance(): Promise<boolean> {
    const mutex = new Mutex(this.productAddress);
    const mutexResult = await mutex.checkMutex(IndexProductStatus.REBALANCING);
    const lockerResult = await mutex.checkLocker(DerivativeProviders.REBALANCE);

    if (!mutexResult.flag || mutexResult.productStatus === IndexProductStatus.AVAILABLE && !lockerResult.flag) {
      return false;
    }

    if ((await this.derivativeFunctions.getStatus()) === DerivativeStatus.Closed) {
      return false;
    }

    const rebalanceAddress = await this.derivativeFunctions.getComponentAddress(DerivativeProviders.REBALANCE);

    // Check whether or not the contract needs to be rebalanced based on the deltaPercentage.
    // If the change is not big enough, rebalance will not be called.
    const deltaPercentage = await Promisify<BigNumber>((cb) =>
      this.rpcService.nativeContract.rebalanceDeltaPercentage(cb),
    );

    if (!await this.checkProviderIfShouldRebalance(
      deltaPercentage,
      this.productAddress,
      rebalanceAddress)
    ) {
      return false;
    }
    return true;
  }

  public async checkProviderIfShouldRebalance(
    rebalancePercentage: BigNumber, productAddress: string, rebalanceProviderAddress: string): Promise<BigNumber> {
    const contract = await this.rpcService.contract(await getLatestABI('REBALANCE_ABI'), rebalanceProviderAddress);
    const result = await Promisify<BigNumber>(async (cb) => (
      await contract.needsRebalance.call(rebalancePercentage.toNumber(), productAddress, cb)));
    return result;
  }

  public getRebalanceTxData() {
    return this.rpcService.nativeContract.rebalance.getData();
  }

}
