import grpc
import simnetrpc_pb2
import simnetrpc_pb2_grpc
from bitcoin_core import BitcoinCore
from lnd import Lnd
from tools import debug


def get_simnetrpc_client(address):
    channel = grpc.insecure_channel(address)
    client = simnetrpc_pb2_grpc.SimnetStub(channel)
    return client


class Config:
    def __init__(self, client, name, number_of_nodes, port_forwarding_conf):
        self.client = client
        self.name = name
        self.number_of_nodes = number_of_nodes
        self.port_forwarding_conf = port_forwarding_conf


class Simnet:
    def __init__(self, address, name, number_of_nodes, port_forwarding_conf):
        client = get_simnetrpc_client(address)
        self.config = Config(client, name, number_of_nodes, port_forwarding_conf)
        self.core = BitcoinCore(network_name=name)
        self.alias_to_lnd = {}
        self.index_to_lnd = {}
        self.alias_to_index = {}

    @debug
    def simnet_work_env(self):
        request = simnetrpc_pb2.SimnetConfig(
            name=self.config.name,
            number_of_nodes=self.config.number_of_nodes,
            port_forwarding_conf=self.config.port_forwarding_conf
        )
        response = self.config.client.SimnetWorkEnv(request)
        return response

    @debug
    def simnet_destroy(self):
        request = simnetrpc_pb2.SimnetID(name=self.config.name)
        response = self.config.client.SimnetDestroy(request)
        return response

    @debug
    def kernel_new(self, *, autostart=False):
        request = simnetrpc_pb2.KernelNewRequest(
            simnet_id=simnetrpc_pb2.SimnetID(name=self.config.name),
            autostart=autostart
        )
        response = self.config.client.KernelNew(request)
        # if autostart:
        #    self.kernel_start()
        return response

    @debug
    def kernel_start(self):
        request = simnetrpc_pb2.SimnetID(name=self.config.name)
        response = self.config.client.KernelStart(request)
        return response

    @debug
    def kernel_stop(self):
        request = simnetrpc_pb2.SimnetID(name=self.config.name)
        response = self.config.client.KernelStop(request)
        return response

    @debug
    def kernel_destroy(self, *, autostop=False):
        # if autostop:
        #   self.kernel_stop()
        request = simnetrpc_pb2.KernelDestroyRequest(
            simnet_id=simnetrpc_pb2.SimnetID(name=self.config.name),
            autostop=autostop
        )
        response = self.config.client.KernelDestroy(request)
        return response

    @debug
    def kernel_report(self):
        request = simnetrpc_pb2.SimnetID(name=self.config.name)
        response = self.config.client.KernelReport(request)
        return response

    @debug
    def lnd_new(self, alias, *, autostart=False):
        request = simnetrpc_pb2.LndNewRequest(
            lnd_id=simnetrpc_pb2.LndID(name=self.config.name),
            # autostart=autostart
        )
        response = self.config.client.LndNew(request)

        lnd = Lnd(
            network_name=self.config.name,
            alias=alias,
            index=response.index,
            http_port=response.http_port,
            rpc_port=response.rpc_port,
            peer_port=response.peer_port,
            simnet=self
        )
        self.alias_to_lnd[alias] = lnd
        self.index_to_lnd[response.index] = lnd
        self.alias_to_index[alias] = response.index

        if autostart:
            self.lnd_start(lnd.index)
        return lnd

    @debug
    def lnd_start(self, index):
        request = simnetrpc_pb2.LndID(name=self.config.name, index=index)
        response = self.config.client.LndStart(request)

        lnd = self.lnd_by_index(index)
        # TODO(evg): move this functionality to initialize method
        lnd.identity_pubkey = lnd.get_info().identity_pubkey
        lnd.bitcoin_address = lnd.new_witness_address().address
        return response

    @debug
    def lnd_stop(self, index):
        request = simnetrpc_pb2.LndID(name=self.config.name, index=index)
        response = self.config.client.LndStop(request)
        return response

    @debug
    def lnd_destroy(self, lnd, *, autostop=False):
        #if autostop:
        #    self.lnd_stop(lnd.index)
        request = simnetrpc_pb2.LndDestroyRequest(
            lnd_id=simnetrpc_pb2.LndID(name=self.config.name, index=lnd.index),
            autostop=autostop
        )
        response = self.config.client.LndDestroy(request)
        return response

    @debug
    def lnd_report(self, index):
        request = simnetrpc_pb2.LndID(name=self.config.name, index=index)
        response = self.config.client.LndReport(request)
        return response

    def lnd_by_alias(self, alias):
        return self.alias_to_lnd[alias]

    def lnd_by_index(self, index):
        return self.index_to_lnd[index]

    def index_of(self, alias):
        return self.alias_to_index[alias]
