import time

from core.chat_blob import ChatBlob
from core.command_alias_service import CommandAliasService
from core.command_param_types import Any
from core.decorators import instance, command, event, setting
from core.dict_object import DictObject
from core.igncore import IgnCore
from core.job_scheduler import JobScheduler
from core.logger import Logger
from core.message_hub_service import MessageHubService
from core.setting_service import SettingService
from core.setting_types import BooleanSettingType, TextSettingType
from core.text import Text
from core.util import Util
from modules.standard.datanet.ws_controller import WebsocketRelayController


@instance()
class WorldBossController:
    # Timers are provided through a local websocket relay, which gets fed by an external API.
    # example timer data:
    # [{"name":"Tarasque","time": <mortal time>},
    # {"name":"Vizaresh","time": <mortal time>}]
    timer_data = []

    alerts = [480 * 60, 360 * 60, 240 * 60, 120 * 60, 60 * 60, 60 * 15,
              60 * 5, 60 * 3, 60 * 2, 60, 30, 15, 5, 0]
    jobs = []

    def inject(self, registry):
        self.logger = Logger(__name__)
        self.bot: IgnCore = registry.get_instance("bot")
        self.text: Text = registry.get_instance("text")
        self.util: Util = registry.get_instance("util")
        self.command_alias_service: CommandAliasService = registry.get_instance("command_alias_service")
        self.job_scheduler: JobScheduler = registry.get_instance("job_scheduler")
        self.setting_service: SettingService = registry.get_instance("setting_service")
        self.relay_hub: MessageHubService = registry.get_instance("message_hub_service")

    def pre_start(self):
        self.relay_hub.register_message_source("timers")

        self.setting_service.register(self.module_name, 'timer_spam', False, BooleanSettingType(),
                                      "should timers be spammed")
        self.command_alias_service.add_alias("tara", "wb tara")
        self.command_alias_service.add_alias("gaunt", "wb gaunt")
        self.command_alias_service.add_alias("loren", "wb loren")
        self.command_alias_service.add_alias("reaper", "wb reaper")

    @event(WebsocketRelayController.WS_RELAY, "save most current local_timers")
    def get_timer(self, _, data):
        if data.type != "timer":
            return
        local_timers = {}
        spam = True if self.setting_service.get_value("timer_spam") == "1" else False
        for x in self.timer_data:
            local_timers[x['name']] = x['time']
        for row in data.payload:
            spawn = self.get_spawn(row)
            if not spawn:
                continue
            if row['name'] not in local_timers:
                self.timer_data.append(row)
                if spam:
                    for x in self.jobs:
                        if x['name'] == row['name']:
                            self.job_scheduler.cancel_job(x['id'])
                    self.jobs.append(
                        {'name': row['name'], 'id': self.job_scheduler.delayed_job(self.timer_alert, 2, spawn)})
                continue
            elif (local_timers[row['name']] + 1) < row['time']:
                for timer in self.timer_data:
                    if timer['name'] == row['name']:
                        timer['time'] = row['time']
                if spam:
                    for job in self.jobs:
                        if job['name'] == row['name']:
                            self.job_scheduler.cancel_job(job['id'])
                            alert_duration = self.get_next_alert(spawn.at - time.time())
                            job_id = self.job_scheduler.scheduled_job(self.timer_alert, 2,
                                                                      spawn)
                            job['id'] = job_id

    def get_spawn(self, timer):
        timer = DictObject(timer)
        if timer.name == "Loren Warr":
            data = self.calc_spawn_mortal(timer.time, 9 * 60 * 60, 15 * 60)
        elif timer.name == "Tarasque":
            data = self.calc_spawn_mortal(timer.time, 9 * 60 * 60, 30 * 60)
        elif timer.name == "The Hollow Reaper":
            data = self.calc_spawn_mortal(timer.time, 9 * 60 * 60, 15 * 60)
        elif timer.name == "Vizaresh":
            data = self.calc_spawn_mortal(timer.time, 17 * 60 * 60, 6 * 60)
        else:
            return None
        if not data:
            return
        if data.spawn < time.time():
            if data.mortal > time.time():
                return DictObject(
                    {'name': timer.name, 'type': 'mortal', 'time': data.mortal - time.time(), 'at': data.mortal,
                     'data': data})
        return DictObject(
            {'name': timer.name, 'type': 'spawn', 'time': data.spawn - time.time(), 'at': data.spawn, 'data': data})

    def calc_spawn_mortal(self, last, respawn, immortal):
        pop = last + respawn
        attackable = pop + immortal
        now = time.time()

        # Mortal
        if immortal > attackable - now > 0:
            return DictObject({'spawn': pop, 'mortal': attackable})
        elif immortal > attackable + respawn - now > 0:
            return DictObject({'spawn': pop + respawn, 'mortal': attackable + respawn})

        # Spawn
        elif respawn > (pop - now) > 0:
            return DictObject({'spawn': pop, 'mortal': pop})
        elif respawn > pop + respawn - now > 0:
            return DictObject({'spawn': attackable + respawn, 'mortal': attackable + respawn + immortal})
        elif last - now > 0:
            return DictObject({'spawn': last - immortal, 'mortal': last})

    def send_warn(self, msg):
        self.relay_hub.send_message("timers", None, f"[<red>WB</red>] {msg}", f"[<red>WB</red>] {msg}")

    def get_next_alert(self, duration):
        for alert in self.alerts:
            if duration - 1 > alert:
                return duration - alert - 1
        return duration

    @command(command="wb", params=[Any("worldboss", is_optional=True)],
             description="Displays the next worldboss spawns", access_level="member")
    def show_worldboss(self, request, boss: str):
        if boss:
            boss = boss.lower()
            if boss in ["tara", "tarasque"]:
                boss = "Tarasque"
            elif boss in ["viza", "vizaresh", "gaunt", "gauntlet"]:
                boss = "Vizaresh"
            elif boss in ["loren", "loren warr", "loren war", "lw"]:
                boss = "Loren Warr"
            elif boss in ["thr", "the hollow", "the hollow reaper", "reaper"]:
                boss = "The Hollow Reaper"
            for x in self.timer_data:
                if x['name'] == boss:
                    return self.show_user(x)
        else:
            blob = "\n".join([self.show_user(x) for x in self.timer_data if
                              self.show_user(x) != "No timers cached; please try again later."])
            return ChatBlob("Next Worldboss spawns", blob)

    def show_user(self, timer):
        timer = self.get_spawn(timer)
        if not timer:
            return "No timers cached; please try again later."
        if timer.type == "mortal":
            return f"<highlight>{timer.name}</highlight> :: mortal in {self.util.format_time(timer.time)}"
        elif timer.type == "spawn":
            return f"<highlight>{timer.name}</highlight> :: spawn in {self.util.format_time(timer.time)}"

    def timer_alert(self, t, timer):
        if self.setting_service.get_value("timer_spam") == "0":
            return
        for row in self.timer_data:
            if row["name"] == timer.name:
                timer = self.get_spawn(row)
        if not timer.at:
            return
        alert_duration = self.get_next_alert(timer.at - time.time())
        if timer.at - time.time() < 1:
            if timer.type == "mortal":
                self.send_warn(f"<highlight>{timer.name}</highlight> :: is now mortal")
                self.jobs = [x for x in self.jobs if x['name'] != timer.name]
            elif timer.type == "spawn":
                self.send_warn(f"<highlight>{timer.name}</highlight> :: has just spawned")
                self.jobs = [x for x in self.jobs if x['name'] != timer.name]
        else:
            if timer.type == "mortal":
                self.send_warn(f"<highlight>{timer.name}</highlight> :: mortal in {self.util.format_time(timer.time)}")
            elif timer.type == "spawn":
                if alert_duration > 60 * 2:
                    self.send_warn(
                        f"<highlight>{timer.name}</highlight> :: spawn in {self.util.format_time(timer.time)}")
        for row in self.timer_data:
            if row["name"] == timer.name:
                timer = self.get_spawn(row)
        job_id = self.job_scheduler.scheduled_job(self.timer_alert, time.time() + alert_duration, timer)
        for job in self.jobs:
            if job['name'] == timer.name:
                job['id'] = job_id
                return
        self.jobs.append({'name': timer.name, 'id': job_id})

    # @setting('alert_times', '[480 * 60, 360 * 60, 240 * 60, 120 * 60, 60 * 60, 60 * 15, 60 * 5, 60 * 3, 60 * 2, 60, 30, 15, 5, 0]', 'Worldboss timer spam messages (ETA and actual)')
    # def alert_times(self):
    #     return TextSettingType(['[480 * 60, 360 * 60, 240 * 60, 120 * 60, 60 * 60, 60 * 15, 60 * 5, 60 * 3, 60 * 2, 60, 30, 15, 5, 0]'])

