using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using PhoenixLib.Events;
using PhoenixLib.Logging;
using WingsEmu.Game._enum;
using WingsEmu.Game._i18n;
using WingsEmu.Game.Extensions;
using WingsEmu.Game.Managers;
using WingsEmu.Game.Networking;
using WingsEmu.Game.RainbowBattle;
using WingsEmu.Game.RainbowBattle.Event;
using WingsEmu.Packets.Enums.Chat;

namespace Plugin.RainbowBattle.RecurrentJob
{
    public class RainbowBattleSystem : BackgroundService
    {
        private static readonly TimeSpan Interval = TimeSpan.FromSeconds(1);
        private static readonly TimeSpan Start = TimeSpan.FromMinutes(5);

        private static List<(TimeSpan, int, TimeType)> _times = new();
        private readonly IAsyncEventPipeline _eventPipeline;
        private readonly IRainbowBattleManager _rainbowBattleManager;
        private readonly ISessionManager _sessionManager;

        public RainbowBattleSystem(IAsyncEventPipeline eventPipeline, IRainbowBattleManager rainbowBattleManager, ISessionManager sessionManager)
        {
            _eventPipeline = eventPipeline;
            _rainbowBattleManager = rainbowBattleManager;
            _sessionManager = sessionManager;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            Log.Info("[RAINBOW_SYSTEM] Start Rainbow Battle system...");

            _times = _rainbowBattleManager.Warnings.ToList();

            while (!stoppingToken.IsCancellationRequested)
            {
                try
                {
                    await MainProcess();
                }
                catch (Exception e)
                {
                    Log.Error("[RAINBOW_SYSTEM] ", e);
                }

                await Task.Delay(Interval, stoppingToken);
            }
        }

        private async Task MainProcess()
        {
            DateTime dateNow = DateTime.UtcNow;
            switch (_rainbowBattleManager.IsRegistrationActive)
            {
                case true:
                    await ProcessRegistration(dateNow);
                    return;
                case false when !_rainbowBattleManager.IsActive:
                    ProcessTime(dateNow);
                    await ProcessStart(dateNow);
                    return;
            }

            if (_rainbowBattleManager.IsActive && !_rainbowBattleManager.RainbowBattleParties.Any())
            {
                _rainbowBattleManager.IsActive = false;
                _times.Clear();
                _times = _rainbowBattleManager.Warnings.ToList();
                return;
            }

            foreach (RainbowBattleParty rainbowBattleParty in _rainbowBattleManager.RainbowBattleParties)
            {
                await ProcessRainbowBattle(rainbowBattleParty, dateNow);
            }
        }

        private async Task ProcessStart(DateTime dateNow)
        {
            if (_rainbowBattleManager.RainbowBattleProcessTime is null)
            {
                return;
            }

            if (dateNow < _rainbowBattleManager.RainbowBattleProcessTime + Start)
            {
                return;
            }

            _times.Clear();
            _times = _rainbowBattleManager.Warnings.ToList();
            await _eventPipeline.ProcessEventAsync(new RainbowBattleStartRegisterEvent());
        }

        private void ProcessTime(DateTime dateNow)
        {
            if (_rainbowBattleManager.RainbowBattleProcessTime is null)
            {
                return;
            }

            if (_times.Count < 1)
            {
                return;
            }

            (TimeSpan, int, TimeType) warning = _times.OrderBy(x => x.Item1).First();

            if (dateNow < _rainbowBattleManager.RainbowBattleProcessTime + warning.Item1)
            {
                return;
            }

            _times.Remove(warning);

            GameDialogKey gameDialogKey = warning.Item3 == TimeType.SECONDS ? GameDialogKey.GAMEEVENT_SHOUTMESSAGE_PREPARATION_SECONDS : GameDialogKey.GAMEEVENT_SHOUTMESSAGE_PREPARATION_MINUTES;

            _sessionManager.Broadcast(x =>
            {
                string rainbowBattleName = x.GetLanguage(GameDialogKey.RAINBOW_BATTLE_EVENT_NAME);
                return x.GenerateMsgPacket(x.GetLanguageFormat(gameDialogKey, rainbowBattleName, warning.Item2), MsgMessageType.Middle);
            });

            _sessionManager.Broadcast(x =>
            {
                string rainbowBattleName = x.GetLanguage(GameDialogKey.RAINBOW_BATTLE_EVENT_NAME);
                return x.GenerateMsgPacket(x.GetLanguageFormat(gameDialogKey, rainbowBattleName, warning.Item2), MsgMessageType.BottomCard);
            });
        }

        private async Task ProcessRegistration(DateTime dateTime)
        {
            if (_rainbowBattleManager.RegistrationStartTime > dateTime)
            {
                return;
            }

            await _eventPipeline.ProcessEventAsync(new RainbowBattleStartProcessRegistrationEvent());
        }

        private async Task ProcessRainbowBattle(RainbowBattleParty rainbowBattleParty, DateTime dateTime)
        {
            ProcessStartGame(rainbowBattleParty, dateTime);
            await ProcessFrozenMembers(rainbowBattleParty);
            await TryEndRainbowBattle(rainbowBattleParty, dateTime);
            await TryDestroyRainbowBattle(rainbowBattleParty, dateTime);
            await ProcessTeamPoints(rainbowBattleParty, dateTime);
            await ProcessActivityTeamPoints(rainbowBattleParty, dateTime);
            await ProcessMembersLife(rainbowBattleParty, dateTime);
        }

        private async Task ProcessActivityTeamPoints(RainbowBattleParty rainbowBattleParty, DateTime dateTime)
        {
            if (!rainbowBattleParty.Started || rainbowBattleParty.FinishTime != null)
            {
                return;
            }

            if (rainbowBattleParty.LastActivityPointsTeamAdd > dateTime)
            {
                return;
            }

            rainbowBattleParty.LastActivityPointsTeamAdd = dateTime.AddSeconds(59);
            await _eventPipeline.ProcessEventAsync(new RainbowBattleProcessActivityPointsEvent
            {
                RainbowBattleParty = rainbowBattleParty
            });
        }

        private async Task ProcessFrozenMembers(RainbowBattleParty rainbowBattleParty)
        {
            if (!rainbowBattleParty.Started)
            {
                return;
            }

            await _eventPipeline.ProcessEventAsync(new RainbowBattleUnfreezeProcessEvent
            {
                RainbowBattleParty = rainbowBattleParty
            });
        }

        private async Task ProcessTeamPoints(RainbowBattleParty rainbowBattleParty, DateTime dateTime)
        {
            if (!rainbowBattleParty.Started || rainbowBattleParty.FinishTime != null)
            {
                return;
            }

            if (rainbowBattleParty.LastPointsTeamAdd > dateTime)
            {
                return;
            }

            rainbowBattleParty.LastPointsTeamAdd = dateTime.AddSeconds(30);
            await _eventPipeline.ProcessEventAsync(new RainbowBattleProcessFlagPointsEvent
            {
                RainbowBattleParty = rainbowBattleParty
            });
        }

        private async Task TryEndRainbowBattle(RainbowBattleParty rainbowBattleParty, DateTime dateTime)
        {
            if (!rainbowBattleParty.Started || rainbowBattleParty.EndTime > dateTime || rainbowBattleParty.FinishTime != null)
            {
                return;
            }

            await _eventPipeline.ProcessEventAsync(new RainbowBattleEndEvent
            {
                RainbowBattleParty = rainbowBattleParty
            });
        }

        private async Task TryDestroyRainbowBattle(RainbowBattleParty rainbowBattleParty, DateTime dateTime)
        {
            if (rainbowBattleParty.MapInstance != null && rainbowBattleParty.MapInstance.Sessions.Count < 1)
            {
                Log.Warn("[RAINBOW_SYSTEM] Destroying Rainbow Battle instance");
                await _eventPipeline.ProcessEventAsync(new RainbowBattleDestroyEvent
                {
                    RainbowBattleParty = rainbowBattleParty
                });
                return;
            }

            if (!rainbowBattleParty.Started || rainbowBattleParty.FinishTime == null)
            {
                return;
            }

            if (rainbowBattleParty.FinishTime > dateTime)
            {
                return;
            }

            Log.Warn("[RAINBOW_SYSTEM] Destroying Rainbow Battle instance");
            await _eventPipeline.ProcessEventAsync(new RainbowBattleDestroyEvent
            {
                RainbowBattleParty = rainbowBattleParty
            });
        }

        private async Task ProcessMembersLife(RainbowBattleParty rainbowBattleParty, DateTime dateTime)
        {
            if (!rainbowBattleParty.Started || rainbowBattleParty.FinishTime != null)
            {
                return;
            }

            if (rainbowBattleParty.LastMembersLife > dateTime)
            {
                return;
            }

            rainbowBattleParty.LastMembersLife = dateTime.AddSeconds(2);
            await _eventPipeline.ProcessEventAsync(new RainbowBattleProcessLifeEvent
            {
                RainbowBattleParty = rainbowBattleParty
            });
        }

        private void ProcessStartGame(RainbowBattleParty rainbowBattleParty, in DateTime time)
        {
            if (rainbowBattleParty.Started)
            {
                return;
            }

            if (rainbowBattleParty.StartTime.AddSeconds(5) > time)
            {
                return;
            }

            rainbowBattleParty.Started = true;

            foreach (IClientSession session in rainbowBattleParty.MapInstance.Sessions)
            {
                session.SendCondPacket();
            }

            rainbowBattleParty.MapInstance.Broadcast(x => x.GenerateMsgPacket(x.GetLanguage(GameDialogKey.RAINBOW_BATTLE_SHOUTMESSAGE_START), MsgMessageType.Middle));
        }
    }
}