import hjson

from core.buddy_service import BuddyService
from core.chat_blob import ChatBlob
from core.command_param_types import Character, Multiple
from core.db import DB, SqlException
from core.decorators import instance, command, event, setting
from core.dict_object import DictObject
from core.igncore import IgnCore
from core.lookup.character_service import CharacterService
from core.lookup.pork_service import PorkService
from core.private_channel_service import PrivateChannelService
from core.setting_service import SettingService
from core.setting_types import BooleanSettingType
from core.text import Text
from core.translation_service import TranslationService
from core.util import Util
from modules.core.accounting.services.account_service import AccountService
from modules.core.ban.ban_service import BanService
from modules.orgbot.org.org_controller import OrgChannelController
from modules.standard.online.online_display import OnlineDisplay


@instance()
class PrivateChannelController:
    MESSAGE_SOURCE = "private_channel"
    PRIVATE_CHANNEL_PREFIX = "[<cyan>Priv</cyan>] "

    def inject(self, registry):
        self.bot: IgnCore = registry.get_instance("bot")
        self.util: Util = registry.get_instance("util")
        self.private_channel_service = registry.get_instance("private_channel_service")
        self.character_service: CharacterService = registry.get_instance("character_service")
        self.pork: PorkService = registry.get_instance("pork_service")
        self.job_scheduler = registry.get_instance("job_scheduler")
        self.access_service = registry.get_instance("access_service")
        self.message_hub_service = registry.get_instance("message_hub_service")
        self.ban_service = registry.get_instance("ban_service")
        self.text: Text = registry.get_instance("text")
        self.ts: TranslationService = registry.get_instance("translation_service")
        self.getresp = self.ts.get_response
        self.setting_service: SettingService = registry.get_instance("setting_service")
        self.buddy_service: BuddyService = registry.get_instance("buddy_service")
        self.account_service: AccountService = registry.get_instance("account_service")
        self.db: DB = registry.get_instance("db")
        self.priv: PrivateChannelService = registry.get_instance("private_channel_service")
        self.online_controller = registry.get_instance("online_controller", is_optional=True)

    def pre_start(self):
        self.db.create_view("online")
        self.message_hub_service.register_message_source(self.MESSAGE_SOURCE)

        try:
            self.reinvite = [x.char_id for x in
                             self.db.query("SELECT char_id from online where channel = ?", [self.bot.name])]

        except SqlException:
            self.reinvite = []

    def start(self):
        self.message_hub_service.register_message_destination(self.MESSAGE_SOURCE,
                                                              self.handle_incoming_relay_message,
                                                              ["registration"],
                                                              [self.MESSAGE_SOURCE])
        self.ts.register_translation("module/private_channel", self.load_private_channel_msg)

    @event("connect", "Reinvite previous raiders")
    def reinvite_all(self, _, _1):
        for user in self.reinvite:
            self.bot.send_mass_message(user, "You have been <green>reinvited</green> into my private channel "
                                             "after a bot restart or crash; Sorry for the inconvenience.")
            self.priv.invite(user)
        del self.reinvite

    @staticmethod
    def load_private_channel_msg():
        with open("modules/core/private_channel/private_channel.msg", mode="r", encoding="utf-8") as f:
            return hjson.load(f)

    def handle_incoming_relay_message(self, ctx):
        if self.display_main().get_value() and ctx.source == "org_channel" and ctx.sender:
            name = f"{OrgChannelController.ORG_CHANNEL_PREFIX} <yellow>{ctx.sender.name}</yellow>"
            if account := self.account_service.get_account(ctx.sender.char_id):
                if account.main != ctx.sender.char_id:
                    name += f" (<yellow>{account.name}</yellow>)"
                self.bot.send_private_channel_message(name + ": " + ctx.message, fire_outgoing_event=False)
        else:
            self.bot.send_private_channel_message(ctx.formatted_message, fire_outgoing_event=False)

    @event(event_type="member_logon", description="Send autoinvites to players logging in")
    def logon_event(self, _, data):
        if not self.bot.is_ready():
            if data.packet.char_id not in self.reinvite:
                account = data.account
                if not self.account_service.simple_checks(account):
                    return
                if self.account_service.get_entry(data.packet.char_id).get("member", self.bot.public_channel_service.org_id) == self.bot.public_channel_service.org_id:
                    return
                if account.auto_invite == 1:
                    self.reinvite.append(data.packet.char_id)
            return
        if self.private_channel_service.in_private_channel(data.packet.char_id):
            return
        account = data.account
        if not self.account_service.simple_checks(account):
            return
        if self.db.query_single("SELECT * from org_bots where char_id=?", [data.packet.char_id]):
            return
        if account.auto_invite == 1:
            if self.account_service.get_entry(data.packet.char_id).get("member", self.bot.public_channel_service.org_id) != self.bot.public_channel_service.org_id:
                self.private_channel_service.invite(data.packet.char_id)
                self.bot.send_mass_message(data.packet.char_id, "You have been "
                                                                "<highlight>auto invited</highlight> "
                                                                "into my private channel.")

    @command(command="join", params=[], access_level="member",
             description="Join the private channel")
    def join_cmd(self, request):
        self.private_channel_service.invite(request.sender.char_id)

    @command(command="leave", params=[], access_level="member",
             description="Leave the private channel")
    def leave_cmd(self, request):
        self.private_channel_service.kick(request.sender.char_id)

    @command(command="invite", params=[Multiple(Character("character"))], access_level="member",
             description="Invite a character to the private channel")
    def invite_cmd(self, request, chars):
        success, in_channel, none, banned = [], [], [], []
        for char in chars:
            if char.char_id:
                if self.private_channel_service.in_private_channel(char.char_id):
                    in_channel.append(char.name)
                elif self.ban_service.get_ban(char.char_id):
                    banned.append(char.name)
                else:
                    self.bot.send_private_message(char.char_id, self.getresp("module/private_channel",
                                                                             "invite_success_target",
                                                                             {"inviter": request.sender.name}))
                    self.private_channel_service.invite(char.char_id)
                    success.append(char.name)
            else:
                none.append(char.name)
        out = ""
        if len(in_channel) > 0:
            out += self.getresp("module/private_channel", "invite_fail", {"target": ", ".join(in_channel)}) + "\n"
        if len(success) > 0:
            out += self.getresp("module/private_channel", "invite_success_self", {"target": ", ".join(success)}) + "\n"
        if len(none) > 0:
            out += self.getresp("global", "char_not_found", {"char": ", ".join(none)}) + "\n"
        if len(banned) > 0:
            out += f'The Character <highlight>{", ".join(banned)}</highlight> is <red>banned</red>, ' \
                   f'and cannot be invited.'
        return out.strip("\n")

    @command(command="kick", params=[Character("character")], access_level="moderator",
             description="Kick a character from the private channel")
    def kick_cmd(self, request, char):
        if char.char_id:
            if not self.private_channel_service.in_private_channel(char.char_id):
                return self.getresp("module/private_channel", "kick_fail_not_in_priv", {"target": char.name})
            else:
                if self.access_service.has_sufficient_access_level(request.sender.char_id, char.char_id):
                    self.bot.send_private_message(char.char_id, self.getresp("module/private_channel",
                                                                             "kick_success_target",
                                                                             {"kicker": request.sender.name}))
                    self.private_channel_service.kick(char.char_id)
                    return self.getresp("module/private_channel", "kick_success_self", {"target": char.name})
                else:
                    return self.getresp("module/private_channel", "kick_fail", {"target": char.name})
        else:
            return self.getresp("global", "char_not_found", {"char": char.name})

    @command(command="kickall", params=[], access_level="moderator",
             description="Kick all characters from the private channel")
    def kickall_cmd(self, request):
        self.bot.send_private_channel_message(self.getresp("module/private_channel", "kick_all",
                                                           {"char": request.sender.name}))
        self.job_scheduler.delayed_job(lambda t: self.private_channel_service.kickall(), 10)

    @event(event_type=BanService.BAN_ADDED_EVENT, description="Kick characters from the private channel who are banned",
           is_hidden=True)
    def ban_added_event(self, _, event_data):
        self.private_channel_service.kick(event_data.char_id)

    @event(event_type=PrivateChannelService.PRIVATE_CHANNEL_MESSAGE_EVENT,
           description="Relay messages from the private channel to the relay hub", is_hidden=True)
    def handle_private_channel_message_event(self, _, event_data):
        if event_data.char_id == self.bot.get_char_id() or self.ban_service.get_ban(event_data.char_id):
            return

        char_name = self.character_service.resolve_char_to_name(event_data.char_id)
        sender = DictObject({"char_id": event_data.char_id, "name": char_name})
        char = self.text.make_charlink(char_name)
        formatted_message = f"{self.PRIVATE_CHANNEL_PREFIX}{char}: {event_data.message}"
        self.message_hub_service.send_message(self.MESSAGE_SOURCE, sender, event_data.message, formatted_message)

    @event(event_type=PrivateChannelService.JOINED_PRIVATE_CHANNEL_EVENT,
           description="Notify when a character joins the private channel")
    def handle_private_channel_joined_event(self, _, event_data):
        main = self.account_service.get_account(event_data.char_id)
        if main:
            info = "" if self.account_service.check_superadmin(main.char_id) else \
                ":: <red>Admin</red> " if self.account_service.check_admin(main.char_id) else \
                ":: <red>Moderator</red> " if self.account_service.check_moderator(main.char_id) else \
                ":: <orange>Raidleader</orange> " if self.account_service.check_leader(main.char_id) else ""
            if main.char_id != event_data.char_id:
                info += f":: Alt of <{main.faction.lower()}>{main.name}</{main.faction.lower()}>"
        else:
            if self.setting_service.get_value('is_alliance_bot') == "0":
                info = ":: <red>WARN</red> » <notice>NO ACC</notice>"
            else:
                info = ""

        msg = f"{self.text.format_char_info(self.pork.get_character_info(event_data.char_id))} joined us {info}"
        self.bot.send_private_channel_message(msg, fire_outgoing_event=False)
        self.message_hub_service.send_message(self.MESSAGE_SOURCE, None, msg, self.PRIVATE_CHANNEL_PREFIX + msg)
        afk_list = {}
        if self.online_controller:
            afk_list = self.online_controller.afk_list
        od = OnlineDisplay(self.text, self.util, self.db, afk_list)
        # od = OnlineDisplay(self.text, self.util, self.db)
        params = [self.bot.name, self.bot.get_char_id()]
        self.bot.send_mass_message(event_data.char_id,
                                   od.format_blob(od.format_by_channel_prof("and channel_id IN (1, 2) ", params)))

    @event(event_type=PrivateChannelService.LEFT_PRIVATE_CHANNEL_EVENT,
           description="Notify when a character leaves the private channel")
    def handle_private_channel_left_event(self, _, event_data):
        char_info = self.pork.get_character_info(event_data.char_id)
        msg = f"<{char_info.faction.lower()}>{char_info.name}</{char_info.faction.lower()}> left us"
        self.bot.send_private_channel_message(msg, fire_outgoing_event=False)
        self.message_hub_service.send_message(self.MESSAGE_SOURCE, None, msg, msg)

    @event(event_type=IgnCore.OUTGOING_PRIVATE_CHANNEL_MESSAGE_EVENT,
           description="Relay commands from the private channel to the relay hub", is_hidden=True)
    def outgoing_private_channel_message_event(self, _, event_data):

        if isinstance(event_data.message, ChatBlob):
            pages = self.text.paginate(event_data.message,
                                       self.setting_service.get("org_channel_max_page_length").get_value())
            if len(pages) < 4:
                for page in pages:
                    # message = "{org} {message}".format(org=self.ORG_CHANNEL_PREFIX, message=page)
                    event_data.message.page_prefix = self.PRIVATE_CHANNEL_PREFIX
                    self.message_hub_service.send_message(self.MESSAGE_SOURCE,
                                                          None,
                                                          page,
                                                          event_data.message)
            else:
                # message = "{org} {message}".format(org=self.ORG_CHANNEL_PREFIX, message=event_data.message.title)
                event_data.message.page_prefix = self.PRIVATE_CHANNEL_PREFIX
                self.message_hub_service.send_message(self.MESSAGE_SOURCE,
                                                      None,
                                                      event_data.message.title,
                                                      event_data.message)
        else:
            message = "{priv}{message}".format(priv=self.PRIVATE_CHANNEL_PREFIX, message=event_data.message)
            self.message_hub_service.send_message(self.MESSAGE_SOURCE,
                                                  None,
                                                  message,
                                                  message)

    @setting(name="display_main", value="false", description="Should the main be displayed in relayed messages")
    def display_main(self) -> BooleanSettingType:
        return BooleanSettingType()
