using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using PhoenixLib.Events;
using PhoenixLib.Logging;
using Plugin.Raids.Const;
using WingsEmu.Core.Extensions;
using WingsEmu.Game._i18n;
using WingsEmu.Game.Extensions;
using WingsEmu.Game.Monster.Event;
using WingsEmu.Game.Raids;
using WingsEmu.Game.Raids.Enum;
using WingsEmu.Game.Raids.Events;
using WingsEmu.Packets.Enums.Chat;

namespace Plugin.Raids.RecurrentJob;

public class RaidSystem : BackgroundService
{
    private static readonly TimeSpan Interval = TimeSpan.FromSeconds(1);
    private readonly IAsyncEventPipeline _eventPipeline;
    private readonly IRaidManager _raidManager;

    public RaidSystem(IRaidManager raidManager, IAsyncEventPipeline eventPipeline)
    {
        _raidManager = raidManager;
        _eventPipeline = eventPipeline;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        Log.Info("[RAID_SYSTEM] Started!");
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                foreach (RaidParty raid in _raidManager.Raids.ToArray())
                {
                    await ProcessRaid(raid);
                }
            }
            catch (Exception e)
            {
                Log.Error("[RAID_SYSTEM]", e);
            }

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

    private async Task ProcessRaid(RaidParty raidParty)
    {
        DateTime currentDate = DateTime.UtcNow;
        await TryFinish(raidParty, currentDate);
        await TryRemove(raidParty, currentDate);
        await TryExecuteEventsAfterSlowMo(raidParty, currentDate);
        await TryRefreshInfo(raidParty);
        await TrySpawnMonsterByRaidWave(raidParty, currentDate);
    }

    private async Task TrySpawnMonsterByRaidWave(RaidParty raidParty, DateTime currentDate)
    {
        if (!raidParty.Started || raidParty.Finished || raidParty.Instance == null)
        {
            return;
        }

        if (raidParty.Instance.RaidSubInstances == null)
        {
            return;
        }

        foreach (RaidSubInstance raidSubInstance in raidParty.Instance.RaidSubInstances.Values)
        {
            if (raidSubInstance == null)
            {
                continue;
            }

            if (!raidSubInstance.RaidWavesActivated)
            {
                continue;
            }

            if (!raidSubInstance.RaidWaves.Any())
            {
                continue;
            }

            if (raidSubInstance.LastRaidWave > currentDate)
            {
                continue;
            }

            RaidWave wave = raidSubInstance.RaidWaves.GetOrDefault(raidSubInstance.RaidWaveState);
            if (wave == null)
            {
                raidSubInstance.RaidWaveState = 0;
                raidSubInstance.LastRaidWave = currentDate;
                continue;
            }

            raidSubInstance.LastRaidWave = currentDate.AddSeconds(wave.TimeInSeconds);
            raidSubInstance.RaidWaveState++;

            await _eventPipeline.ProcessEventAsync(new MonsterSummonEvent(raidSubInstance.MapInstance, wave.Monsters));
            raidSubInstance.MapInstance.Broadcast(x =>
                x.GenerateMsgPacket(x.GetLanguage(GameDialogKey.RAID_SHOUTMESSAGE_MONSTERS_WAVE), MsgMessageType.Middle));
        }
    }

    private async Task TryFinish(RaidParty raidParty, DateTime currentTime)
    {
        if (!raidParty.Started || raidParty.Finished || raidParty.Instance.FinishDate > currentTime)
        {
            return;
        }

        await _eventPipeline.ProcessEventAsync(new RaidInstanceFinishEvent(raidParty, RaidFinishType.TimeIsUp));
    }

    private async Task TryRemove(RaidParty raidParty, DateTime currentTime)
    {
        if (!raidParty.Started || !raidParty.Finished || raidParty.Instance.RemoveDate > currentTime)
        {
            return;
        }

        Log.Warn($"REMOVING RAID MAP {raidParty.Instance.RemoveDate}");
        await _eventPipeline.ProcessEventAsync(new RaidInstanceDestroyEvent(raidParty));
    }

    private async Task TryExecuteEventsAfterSlowMo(RaidParty raidParty, DateTime currentTime)
    {
        if (!raidParty.Started || !raidParty.Finished || raidParty.Instance.FinishSlowMoDate == null || raidParty.Instance.FinishSlowMoDate > currentTime)
        {
            return;
        }

        raidParty.Instance.SetFinishSlowMoDate(null);
        foreach (RaidSubInstance subInstance in raidParty.Instance.RaidSubInstances.Values)
        {
            await subInstance.TriggerEvents(RaidConstEventKeys.RaidSubInstanceAfterSlowMo);
        }
    }

    private async Task TryRefreshInfo(RaidParty raidParty)
    {
        if (!raidParty.Started || raidParty.Destroy)
        {
            return;
        }

        await _eventPipeline.ProcessEventAsync(new RaidInstanceRefreshInfoEvent(raidParty));
    }
}