import { merge } from 'lodash'
import { rpc, wallet } from '@cityofzion/neon-core'
import { TMNAPI, NeoInterface } from './api'
import { MintType } from "./interface";
import { sc } from "@cityofzion/neon-js";
import fs from "fs";

const DEFAULT_OPTIONS: TMNWrapperOptions = {
  node: 'http://localhost:50012',
  scriptHash: '0x10c3d1730b957139e81941d036ef5c6699c320b5'
}

export interface TMNWrapperOptions {
  node?: string
  scriptHash?: string
}

export class TMN {
  private options: TMNWrapperOptions
  private networkMagic: number = -1

  constructor(options: TMNWrapperOptions = {}) {
    this.options = merge({}, DEFAULT_OPTIONS, options)
  }

  async init() {
    const getVersionRes = await this.node.getVersion()
    this.networkMagic = getVersionRes.protocol.network
  }

  get node(): rpc.RPCClient {
    if (this.options.node) {
      return new rpc.RPCClient(this.options.node)
    }
    throw new Error('no node selected!')
  }

  get scriptHash() : string {
    if (this.options.scriptHash) {
      return this.options.scriptHash
    }
    throw new Error('node scripthash defined')
  }

  async balanceOf(address: string, signer?: wallet.Account): Promise<number> {
    return TMNAPI.balanceOf(this.node.url, this.networkMagic, this.scriptHash, address, signer)
  }

  async decimals(signer?: wallet.Account): Promise<number> {
    return TMNAPI.decimals(this.node.url, this.networkMagic, this.scriptHash, signer)
  }

  async deploy(signer: wallet.Account): Promise<number> {
    return TMNAPI.deploy(this.node.url, this.networkMagic, this.scriptHash, signer)
  }

  async getUserJSON(address: string, signer?: wallet.Account): Promise<any> {
    return TMNAPI.getUserJSON(this.node.url, this.networkMagic, this.scriptHash, address, signer)
  }

  async mint(target: MintType, signer: wallet.Account): Promise<string> {
    return TMNAPI.mint(this.node.url, this.networkMagic, this.scriptHash, target, signer)
  }

  async mintBatch(targets: MintType[], signer: wallet.Account): Promise<string> {
    return TMNAPI.mintBatch(this.node.url, this.networkMagic, this.scriptHash, targets, signer)
  }

  async setUserPermission(address: string, permission: string, value: boolean, signer: wallet.Account): Promise<string> {
    return TMNAPI.setUserPermission(this.node.url, this.networkMagic, this.scriptHash, address, permission, value, signer)
  }

  async symbol(signer?: wallet.Account): Promise<string> {
    return TMNAPI.symbol(this.node.url, this.networkMagic, this.scriptHash, signer)
  }

  async totalAccounts(signer?: wallet.Account): Promise<string | number> {
    return TMNAPI.totalAccounts(this.node.url, this.networkMagic, this.scriptHash, signer)
  }

  async totalSupply(signer?: wallet.Account): Promise<number> {
    return TMNAPI.totalSupply(this.node.url, this.networkMagic, this.scriptHash, signer)
  }

  async transfer(fromAccount: wallet.Account, toAddress: string, amount: number): Promise<boolean> {
    return TMNAPI.transfer(this.node.url, this.networkMagic, this.scriptHash, fromAccount.address, toAddress, amount, fromAccount)
  }

  async update(script: string, manifest: string, signer: wallet.Account): Promise<string | undefined> {
    return TMNAPI.update(this.node.url, this.networkMagic, this.scriptHash, script, manifest, '', signer)
  }

  async arbitraryTransfer(scriptHash: string, fromAccount: wallet.Account, toAddress: string, amount: number): Promise<string | undefined> {
    const params = [
      sc.ContractParam.hash160(fromAccount.address),
      sc.ContractParam.hash160(toAddress),
      sc.ContractParam.integer(amount),
      sc.ContractParam.any()
    ]
    return NeoInterface.publishInvoke(
      this.node.url,
      this.networkMagic,
      scriptHash,
      "transfer",
      params,
      fromAccount
    );
  }
}
