from copy import deepcopy

import main
from core.bot import IRCBotFactory
from modules import alias
from modules.chankeep import nukeNetwork
from modules.provision import provisionRelay
from modules.regproc import needToRegister
from twisted.internet import reactor
from twisted.internet.ssl import DefaultOpenSSLContextFactory
from utils.deliver_relay_commands import deliverRelayCommands
from utils.get import getRelay
from utils.logging.log import log


def migrate():
    existing = deepcopy(main.network)
    log("Migrating network configuration")
    log(f"Existing network configuration: {existing}")
    for net, net_inst in existing.items():
        log(f"Migrating network {net}")
        net = net_inst.net
        host = net_inst.host
        port = net_inst.port
        security = net_inst.security
        auth = net_inst.auth
        last = net_inst.last
        relays = net_inst.relays
        aliases = net_inst.aliases

        new_net = Network(net, host, port, security, auth)
        log(f"New network for {net}: {new_net}")
        new_net.last = last
        new_net.relays = relays
        new_net.aliases = aliases
        main.network[net] = new_net
    main.saveConf("network")
    log("Finished migrating network configuration")


class Network:
    def __init__(self, net, host, port, security, auth):
        self.net = net
        self.host = host
        self.port = port
        self.security = security
        self.auth = auth
        self.chanlimit = None

        self.last = 1
        self.relays = {}
        self.aliases = {}

    def add_relay(self, num=None):
        # Grrrrrrrrrr
        self.last = int(self.last)
        if not num:
            num = self.last
            self.last += 1
        elif num == self.last:
            self.last += 1
        registered = False
        if not needToRegister(self.net):
            registered = True
            # Don't need to register if it's been disabled in definitions,
            #  so we'll pretend we already did
        self.relays[num] = {
            "enabled": main.config["ConnectOnCreate"],
            "net": self.net,
            "id": num,
            "registered": registered,
        }
        password = alias.generate_password()
        if num not in main.alias.keys():
            main.alias[num] = alias.generate_alias()
            main.saveConf("alias")
        self.aliases[num] = {"password": password}
        # if main.config["ConnectOnCreate"]: -- Done in provision
        #    self.start_bot(num)
        provisionRelay(num, self.net)
        return num, main.alias[num]["nick"]

    def enable_relay(self, num):
        """
        Enable a relay for this network.
        Send a command to ZNC to connect.
        """
        self.relays[num]["enabled"] = True
        user = main.alias[num]["nick"]
        commands = {"status": ["Connect"]}
        name = f"{self.net}{num}"
        deliverRelayCommands(num, commands, user=user + "/" + self.net)
        main.saveConf("network")
        if name not in main.IRCPool.keys():
            self.start_bot(num)

    def disable_relay(self, num):
        """
        Disable a relay for this network.
        Send a command to ZNC to disconnect.
        Stop trying to connect to the relay.
        """
        self.relays[num]["enabled"] = False
        user = main.alias[num]["nick"]
        # relay = main.network[spl[1]].relays[relayNum]
        commands = {"status": ["Disconnect"]}
        name = f"{self.net}{num}"
        deliverRelayCommands(num, commands, user=user + "/" + self.net)
        main.saveConf("network")
        if name in main.ReactorPool.keys():
            if name in main.FactoryPool.keys():
                main.FactoryPool[name].stopTrying()
            main.ReactorPool[name].disconnect()
            if name in main.IRCPool.keys():
                del main.IRCPool[name]
            del main.ReactorPool[name]
            del main.FactoryPool[name]

    def killAliases(self, aliasList):
        for i in aliasList:
            name = self.net + str(i)
            if name in main.ReactorPool.keys():
                if name in main.FactoryPool.keys():
                    main.FactoryPool[name].stopTrying()
                main.ReactorPool[name].disconnect()
                if name in main.IRCPool.keys():
                    del main.IRCPool[name]
                del main.ReactorPool[name]
                del main.FactoryPool[name]

    def delete_relay(self, id):
        del self.relays[id]
        del self.aliases[id]
        # del main.alias[id] - Aliases are global per num, so don't delete them!
        self.killAliases([id])

    def seppuku(self):
        # Removes all bots in preperation for deletion
        self.killAliases(self.relays.keys())
        nukeNetwork(self.net)

    def start_bot(self, num):
        # a single name is given to relays in the backend
        # e.g. freenode1 for the first relay on freenode network
        keyFN = main.certPath + main.config["Key"]
        certFN = main.certPath + main.config["Certificate"]
        contextFactory = DefaultOpenSSLContextFactory(
            keyFN.encode("utf-8", "replace"), certFN.encode("utf-8", "replace")
        )
        bot = IRCBotFactory(self.net, num)
        # host, port = self.relays[num]["host"], self.relays[num]["port"]
        host, port = getRelay(num)
        rct = reactor.connectSSL(host, port, bot, contextFactory)
        name = self.net + str(num)
        main.ReactorPool[name] = rct
        main.FactoryPool[name] = bot

        log("Started bot on relay %s on %s" % (num, self.host))

    def start_bots(self):
        for num in self.relays.keys():
            if self.relays[num]["enabled"]:
                self.start_bot(num)
