import { Api, JsonRpc } from 'eosjs';
import { JsSignatureProvider } from 'eosjs/dist/eosjs-jssig';
import { TextEncoder, TextDecoder } from 'util';
import fetch from 'isomorphic-fetch';
import EosApi from 'eosjs-api';
import getInternalAction from '../utils/getInternalAction';
import { GatewayContract, RegistratorContract, SovietContract } from 'cooptypes';
import { IUser } from '../models/user.model';

const rpc = new JsonRpc(process.env.BLOCKCHAIN_RPC as string, { fetch });

/**
 * Получить инстанс для осуществления транзакции в блокчейн.
 * @param {username} - авторизация от аккаунта
 * @param {wif} - приватный ключ от аккаунта
 * @returns {eosjs-api}
 */

async function getInstance(wif) {
  const signatureProvider = new JsSignatureProvider([wif]);
  const api = new Api({ rpc, signatureProvider, textDecoder: new TextDecoder(), textEncoder: new TextEncoder() });
  return api;
}

async function getApi() {
  const options = {
    httpEndpoint: process.env.BLOCKCHAIN_RPC, // default, null for cold-storage
    verbose: false, // API logging
    fetchConfiguration: {},
  };

  const api = new EosApi(options);
  return api;
}

async function lazyFetch(api, code, scope, table, lower_bound?, upper_bound?, limit?, key_type?, index_position?) {
  if (!limit) limit = 10;
  if (!lower_bound && lower_bound !== '') lower_bound = 0;

  const data = await api.getTableRows({
    json: true,
    code,
    scope,
    table,
    upper_bound,
    lower_bound,
    limit,
    key_type,
    index_position,
  });

  let result = data.rows;
  if (data.more == true && limit !== 1) {
    const redata = await lazyFetch(api, code, scope, table, data.next_key, upper_bound, limit, key_type, index_position);
    result = [...result, ...redata];
    return result;
  }
  return result;
}

async function getCooperative(coopname) {
  const api = await getApi();

  const [cooperative] = await lazyFetch(
    api,
    process.env.REGISTRATOR_CONTRACT,
    process.env.REGISTRATOR_CONTRACT,
    'orgs',
    coopname,
    coopname,
    1
  );
  return cooperative;
}

async function registerBlockchainAccount(user: IUser, orderData: GatewayContract.Actions.CompleteDeposit.ICompleteDeposit) {
  const eos = await getInstance(process.env.REGISTRATOR_WIF);

  const newaccount: RegistratorContract.Actions.CreateAccount.ICreateAccount = {
    registrator: process.env.COOPNAME as string,
    coopname: process.env.COOPNAME as string,
    referer: user.referer ? user.referer : '',
    username: user.username,
    public_key: user.public_key,
    meta: '',
    signature_hash: '' // LEGACY - delete it later
  }

  const registerUserData: RegistratorContract.Actions.RegisterUser.IRegistrerUser = {
    coopname: process.env.COOPNAME as string,
    registrator: process.env.COOPNAME as string,
    username: user.username,
  }

  const joinCooperativeData: RegistratorContract.Actions.JoinCooperative.IJoinCooperative = {
    coopname: process.env.COOPNAME as string,
    registrator: process.env.COOPNAME as string,
    username: user.username,
    document: {...user.statement, meta: JSON.stringify(user.statement.meta)},
  }

  console.log("joinCooperativeData: ", joinCooperativeData)

  const actions = [
    {
      account: RegistratorContract.contractName.production,
      name: RegistratorContract.Actions.CreateAccount.actionName,
      authorization: [
        {
          actor: process.env.COOPNAME as string,
          permission: 'active',
        },
      ],
      data: newaccount,
    },
    {
      account: RegistratorContract.contractName.production,
      name: RegistratorContract.Actions.RegisterUser.actionName,//reguser
      authorization: [
        {
          actor: process.env.COOPNAME as string,
          permission: 'active',
        },
      ],
      data: registerUserData,
    },
    {
      account: RegistratorContract.contractName.production,
      name: RegistratorContract.Actions.JoinCooperative.actionName,
      authorization: [
        {
          actor: process.env.COOPNAME as string,
          permission: 'active',
        },
      ],
      data: joinCooperativeData,
    },
    {
      account: GatewayContract.contractName.production,
      name: GatewayContract.Actions.CompleteDeposit.actionName,
      authorization: [
        {
          actor: process.env.COOPNAME as string,
          permission: 'active',
        },
      ],
      data: orderData,
    },

  ];

  const result = await eos.transact(
    {
      actions,
    },
    {
      blocksBehind: 3,
      expireSeconds: 30,
    }
  );
}

async function createOrder(data) {
  const eos = await getInstance(process.env.REGISTRATOR_WIF);

  const actions = [
    {
      account: process.env.GATEWAY_CONTRACT as string,
      name: 'deposit',
      authorization: [
        {
          actor: process.env.COOPNAME as string,
          permission: 'active',
        },
      ],
      data,
    },
  ];

  const result = await eos.transact(
    {
      actions,
    },
    {
      blocksBehind: 3,
      expireSeconds: 30,
    }
  );

  const order_id = getInternalAction(result, 'newdepositid').id;

  return order_id;
}

async function completeOrder(data: GatewayContract.Actions.CompleteDeposit.ICompleteDeposit) {
  const eos = await getInstance(process.env.REGISTRATOR_WIF);

  const actions = [
    {
      account: GatewayContract.contractName.production,
      name: GatewayContract.Actions.CompleteDeposit.actionName,
      authorization: [
        {
          actor: process.env.COOPNAME as string,
          permission: 'active',
        },
      ],
      data,
    },
  ];

  await eos.transact(
    {
      actions,
    },
    {
      blocksBehind: 3,
      expireSeconds: 30,
    }
  );
}

async function failOrder(data) {
  const eos = await getInstance(process.env.REGISTRATOR_WIF);

  const actions = [
    {
      account: process.env.GATEWAY_CONTRACT as string,
      name: 'dpfail',
      authorization: [
        {
          actor: process.env.COOPNAME as string,
          permission: 'active',
        },
      ],
      data,
    },
  ];

  await eos.transact(
    {
      actions,
    },
    {
      blocksBehind: 3,
      expireSeconds: 30,
    }
  );
}

async function getSoviet(coopname) {
  const api = await getApi();

  const soviet = (await lazyFetch(api, process.env.SOVIET_CONTRACT, coopname, 'boards'))[0];

  return soviet;
}

async function fetchAllParticipants() {
  const api = await getApi();

  const participants = await lazyFetch(api, process.env.SOVIET_CONTRACT, process.env.COOPNAME, 'participants');
  return participants;
}

export {
  getInstance,
  getApi,
  registerBlockchainAccount,
  lazyFetch,
  fetchAllParticipants,
  createOrder,
  getCooperative,
  failOrder,
  completeOrder,
  getSoviet,
};
