using Dalamud.Game.ClientState.JobGauge.Types;
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Game.ClientState.Statuses;
using HSUI.Config;
using HSUI.Config.Attributes;
using HSUI.Helpers;
using HSUI.Interface.Bars;
using HSUI.Interface.GeneralElements;
using FFXIVClientStructs.FFXIV.Client.Game.Gauge;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;

namespace HSUI.Interface.Jobs
{
    public class NinjaHud : JobHud
    {
        private new NinjaConfig Config => (NinjaConfig)_config;

        public NinjaHud(NinjaConfig config, string? displayName = null) : base(config, displayName) { }

        protected override (List<Vector2>, List<Vector2>) ChildrenPositionsAndSizes()
        {
            List<Vector2> positions = new List<Vector2>();
            List<Vector2> sizes = new List<Vector2>();

            if (Config.KazematoiBar.Enabled)
            {
                positions.Add(Config.Position + Config.KazematoiBar.Position);
                sizes.Add(Config.KazematoiBar.Size);
            }

            if (Config.NinkiBar.Enabled)
            {
                positions.Add(Config.Position + Config.NinkiBar.Position);
                sizes.Add(Config.NinkiBar.Size);
            }

            if (Config.TrickAttackBar.Enabled)
            {
                positions.Add(Config.Position + Config.TrickAttackBar.Position);
                sizes.Add(Config.TrickAttackBar.Size);
            }

            if (Config.SuitonBar.Enabled)
            {
                positions.Add(Config.Position + Config.SuitonBar.Position);
                sizes.Add(Config.SuitonBar.Size);
            }

            if (Config.MudraBar.Enabled)
            {
                positions.Add(Config.Position + Config.MudraBar.Position);
                sizes.Add(Config.MudraBar.Size);
            }

            return (positions, sizes);
        }

        public override void DrawJobHud(Vector2 origin, IPlayerCharacter player)
        {
            var pos = origin + Config.Position;
            if (Config.MudraBar.Enabled)
            {
                DrawMudraBars(pos, player);
            }

            if (Config.KazematoiBar.Enabled)
            {
                DrawKazematoiBar(pos, player);
            }

            if (Config.NinkiBar.Enabled)
            {
                DrawNinkiGauge(pos, player);
            }

            if (Config.TrickAttackBar.Enabled)
            {
                DrawTrickAttackBar(pos, player);
            }

            if (Config.SuitonBar.Enabled)
            {
                DrawSuitonBar(pos, player);
            }
        }

        public (bool, bool, bool) GetMudraBuffs(IPlayerCharacter? player, out IStatus? ninjutsuBuff, out IStatus? kassatsuBuff, out IStatus? tcjBuff)
        {
            ninjutsuBuff = null;
            kassatsuBuff = null;
            tcjBuff = null;

            if (player is not null)
            {
                var statusList = Utils.StatusListForBattleChara(player);
                foreach (IStatus status in statusList)
                {
                    if (status.StatusId == 496) { ninjutsuBuff = status; }
                    if (status.StatusId == 497) { kassatsuBuff = status; }
                    if (status.StatusId == 1186) { tcjBuff = status; }
                }
            }

            return (ninjutsuBuff is not null, kassatsuBuff is not null, tcjBuff is not null);
        }

        private void DrawMudraBars(Vector2 origin, IPlayerCharacter player)
        {
            var (hasNinjutsuBuff, hasKassatsuBuff, hasTCJBuff) = GetMudraBuffs(player, out IStatus? ninjutsuBuff, out IStatus? kassatsuBuff, out IStatus? tcjBuff);

            int mudraStacks = SpellHelper.Instance.GetStackCount(2, 2259);
            float mudraCooldown = SpellHelper.Instance.GetSpellCooldown(2259);

            float current = 0f;
            float max = 0f;

            // For some reason, the mudras may be on cooldown before the "Mudra" buff is applied.
            // Mudra stack count is set to -2 when a mudra is in the middle of its re-cast timer, so we can check for that instead.
            bool inNinjutsu = mudraStacks == -2 || hasNinjutsuBuff;

            if (hasTCJBuff || hasKassatsuBuff || inNinjutsu)
            {
                if (hasTCJBuff)
                {
                    max = 6f;
                    current = tcjBuff is null || tcjBuff.RemainingTime < 0 ? max : tcjBuff.RemainingTime;
                    Config.MudraBar.Label.SetText(GenerateNinjutsuText(tcjBuff?.Param ?? 0, hasKassatsuBuff, hasTCJBuff));
                }
                else if (hasKassatsuBuff)
                {
                    max = 15f;
                    current = kassatsuBuff is null || kassatsuBuff.RemainingTime < 0 ? max : kassatsuBuff.RemainingTime;
                    Config.MudraBar.Label.SetText("KASSATSU");
                }

                if (inNinjutsu)
                {
                    max = 6f;
                    current = ninjutsuBuff is null || ninjutsuBuff.RemainingTime < 0 ? max : ninjutsuBuff.RemainingTime;
                    Config.MudraBar.Label.SetText(GenerateNinjutsuText(ninjutsuBuff?.Param ?? 0, hasKassatsuBuff, hasTCJBuff));
                }

                PluginConfigColor fillColor = hasTCJBuff ? Config.MudraBar.TCJBarColor : hasKassatsuBuff ? Config.MudraBar.KassatsuBarColor : Config.MudraBar.FillColor;
                Rect foreground = BarUtilities.GetFillRect(Config.MudraBar.Position, Config.MudraBar.Size, Config.MudraBar.FillDirection, fillColor, current, max);

                BarHud bar = new BarHud(Config.MudraBar, player);
                bar.AddForegrounds(foreground);
                bar.AddLabels(Config.MudraBar.Label);

                AddDrawActions(bar.GetDrawActions(origin, Config.MudraBar.StrataLevel));
            }
            else
            {
                max = 40f;
                current = max - mudraCooldown;

                if (!Config.MudraBar.HideWhenInactive || current < max)
                {
                    Config.MudraBar.Label.SetValue((max - current) % 20);

                    BarHud[] bars = BarUtilities.GetChunkedProgressBars(Config.MudraBar, 2, current, max, 0f, player);
                    foreach (BarHud bar in bars)
                    {
                        AddDrawActions(bar.GetDrawActions(origin, Config.MudraBar.StrataLevel));
                    }
                }
            }
        }

        private unsafe void DrawKazematoiBar(Vector2 origin, IPlayerCharacter player)
        {
            NinjaGauge* gauge = (NinjaGauge*)Plugin.JobGauges.Get<NINGauge>().Address;
            int stacks = gauge->Kazematoi;

            if (Config.KazematoiBar.HideWhenInactive && stacks == 0)
            {
                return;
            }

            BarHud[] bars = BarUtilities.GetChunkedBars(Config.KazematoiBar, 5, stacks, 5, 0, player);
            foreach (BarHud bar in bars)
            {
                AddDrawActions(bar.GetDrawActions(origin, Config.KazematoiBar.StrataLevel));
            }
        }

        private unsafe void DrawNinkiGauge(Vector2 origin, IPlayerCharacter player)
        {
            NinjaGauge* gauge = (NinjaGauge*)Plugin.JobGauges.Get<NINGauge>().Address;
            int ninki = gauge->Ninki;

            if (Config.NinkiBar.HideWhenInactive && ninki == 0)
            {
                return;
            }

            Config.NinkiBar.Label.SetValue(ninki);

            BarHud[] bars = BarUtilities.GetChunkedProgressBars(Config.NinkiBar, 2, ninki, 100, 0, player);
            foreach (BarHud bar in bars)
            {
                AddDrawActions(bar.GetDrawActions(origin, Config.NinkiBar.StrataLevel));
            }
        }

        private void DrawTrickAttackBar(Vector2 origin, IPlayerCharacter player)
        {
            IGameObject? actor = Plugin.TargetManager.SoftTarget ?? Plugin.TargetManager.Target;

            float trickDuration = Utils.StatusListForActor(actor).FirstOrDefault(
                o => o.StatusId is 3254 or 3906 && o.SourceId == player.GameObjectId && o.RemainingTime > 0
            )?.RemainingTime ?? 0f;

            if (Config.TrickAttackBar.HideWhenInactive && trickDuration == 0)
            {
                return;
            }

            Config.TrickAttackBar.Label.SetValue(trickDuration);

            BarHud bar = BarUtilities.GetProgressBar(Config.TrickAttackBar, trickDuration, 15f, 0f, player);
            AddDrawActions(bar.GetDrawActions(origin, Config.TrickAttackBar.StrataLevel));
        }

        private void DrawSuitonBar(Vector2 origin, IPlayerCharacter player)
        {
            float suitonDuration = Utils.StatusListForBattleChara(player).FirstOrDefault(
                o => o.StatusId == 3848 && o.RemainingTime > 0
            )?.RemainingTime ?? 0f;

            if (Config.SuitonBar.HideWhenInactive && suitonDuration == 0)
            {
                return;
            }

            Config.SuitonBar.Label.SetValue(suitonDuration);

            BarHud bar = BarUtilities.GetProgressBar(Config.SuitonBar, suitonDuration, 20f, 0f, player);
            AddDrawActions(bar.GetDrawActions(origin, Config.SuitonBar.StrataLevel));
        }

        private string GenerateNinjutsuText(ushort param, bool haveKassatsuBuff, bool haveTCJBuff)
        {
            return param switch
            {
                1 or 2 or 3 => "FUMA SHURIKEN",
                6 or 7 => haveKassatsuBuff ? "GOKA MEKKYAKU" : "KATON",
                9 or 11 => "RAITON",
                13 or 14 => haveKassatsuBuff ? "HYOSHO RANRYU" : "HYOTON",
                27 or 30 => "HUTON",
                39 or 45 => "DOTON",
                54 or 57 => "SUITON",
                _ => haveTCJBuff ? "TEN CHI JIN" : "",
            };
        }
    }

    [Section("Job Specific Bars")]
    [SubSection("Melee", 0)]
    [SubSection("Ninja", 1)]
    public class NinjaConfig : JobConfig
    {
        [JsonIgnore] public override uint JobId => JobIDs.NIN;

        public new static NinjaConfig DefaultConfig()
        {
            var config = new NinjaConfig();

            config.MudraBar.Label.FontID = FontsConfig.DefaultMediumFontKey;
            config.MudraBar.LabelMode = LabelMode.ActiveChunk;

            config.TrickAttackBar.Label.FontID = FontsConfig.DefaultMediumFontKey;
            config.TrickAttackBar.Enabled = false;

            config.SuitonBar.Label.FontID = FontsConfig.DefaultMediumFontKey;
            config.SuitonBar.Enabled = false;

            config.NinkiBar.UsePartialFillColor = true;

            return config;
        }

        [NestedConfig("Mudra Bar", 30)]
        public NinjaMudraBarConfig MudraBar = new NinjaMudraBarConfig(
            new(0, -50),
            new(254, 10),
            new PluginConfigColor(new Vector4(211f / 255f, 166f / 255f, 75f / 242f, 100f / 100f))
        );

        [NestedConfig("Kazematoi Bar", 35)]
        public ChunkedBarConfig KazematoiBar = new ChunkedBarConfig(
            new(0, -10),
            new(254, 20),
            PluginConfigColor.FromHex(0xFFABB6BD)
        );


        [NestedConfig("Ninki Bar", 40)]
        public ChunkedProgressBarConfig NinkiBar = new ChunkedProgressBarConfig(
            new(0, -32),
            new(254, 20),
            new PluginConfigColor(new Vector4(137f / 255f, 82f / 255f, 236f / 255f, 100f / 100f))
        );

        [NestedConfig("Trick Attack Bar", 45)]
        public ProgressBarConfig TrickAttackBar = new ProgressBarConfig(
            new(0, -63),
            new(254, 10),
            new PluginConfigColor(new Vector4(191f / 255f, 40f / 255f, 0f / 255f, 100f / 100f))
        );

        [NestedConfig("Shadow Walker Bar", 50)]
        public ProgressBarConfig SuitonBar = new ProgressBarConfig(
            new(0, -75),
            new(254, 10),
            new PluginConfigColor(new Vector4(202f / 255f, 228f / 255f, 246f / 242f, 100f / 100f))
        );
    }

    [Exportable(false)]
    public class NinjaMudraBarConfig : ChunkedProgressBarConfig
    {
        [ColorEdit4("Kassatsu Color", spacing = true)]
        [Order(60)]
        public PluginConfigColor KassatsuBarColor = new(new Vector4(239 / 255f, 123 / 255f, 222 / 242f, 100f / 100f));

        [ColorEdit4("Ten Chi Jin Color")]
        [Order(65)]
        public PluginConfigColor TCJBarColor = new(new Vector4(181 / 255f, 33 / 255f, 41 / 242f, 100f / 100f));

        public NinjaMudraBarConfig(Vector2 position, Vector2 size, PluginConfigColor fillColor) : base(position, size, fillColor, 2)
        {
            Label.Enabled = true;
            UsePartialFillColor = true;
        }
    }
}
