#!/usr/bin/python3
import random as r
import config as conf
import commands as cmds
from typing import Union, Callable
from overrides import bytes, bbytes
from importlib import reload
import bare, re, checks
from traceback import format_exc


def CTCP(bot: bare.bot, msg: str) -> bool:
    sender = msg.split("!", 1)[0][1:]
    kind = msg.split("\x01")[1].split(" ", 1)[0]
    bot.log(f'Responding to CTCP "{kind}" from {sender}')
    if kind == "VERSION":
        bot.notice(
            f"\x01VERSION FireBot {conf.__version__} (https://git.h.hackclub.app/Firepup650/FireBot)\x01",
            sender,
            True,
        )
        return True
    elif kind == "USERINFO":
        bot.notice("\x01USERINFO FireBot (Firepup's bot)\x01", sender, True)
        return True
    elif kind == "SOURCE":
        bot.notice(
            "\x01SOURCE https://git.h.hackclub.app/Firepup650/FireBot\x01",
            sender,
            True,
        )
        return True
    elif kind == "FINGER":
        bot.notice("\x01FINGER Firepup's bot\x01", sender, True)
        return True
    elif kind == "CLIENTINFO":
        bot.notice(
            "\x01CLIENTINFO ACTION VERSION USERINFO SOURCE FINGER\x01", sender, True
        )
        return True
    bot.log(f'Unknown CTCP "{kind}"', "WARN")
    return False


def PRIVMSG(bot: bare.bot, msg: str) -> Union[tuple[None, None], tuple[str, str]]:
    # Format of ":[Nick]![ident]@[host|vhost] PRIVMSG [channel] :[message]”
    name = msg.split("!", 1)[0][1:]
    host = msg.split("@", 1)[1].split(" ", 1)[0]
    bot.tmpHost = host
    bridge = False
    bot.current = "user"
    if (
        (name.startswith("saxjax") and bot.server == "efnet")
        or (name in ["ReplIRC", "sshchat"] and bot.server == "replirc")
        or (
            name in ["FirePyLink_", "FirePyLink"]
            and bot.server in ["ircnow", "backupbox"]
        )
    ):
        if "<" in msg and ">" in msg:
            bridge = True
            bot.current = "bridge"
            Nname = msg.split("<", 1)[1].split(">", 1)[0].strip()
            if name == "ReplIRC":
                name = Nname[4:]
            elif name in ["FirePyLink_", "FirePyLink"]:
                name = Nname.lstrip("@%~+")[3:-1]
            else:
                name = Nname
            message = msg.split(">", 1)[1].strip()
        else:
            message = msg.split("PRIVMSG", 1)[1].split(":", 1)[1].strip()
    elif name == bot.nick:
        return None, None
    else:
        message = msg.split("PRIVMSG", 1)[1].split(":", 1)[1].strip()
    chan = msg.split("PRIVMSG", 1)[1].split(":", 1)[0].strip()
    message = conf.sub(message, bot, chan, name)
    if chan in bot.ignores:
        return None, None
    bot.log(
        f'Got "{bytes(message).lazy_decode()}" from "{name}" in "{chan}" ({bot.current})',
    )
    if len(name) > bot.nicklen:
        bot.log(f"Name too long ({len(name)} > {bot.nicklen})")
        if not bridge:
            return None, None
        else:
            bot.log("This user is a bridge, overriding")
    elif chan not in bot.channels:
        if not chan == bot.nick:
            bot.log(
                f"Channel not in channels ({chan} not in {bot.channels})",
                "WARN",
            )
        if not chan.startswith(("#", "+", "&")):
            chan = name
    else:
        bot.channels[chan] += 1
    if "goat" in name.lower() and bot.gmode == True:
        cmds.goat(bot, chan, name, message)
    handled = False
    for cmd in cmds.data:
        triggers = [cmd]
        triggers.extend(cmds.data[cmd]["aliases"])
        triggers = list(conf.sub(call, bot, chan, name).lower() for call in triggers)
        if conf.cmdFind(
            conf.sub(message, bot, chan, name).lower(),
            triggers,
            cmds.data[cmd]["prefix"],
        ):
            if "check" in cmds.data[cmd] and cmds.data[cmd]["check"]:
                if cmds.data[cmd]["check"](bot, name, host, chan, cmd):
                    try:
                        cmds.call[cmd](bot, chan, name, message)
                    except Exception:
                        Err = format_exc()
                        for line in Err.split("\n"):
                            bot.log(line, "ERROR")
                        bot.msg(
                            "Sorry, I had an error trying to execute that command. Please check error logs.",
                            chan,
                        )
            else:
                try:
                    cmds.call[cmd](bot, chan, name, message)
                except Exception:
                    Err = format_exc()
                    for line in Err.split("\n"):
                        bot.log(line, "ERROR")
                    bot.msg(
                        "Sorry, I had an error trying to execute that command. Please check error logs.",
                        chan,
                    )
            handled = True
            break
    if not handled:
        for check in cmds.regexes:
            if re.search(
                conf.sub(check, bot, chan, name),
                message,
            ):
                cmds.call[check](bot, chan, name, message)
                handled = True
                break
    if not handled and conf.cmdFind(message, ["reload", "r"]):
        if checks.admin(bot, name, host, chan, "reload"):
            return "reload", chan
        handled = True
    if not handled and len(message.split("\x01")) == 3:
        if not CTCP(bot, message):
            kind = message.split("\x01")[1]
            if kind.startswith("ACTION ducks") and len(kind.split(" ", 2)) == 3:
                bot.msg(
                    f"\x01ACTION gets hit by {kind.split(' ', 2)[2]}\x01",
                    chan,
                )
            elif kind == "ACTION ducks":
                bot.msg("\x01ACTION gets hit by a duck\x01", chan)
    if chan in bot.channels and bot.channels[chan] >= bot.interval:
        sel = ""
        bot.channels[chan] = 0
        if bot.autoMethod == "QUOTE":
            r.seed()
            with open("mastermessages.txt", "r") as mm:
                sel = conf.decode_escapes(
                    r.sample(mm.readlines(), 1)[0].replace("\\n", "").replace("\n", "")
                )
        else:
            sel = bot.markov.generate_from_sentence(message)
        bot.msg(f"[{bot.autoMethod}] {sel}", chan)
    return None, None


def NICK(bot: bare.bot, msg: str) -> tuple[None, None]:
    name = msg.split("!", 1)[0][1:]
    if name == bot.nick:
        bot.nick = msg.split("NICK", 1)[1].split(":", 1)[1].strip()
    return None, None


def KICK(bot: bare.bot, msg: str) -> tuple[None, None]:
    important = msg.split("KICK", 1)[1].split(":", 1)[0].strip().split(" ")
    channel = important[0]
    kicked = important[1]
    if kicked == bot.nick:
        bot.channels.pop(channel, None)
    return None, None


def PART(bot: bare.bot, msg: str) -> tuple[None, None]:
    parted = msg.split("!", 1)[0][1:]
    channel = msg.split("PART", 1)[1].split(":", 1)[0].strip()
    if parted == bot.nick:
        bot.channels.pop(channel, None)
    return None, None


def QUIT(bot: bare.bot, msg: str) -> tuple[None, None]:
    if bot.server == "replirc":
        quitter = msg.split("!", 1)[0][1:]
        if quitter == "FireMCbot":
            bot.send("TOPIC #firemc :FireMC Relay channel (offline)\n")
    return None, None


def JOIN(bot: bare.bot, msg: str) -> tuple[None, None]:
    nick = msg.split("!", 1)[0][1:]
    hostname = msg.split("@", 1)[1].split(" ", 1)[0].strip()
    chan = msg.split("#")[-1].strip()
    conf.dnsblHandler(bot, nick, hostname, chan)
    return None, None


def MODE(bot: bare.bot, msg: str) -> tuple[None, None]:
    try:
        chan = msg.split("#", 1)[1].split(" ", 1)[0]
        add = True if msg.split("#", 1)[1].split(" ", 2)[1][0] == "+" else False
        modes = msg.split("#", 1)[1].split(" ", 2)[1][1:]
        users = ""
        try:
            users = msg.split("#", 1)[1].split(" ", 2)[2].split()
        except IndexError:
            ...
        if len(modes) != len(users):
            bot.log("Refusing to handle modes that do not have corresponding users.")
            return None, None
        for i in range(len(modes)):
            if users[i] == bot.nick:
                if modes[i] == "o":
                    bot.ops[chan] = add
                    bot.log(f"{'Got' if add else 'Lost'} ops in {chan}")
    except IndexError:  # *our* modes are changing, not a channel
        bot.log("Not handling changing of my modes")
    return None, None


def NULL(bot: bare.bot, msg: str) -> tuple[None, None]:
    return None, None


handles: dict[
    str, Callable[[bare.bot, str], Union[tuple[None, None], tuple[str, str]]]
] = {
    "PRIVMSG": PRIVMSG,
    "NICK": NICK,
    "KICK": KICK,
    "PART": PART,
    "MODE": MODE,
    "TOPIC": NULL,
    "QUIT": QUIT,
    "JOIN": JOIN,
    "NOTICE": NULL,
    "INVITE": NULL,
}