import time

from core.command_alias_service import CommandAliasService
from core.command_param_types import Const, Character, Options
from core.db import DB
from core.decorators import instance, command, timerevent, event
from core.lookup.character_service import CharacterService
from core.private_channel_service import PrivateChannelService
from core.setting_service import SettingService
from core.setting_types import ColorSettingType, BooleanSettingType
from core.text import Text
from core.igncore import IgnCore
from modules.core.accounting.services.access_service import AccessService


@instance()
class LeaderController:
    NOT_LEADER_MSG = "Error! You must be raid leader, or have higher access " \
                     "level than the raid leader to use this command."

    def __init__(self):
        self.leader = None
        self.last_activity = None
        self.echo = False

    def inject(self, registry):
        self.db: DB = registry.get_instance("db")
        self.text: Text = registry.get_instance("text")
        self.access_service: AccessService = registry.get_instance("access_service")
        self.character_service: CharacterService = registry.get_instance("character_service")
        self.bot: IgnCore = registry.get_instance("bot")
        self.setting_service: SettingService = registry.get_instance("setting_service")
        self.command_alias_service: CommandAliasService = registry.get_instance("command_alias_service")

    def pre_start(self):
        self.command_alias_service.add_alias("repeat", "leader echo")
        self.setting_service.register(self.module_name, "leader_echo_color", "#00FF00", ColorSettingType(),
                                          "Color with which the leader's messages will be echoed with")
        self.setting_service.register(self.module_name, "leader_auto_echo", True, BooleanSettingType(),
                                          "If turned on, when someone assume the leader role, "
                                          "leader echo will automatically be activated for said person")

    @command(command="leader", params=[], access_level="member",
             description="Show the current raid leader")
    def leader_show_command(self, _):
        if self.leader:
            return "The current raid leader is <highlight>{leader}<end>.".format(leader=self.leader.name)
        else:
            return "There is no current raid leader. Use <highlight><symbol>leader set<end> to become the raid leader."

    @command(command="leader", params=[Const("echo"), Options(["on", "off"])], access_level="leader",
             description="Echo whatever the current leader types in channel, in a distinctive color", sub_command="mgn")
    def leader_echo_command(self, request, _2, switch_to):
        if self.leader:
            if self.can_use_command(request.sender.char_id):
                self.echo = switch_to == "on"
                return f"Raidleader echo for <highlight>{self.leader.name}<end> has been turned " \
                       f"<highlight>{switch_to}<end>."
            else:
                return "Insufficient access level."
        elif self.leader is None and switch_to == "on":
            return "No current leader set, can't turn on leader echo."

    @command(command="leader", params=[Const("echo")], access_level="leader",
             description="See the current status for leader echoing", sub_command="mgn")
    def leader_echo_status_command(self, _1, _2):
        if self.leader:
            on_off = "on" if self.echo else "off"
            return f"<highlight>{self.leader.name}<end> is set as leader, leader echo is <highlight>{on_off}<end>"
        else:
            return "No current leader set, echo is off."

    @command(command="leader", params=[Const("clear")], access_level="leader",
             description="Clear the current raid leader", sub_command="mgn")
    def leader_clear_command(self, request, _):
        return self.set_raid_leader(request.sender, None)

    @command(command="leader", params=[Const("set")], access_level="leader", sub_command="mgn",
             description="Set (or unset) yourself as raid leader")
    def leader_set_self_command(self, request, _):
        return self.set_raid_leader(request.sender, request.sender)

    @command(command="leader",
             params=[Const("set", is_optional=True), Character("character")],
             access_level="leader",
             sub_command="mgn1",
             description="Set another character as raid leader")
    def leader_set_other_command(self, request, _, char):
        if not char.char_id:
            return "Could not find <highlight>{player}<end>.".format(player=char)

        return self.set_raid_leader(request.sender, char)

    @timerevent(budatime="15m", description="Remove raid leader if raid leader hasn't been active for more than 1 hour")
    def purge_leader_on_inactivity(self, _1, _2):
        if self.last_activity:
            if self.last_activity - int(time.time()) > 3600:
                self.leader = None
                self.last_activity = None
                self.echo = False

                self.bot.send_private_channel_message("Raid leader has been cleared automatically "
                                                      "because of inactivity.")

    @event(PrivateChannelService.LEFT_PRIVATE_CHANNEL_EVENT, "Remove raid leader if raid leader leaves private channel")
    def leader_remove_on_leave_private(self, _, event_data):
        if self.leader:
            if self.leader.char_id == event_data.char_id:
                self.leader = None
                self.last_activity = None
                self.echo = False
                self.bot.send_private_channel_message(
                    f"{self.character_service.resolve_char_to_name(event_data.char_id)} "
                    f"left private channel, and has been cleared as raid leader.")

    @event(PrivateChannelService.PRIVATE_CHANNEL_MESSAGE_EVENT, "Echo leader messages from private channel",
           is_hidden=True)
    def leader_echo_private_event(self, _, event_data):
        if self.leader and self.echo:
            if self.leader.char_id == event_data.char_id:
                if self.setting_service.get("symbol").get_value() != event_data.message[0]:
                    self.leader_echo(event_data.char_id, event_data.message)

    def leader_echo(self, char_id, message):
        sender = self.character_service.resolve_char_to_name(char_id)
        color = self.setting_service.get("leader_echo_color").get_value()

        self.bot.send_private_channel_message(f"{sender}: <font color={color}>{message}</font>",
                                              fire_outgoing_event=False)

        self.activity_done()

    def activity_done(self):
        self.last_activity = int(time.time())

    def can_use_command(self, char_id):
        if not self.leader or self.access_service.has_sufficient_access_level(char_id, self.leader.char_id):
            self.activity_done()
            return True

        return False

    def set_raid_leader(self, sender, set_to):
        if set_to is None:
            if not self.leader:
                return "There is no current raid leader."
            elif self.leader.char_id == sender.char_id:
                self.leader = None
                self.echo = False
                return "You have been removed as raid leader."
            elif self.can_use_command(sender.char_id):
                old_leader = self.leader
                self.leader = None
                self.echo = False
                self.bot.send_private_message(old_leader.char_id,
                                              f"You have been removed as raid leader "
                                              f"by <highlight>{sender.name}<end>.")
                return f"You have removed <highlight>{old_leader.name}<end> as raid leader."
            else:
                return f"You do not have a high enough access level to remove raid leader " \
                       f"from <highlight>{self.leader.name}<end>."
        elif sender.char_id == set_to.char_id:
            if not self.leader:
                self.leader = sender
                self.echo = self.setting_service.get("leader_auto_echo").get_value()
                reply = "You have been set as raid leader."
                if self.echo:
                    reply += " Leader echo is <green>enabled<end>."
                return reply
            elif self.leader.char_id == sender.char_id:
                self.leader = None
                self.echo = False
                return "You have been removed as raid leader."
            elif self.can_use_command(sender.char_id):
                old_leader = self.leader
                self.leader = sender
                self.echo = self.setting_service.get("leader_auto_echo").get_value()
                reply = f"<highlight>{sender.name}<end> has taken raid leader from you."
                if self.echo:
                    reply += " Leader echo is <green>enabled<end>."
                self.bot.send_private_message(old_leader.char_id, reply)
                reply = f"You have taken raid leader from <highlight>{old_leader.name}<end>."
                if self.echo:
                    reply += " Leader echo is <green>enabled<end>."
                return reply
            else:
                return f"You do not have a high enough access level to " \
                       f"take raid leader from <highlight>{self.leader.name}<end>."
        else:
            if self.can_use_command(sender.char_id):
                self.leader = set_to
                self.echo = self.setting_service.get("leader_auto_echo").get_value()
                reply = f"<highlight>{sender.name}<end> has set you as raid leader."
                if self.echo:
                    reply += " Leader echo is <green>enabled<end>."
                self.bot.send_private_message(set_to.char_id, reply)
                reply = f"<highlight>{set_to.name}<end> has been set as raid leader by {sender.name}."
                if self.echo:
                    reply += " Leader echo is <green>enabled<end>."
                return reply
            else:
                return f"You do not have a high enough access level to " \
                       f"take raid leader from <highlight>{self.leader.name}<end>."
