import {sc} from "@cityofzion/neon-js"
import {wallet} from "@cityofzion/neon-core"
import {MintType} from "../interface";
import {formatter, variableInvoke} from "../helpers";

export class TMNAPI {

  /**
   * Returns the token symbol
   * @param node
   * @param networkMagic
   * @param contractHash
   */
  static async symbol(
    node: string,
    networkMagic: number,
    contractHash: string,
    signer?: wallet.Account
  ): Promise<string> {
    const method = "symbol";

    const res = await variableInvoke(node, networkMagic, contractHash, method, [], signer)
    if (signer) {
      return res
    }
    return formatter(res[0]);
  }

  /**
   * Returns the decimals of the token
   * @param node
   * @param networkMagic
   * @param contractHash
   */
  static async decimals(
    node: string,
    networkMagic: number,
    contractHash: string,
    signer?: wallet.Account
  ): Promise<number> {
    const method = "decimals";

    const res = await variableInvoke(node, networkMagic, contractHash, method, [], signer)
    if (signer) {
      return res
    }
    return formatter(res[0]);
  }

  /**
   * Returns the total supply of the token
   * @param node
   * @param networkMagic
   * @param contractHash
   */
  static async totalSupply(
    node: string,
    networkMagic: number,
    contractHash: string,
    signer?: wallet.Account
  ): Promise<number> {
    const method = "totalSupply";

    const res = await variableInvoke(node, networkMagic, contractHash, method, [], signer)
    if (signer) {
      return res
    }
    return formatter(res[0]);
  }

  /**
   * Returns the balance of an account
   * @param node
   * @param networkMagic
   * @param contractHash
   * @param address
   */
  static async balanceOf(
    node: string,
    networkMagic: number,
    contractHash: string,
    address: string,
    signer?: wallet.Account
  ): Promise<number> {
    const method = "balanceOf";

    const params = [sc.ContractParam.hash160(address)];

    const res = await variableInvoke(node, networkMagic, contractHash, method, params, signer)
    if (signer) {
      return res
    }
    return formatter(res[0]);
  }

  /**
   * Executes a transfer invocation for the tokens.
   * @param node
   * @param networkMagic
   * @param contractHash
   * @param fromAddress
   * @param toAddress
   * @param amount
   * @param signer
   * @param data
   */
  static async transfer(
    node: string,
    networkMagic: number,
    contractHash: string,
    fromAddress: string,
    toAddress: string,
    amount: number,
    signer: wallet.Account,
    data?: any
  ): Promise<any> {
    const method = "transfer";
    const params = [
      sc.ContractParam.hash160(fromAddress),
      sc.ContractParam.hash160(toAddress),
      amount,
      data,
    ];

    const res = await variableInvoke(node, networkMagic, contractHash, method, params, signer)
    if (signer) {
      return res
    }
    return formatter(res);
  }

  /**
   * Creates a new NEP11 token on the contract
   * @param node
   * @param networkMagic
   * @param contractHash
   * @param address The address to mint to.
   * @param amount: the amount to mint
   * @param signer: the signing account for the transaction
   */
  static async mint(
    node: string,
    networkMagic: number,
    contractHash: string,
    target: MintType,
    signer: wallet.Account
  ): Promise<string> {
    const method = "mint";
    const params = [
      sc.ContractParam.hash160(target.address),
      sc.ContractParam.integer(target.amount)
    ];

    const res = await variableInvoke(node, networkMagic, contractHash, method, params, signer)
    if (signer) {
      return res
    }
    return formatter(res);
  }

    static async mintBatch(
    node: string,
    networkMagic: number,
    contractHash: string,
    targets: MintType[],
    signer: wallet.Account
  ): Promise<string> {
    const method = "mint_batch";

    const formatted = targets.map( (entry) => {

        const params = [
            sc.ContractParam.hash160(entry.address),
            sc.ContractParam.integer(entry.amount)
        ]

        return sc.ContractParam.array(...params)
    })

    const params = [
        sc.ContractParam.array(...formatted)
    ]

      const res = await variableInvoke(node, networkMagic, contractHash, method, params, signer)
      if (signer) {
        return res
      }
      return formatter(res);
  }

  static async deploy(
    node: string,
    networkMagic: number,
    contractHash: string,
    signer: wallet.Account
  ): Promise<any> {
    const method = "deploy";

    const res = await variableInvoke(node, networkMagic, contractHash, method, [], signer)
    if (signer) {
      return res
    }
    return formatter(res);
  }

  static async getUserJSON(
    node: string,
    networkMagic: number,
    contractHash: string,
    address: string,
    signer?: wallet.Account
  ): Promise<any> {
    const method = "get_user_json";

    const params = [
      sc.ContractParam.hash160(address)
    ]

    const res = await variableInvoke(node, networkMagic, contractHash, method, params, signer)
    if (signer) {
      return res
    }
    return formatter(res[0]);
  }

  static async setUserPermission(
    node: string,
    networkMagic: number,
    contractHash: string,
    address: string,
    permission: string,
    value: boolean,
    signer: wallet.Account
  ): Promise<string> {
    const method = "set_user_permission";
    const params = [
      sc.ContractParam.hash160(address),
      sc.ContractParam.string(permission),
      sc.ContractParam.boolean(value)
    ];

    const res = await variableInvoke(node, networkMagic, contractHash, method, params, signer)
    if (signer) {
      return res
    }
    return formatter(res);
  }

  static async totalAccounts(
    node: string,
    networkMagic: number,
    contractHash: string,
    signer?: wallet.Account,
  ): Promise<number | string> {
    const method = "total_accounts";

    const res = await variableInvoke(node, networkMagic, contractHash, method, [], signer)
    if (signer) {
      return res
    }
    return formatter(res[0]);
  }

  static async update(
    node: string,
    networkMagic: number,
    contractHash: string,
    script: string,
    manifest: string,
    data: any,
    signer: wallet.Account
  ): Promise<string> {
    const method = "update";
    const params = [
      sc.ContractParam.byteArray(script),
      sc.ContractParam.string(manifest),
      sc.ContractParam.any(data)
    ];

    const res = await variableInvoke(node, networkMagic, contractHash, method, params, signer)
    if (signer) {
      return res
    }
    return formatter(res);
  }
}