import time
from typing import List

from core.chat_blob import ChatBlob
from core.db import DB
from core.dict_object import DictObject
from core.text import Text
from core.util import Util


class OnlineDisplay:
    def __init__(self, text, util, db, afk_list={}):
        self.text: Text = text
        self.util: Util = util
        self.db: DB = db
        self.afk_list = afk_list

    def get_online_players(self, order, group="", params=None):
        return self.db.query(f"SELECT r.*, "
                             f"CASE WHEN r.rank = 'admin' then 1 "
                             f"WHEN  r.rank = 'moderator' then 2 "
                             f"WHEN  r.rank = 'council' then 3 "
                             f"WHEN  r.rank = 'leader' then 4 "
                             f"else 5 END as  r_id, "
                             f"CASE WHEN o.channel = 'org' then 1 "
                             f"WHEN o.channel = ? then 2 "
                             f"WHEN o.channel = 'notify' then 3 "
                             f"else 4 END as channel_id, o.channel, "
                             f"p.*, a.main as main_id, p2.name as main_name, o.bot from online o "
                             f"left join player p on o.char_id = p.char_id "
                             f"left join account a on o.char_id = a.char_id "
                             f"left join player p2 on a.main = p2.char_id "
                             f"left join ranks r on a.main = r.main "
                             f"{group} HAVING bot=? {order}", params)

    def format_blob(self, online, title="Online Players"):
        blob, org, priv, notify = online
        postfix = []
        if org > 0:
            postfix.append(f"<notice>Org: {org}</notice>")
        if priv > 0:
            postfix.append(f"<notice>Priv: {priv}</notice>")
        if notify > 0:
            postfix.append(f"<notice>Buddylist: {notify}</notice>")
        blob = ChatBlob(title, blob, suffix=f" ({f', '.join(postfix)})")
        return blob if (org+priv+notify) != 0 else "There's nobody online."

    def format_by_channel_main(self, query, params):
        query += "order by channel_id, main_name, p.name=main_name, p.name, r_id"
        players = self.get_online_players(query, params=params)
        channel_id = 0
        main_id = 0
        blob = ""
        in_org_priv = []
        org, priv, notify = 0, 0, 0
        previous = DictObject({'char_id': 0})
        for player in players:
            if player.faction == "":
                player.faction = "unknown"

            rank = ""

            if 'rank' in player:
                rank = f" [<red>{player.rank[:1].upper()}</red>]" if player.rank else ""
            if previous.char_id == player.char_id:
                continue
            previous = player

            if player.char_id in in_org_priv:
                continue
            if player.channel_id != channel_id:
                channel_id = player.channel_id
                blob += f"\n\n<tab><notice>.:: <header>{self.get_channel_name(channel_id)}</header> ::.</notice>"
                main_id = 0
            in_org_priv.append(player.char_id)
            afk = self.afk_list.get(player.char_id, None)
            if main_id != player.main_id:
                main_id = player.main_id
                afk = "" if not afk else f" [<notice>{afk.message} - since {self.util.time_to_readable(int(time.time() - afk.time))}</notice>]"
                style = "style='text-decoration:none'"
                blob += f"\n<highlight>{self.text.make_tellcmd(player.main_name, f'alts {player.main_name}', style=style)}</highlight>{rank}:{afk}\n"
                if channel_id == 1:
                    org += 1
                elif channel_id == 2:
                    priv += 1
                elif channel_id == 3:
                    notify += 1
            if channel_id == 1:
                blob += self.format_org(player)
            elif channel_id == 2:
                blob += self.format_priv(player)
            elif channel_id == 3:
                blob += self.format_notify(player)
        return blob, org, priv, notify

    def format_by_channel_prof(self, query, params):
        query += "order by channel_id, profession, level desc, r_id"
        players = self.get_online_players(query, params=params)
        channel_id = 0
        profession = 0
        blob = ""
        in_org_priv = []
        org, priv, notify = 0, 0, 0
        previous = DictObject({'char_id': 0})
        for player in players:
            if player.faction == "":
                player.faction = "unknown"
            rank = ""
            if player.char_id in in_org_priv:
                continue
            if 'rank' in player:
                rank = f"[<red>{player.rank[:1].upper()}</red>] " if player.rank else ""
            if previous.char_id == player.char_id:
                continue
            previous = player
            if player.channel_id != channel_id:
                channel_id = player.channel_id
                blob += f"\n\n<tab><notice>.:: <header>{self.get_channel_name(channel_id)}</header> ::.</notice>"
                profession = 0
            in_org_priv.append(player.char_id)
            afk = self.afk_list.get(player.char_id, None)
            afk = "" if not afk else f" [<notice>{afk.message} - since {self.util.time_to_readable(int(time.time() - afk.time))}</notice>]"

            if profession != player.profession:
                profession = player.profession
                blob += f"\n<highlight>{player.profession}</highlight>:\n"
            if channel_id == 1:
                blob += self.format_org(player, rank, afk, main_order=True)
                org += 1
            elif channel_id == 2:
                blob += self.format_priv(player, rank, afk, main_order=True)
                priv += 1
            elif channel_id == 3:
                blob += self.format_notify(player, rank, afk, main_order=True)
                notify += 1
        return blob, org, priv, notify

    def format_by_org(self, query, params):
        blob = ""
        query += " and org_id IN (SELECT org_id from orgs) and channel_id = 3 " \
                 "ORDER BY p.org_name, p.name, p.org_rank_id"
        query = self.get_online_players(query, params=params)
        org_id = 0
        temp_blob = {
        }
        last = 0
        for user in query:
            if user.faction == "":
                user.faction = "unknown"
            if last == user.char_id:
                continue
            last = user.char_id
            if user.org_name not in temp_blob:
                temp_blob[user.org_name] = {"name": user.org_name, "id": user.org_id, "online": []}
            temp_blob[user.org_name]["online"].extend([user])

        for key in sorted(temp_blob):
            if len(temp_blob[key]['online']) == 0:
                continue
            for user in temp_blob[key]['online']:
                if org_id != temp_blob[key]["id"]:
                    org_id = temp_blob[key]["id"]
                    blob += f"\n<pagebreak><highlight>{temp_blob[key]['name']}</highlight>" \
                            f" <header>({len(temp_blob[key]['online']): >3} " \
                            f"| {len(temp_blob[key]['online']) / len(query) * 100:.2f}%)</header>\n"
                blob += self.format_org(user)
        return blob, 0, 0, len(query)

    def get_channel_name(self, c_id):
        if c_id == 1:
            return "Org Channel"
        elif c_id == 2:
            return "Private Channel"
        elif c_id == 3:
            return "Buddylist"

    def format_org(self, player, rank="", afk="", main_order=False):
        main = f"[{self.text.make_tellcmd(player.main_name, f'alts {player.main_name}')}]" if main_order else ""
        org = f"({player.org_rank_name}) " if player.org_rank_name else "(Applicant)"
        return f" {self.util.get_prof_icon(player.profession)} {rank}" \
               f"{self.text.zfill(player.level, 220)}/<green>{self.text.zfill(player.ai_level, 30)}</green> " \
               f"<{player.faction.lower()}>{player.name}</{player.faction.lower()}> {org}{afk} {main}\n"

    def format_priv(self, player, rank="", afk="", main_order=False):
        main = f"[{self.text.make_tellcmd(player.main_name, f'alts {player.main_name}')}]" if main_order else ""
        org = f"({player.org_name}|{player.org_rank_name}) " if player.org_name else ""
        return f" {self.util.get_prof_icon(player.profession)} {rank}" \
               f"{self.text.zfill(player.level, 220)}/<green>{self.text.zfill(player.ai_level, 30)}</green> " \
               f"<{player.faction.lower()}>{player.name}</{player.faction.lower()}> " \
               f"{org}{afk} {main}\n"

    def format_notify(self, player, rank="", afk="", main_order=False):
        main = f"[{self.text.make_tellcmd(player.main_name, f'alts {player.main_name}')}]" if main_order else ""
        org = f"({player.org_name}|{player.org_rank_name}) " if player.org_name else ""
        return f" {self.util.get_prof_icon(player.profession)} {rank}" \
               f"{self.text.zfill(player.level, 220)}/<green>{self.text.zfill(player.ai_level, 30)}</green> " \
               f"<{player.faction.lower()}>{player.name}</{player.faction.lower()}> " \
               f"{org}{afk} {main}\n"

    def count_prof(self, query, params, filters):
        if filters:
            filters = self.util.get_profession(filters)
            params.append(filters)
            if not filters:
                return "Unknown profession"
            query += "and profession = ? ORDER BY p.profession "
            previous = []
            data = []
            for x in self.get_online_players(query, params=params):
                if x.char_id in previous:
                    continue
                data.append(f"{x.name} [{x.level}/<green>{x.ai_level}</green>]")
                previous.append(x.char_id)
            return f"""<highlight>{filters}'s</highlight>: {", ".join(data)}"""
        else:
            query += "ORDER BY p.profession "
            data = self.get_online_players(query, params=params)
            profs = {}
            previous = []
            for user in data:
                if user.char_id in previous:
                    continue
                previous.append(user.char_id)
                if user.profession not in profs:
                    profs[user.profession] = 0
                profs[user.profession] += 1
            blob = []
            for key, value in profs.items():
                blob.append(f"[<highlight>{self.util.get_profession(key, long=False)}</highlight>: {value}]")
            return ", ".join(blob)

    def count_org(self, query, params, search):
        if search:
            filters = self.db.query_single("SELECT * from all_orgs where org_id = ? OR org_name = ?",
                                           [search, search])
            if not filters:
                return f"Could not find org {search}"
            params.append(filters.org_id)
            query += "and p.org_id=? ORDER BY p.profession, p.name, p.level "
            previous = []
            data = []
            for x in self.get_online_players(query, params=params):
                if x.char_id in previous:
                    continue
                data.append(self.format_org(x))
                previous.append(x.char_id)
            return ChatBlob(f"Online Players ({filters.org_name} - {len(data)})", "".join(data))
        else:
            query += "ORDER BY p.org_name "
            data = self.get_online_players(query, params=params)
            orgs = {}
            count = 0
            previous = []
            for user in data:
                if user.char_id in previous:
                    continue
                previous.append(user.char_id)
                if user.org_name not in orgs:
                    orgs[user.org_name] = 0
                orgs[user.org_name] += 1
                count += 1
            blob = []
            for key, value in orgs.items():
                blob.append(
                    f"[<highlight>{key}</highlight>: {value} - {value / count * 100:.2f}%] "
                    f"[{self.text.make_tellcmd('Show', f'count org {key}')}]")
            return ChatBlob(f"Online Organisations ({len(blob)})", "\n ".join(blob))

    def count_tl(self, query, params: List, search):
        if search:
            min_level, max_level = self.util.get_level_range_tl(search)
            if min_level == 0 and max_level == 0:
                return f" Title level out of range: {search}"
            params.extend([min_level, max_level])
            query += "and p.level > ? and p.level < ? ORDER BY p.level desc "
            previous = []
            data = []
            for x in self.get_online_players(query, params=params):
                if x.char_id in previous:
                    continue
                data.append(self.format_priv(x))
                previous.append(x.char_id)
            return ChatBlob(f"Online Players TL{search} ({len(data)})", "".join(data))
        else:
            query += "ORDER BY p.level "
            tls = {}
            previous = []
            for user in self.get_online_players(query, params=params):
                if user.char_id in previous:
                    continue
                tl = self.util.get_title_level(user.level)
                if tl not in tls:
                    tls[tl] = 0
                tls[tl] += 1
                previous.append(user.char_id)
            blob = []
            for key, value in tls.items():
                blob.append(f"[<highlight>TL{key}</highlight>: {value}]")
            return ", ".join(blob)
