import { BatchId, Utils } from '@ethersphere/bee-js'
import { Dates, Numbers } from 'cafe-utility'
import chalk from 'chalk'
import { LeafCommand, Option } from 'furious-commander'
import { exit } from 'process'
import { isChainStateReady } from '../../utils/chainsync'
import { createSpinner } from '../../utils/spinner'
import { createKeyValue } from '../../utils/text'
import { VerbosityLevel } from '../root-command/command-log'
import { StampCommand } from './stamp-command'

export class Buy extends StampCommand implements LeafCommand {
  public readonly name = 'buy'

  public readonly description = 'Buy postage stamp based on depth and amount'

  @Option({
    key: 'depth',
    description: 'Depth of the postage stamp',
    type: 'number',
    required: true,
    minimum: 17,
    maximum: 255,
  })
  public depth!: number

  @Option({
    key: 'amount',
    description: 'Value per chunk in PLUR, deprecates over time with new blocks mined',
    type: 'bigint',
    required: true,
    minimum: 1,
  })
  public amount!: bigint

  @Option({
    key: 'gas-price',
    description: 'Gas price of the transaction in wei',
    type: 'bigint',
    minimum: 0,
  })
  public gasPrice!: bigint

  @Option({ key: 'immutable', description: 'Disable stamp reuse', type: 'boolean', default: true })
  public immutable!: boolean

  @Option({ key: 'label', description: 'Label of the postage stamp' })
  public label!: string

  @Option({
    key: 'wait-usable',
    description: 'Wait until the postage stamp becomes usable',
    type: 'boolean',
    default: true,
  })
  public waitUsable!: boolean

  // CLASS FIELDS
  public postageBatchId!: BatchId

  public async run(): Promise<void> {
    super.init()

    if (!(await isChainStateReady(this.bee))) {
      this.console.error('Synchronization with the blockchain is not yet complete.')
      this.console.error('Please wait until the Bee is fully synced before buying a postage stamp.')
      this.console.error('You can check the synchronization status with the "status" command.')

      return
    }

    const chainState = await this.bee.getChainState()
    const minimumAmount = BigInt(chainState.currentPrice) * 17280n

    if (minimumAmount >= this.amount) {
      this.console.error('The amount is too low. The minimum amount is', (minimumAmount + 1n).toString())

      return
    }

    const estimatedCost = Utils.getStampCost(this.depth, BigInt(this.amount))
    const { bzzBalance } = await this.bee.getWalletBalance()

    if (!this.dev && estimatedCost.gt(bzzBalance)) {
      this.console.error('You do not have enough BZZ to create this postage stamp.')
      this.console.error(`Estimated cost: ${estimatedCost.toDecimalString()} xBZZ`)
      this.console.error(`Available balance: ${bzzBalance.toDecimalString()} xBZZ`)

      this.console.log('')
      this.console.log('Visit the following link to learn how to fund your Bee node:')
      this.console.log(chalk.blue('https://docs.ethswarm.org/docs/bee/installation/fund-your-node/'))

      exit(1)
    }

    const estimatedCapacity = Numbers.convertBytes(Utils.getStampEffectiveBytes(this.depth))
    const estimatedTtl = Utils.getStampDuration(BigInt(this.amount), Number(chainState.currentPrice), 5)

    this.console.log(createKeyValue('Estimated cost', `${estimatedCost.toDecimalString()} xBZZ`))
    this.console.log(createKeyValue('Estimated capacity', estimatedCapacity))
    this.console.log(createKeyValue('Estimated TTL', Dates.secondsToHumanTime(estimatedTtl.toSeconds())))
    this.console.log(createKeyValue('Type', this.immutable ? 'Immutable' : 'Mutable'))

    if (this.immutable) {
      this.console.info('At full capacity, an immutable stamp no longer allows new content uploads.')
    } else {
      this.console.info('At full capacity, a mutable stamp allows new content uploads, but overwrites old content.')
    }

    if (!this.quiet && !this.yes) {
      this.yes = await this.console.confirm('Confirm the purchase')
    }

    if (!this.yes && !this.quiet) {
      return
    }

    const spinner = createSpinner('Creating postage batch. This may take up to 5 minutes.')

    if (this.verbosity !== VerbosityLevel.Quiet && !this.curl) {
      spinner.start()
    }

    try {
      const batchId = await this.bee.createPostageBatch(this.amount.toString(), this.depth, {
        label: this.label,
        gasPrice: this.gasPrice?.toString(),
        immutableFlag: this.immutable,
        waitForUsable: this.waitUsable === false ? false : true,
      })
      spinner.stop()
      this.console.quiet(batchId.toHex())
      this.console.log(createKeyValue('Stamp ID', batchId.toHex()))
      this.postageBatchId = batchId
    } finally {
      spinner.stop()
    }
  }
}
