import { ethers } from 'ethers'
import { CONSTANTS } from '@depay/web3-constants'

class Transaction {

  constructor({ blockchain, from, to, value, api, method, params, sent, confirmed, failed }) {

    // required
    this.blockchain = blockchain
    this.from = from
    this.to = to
    this.value = Transaction.bigNumberify(value, blockchain)?.toString()

    // optional
    this.api = api
    this.method = method
    this.params = params
    this.sent = sent
    this.confirmed = confirmed
    this.failed = failed

    // internal
    this._confirmed = false
    this._failed = false
  }

  async prepare({ wallet }) {
    this.from = await wallet.account()
  }

  static bigNumberify(value, blockchain) {
    if (typeof value === 'number') {
      return ethers.utils.parseUnits(value.toString(), CONSTANTS[blockchain].DECIMALS)
    } else if (value && value.toString) {
      return ethers.BigNumber.from(value.toString())
    } else {
      return value
    }
  }

  getContractArguments() {
    let fragment = this.getContract().interface.fragments.find((fragment) => {
      return fragment.name == this.method
    })

    if(this.params instanceof Array) {
      return this.params
    } else if (this.params instanceof Object) {
      return fragment.inputs.map((input) => {
        return this.params[input.name]
      })
    } else {
      throw 'Contract params have wrong type!'
    }
  }

  getContract() {
    return new ethers.Contract(this.to, this.api)
  }

  async getData() {
    let populatedTransaction = await this.getContract().populateTransaction[this.method].apply(
      null, this.getContractArguments()
    )
    return populatedTransaction.data
  }

  confirmation() {
    if (this._confirmed) {
      return Promise.resolve(this)
    }
    return new Promise((resolve, reject) => {
      let originalConfirmed = this.confirmed
      this.confirmed = () => {
        if (originalConfirmed) originalConfirmed(this)
        resolve(this)
      }
    })
  }

  failure() {
    if (this._failed) {
      return Promise.resolve(this)
    }
    return new Promise((resolve, reject) => {
      let originalFailed = this.failed
      this.failed = () => {
        if (originalFailed) originalFailed(this)
        resolve(this)
      }
    })
  }
}

export {
  Transaction
}