using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.ManagedFontAtlas;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using System.Numerics;

namespace LightlessSync.UI.Style;

/// <summary>
/// A reusable animated header component with a gradient background, some funny stars, and shooting star effects to match the lightless void theme a bit.
/// </summary>
public class AnimatedHeader
{
    private struct Particle
    {
        public Vector2 Position;
        public Vector2 Velocity;
        public float Life;
        public float MaxLife;
        public float Size;
        public ParticleType Type;
        public List<Vector2>? Trail;
        public float Twinkle;
        public float Depth;
        public float Hue;
    }

    private enum ParticleType
    {
        TwinklingStar,
        ShootingStar
    }

    private readonly List<Particle> _particles = [];
    private float _particleSpawnTimer;
    private readonly Random _random = new();

    private const float _particleSpawnInterval = 0.2f;
    private const int _maxParticles = 50;
    private const int _maxTrailLength = 50;
    private const float _edgeFadeDistance = 30f;
    private const float _extendedParticleHeight = 40f;

    public float Height { get; set; } = 150f;
    public Vector4 TopColor { get; set; } = new(0.08f, 0.05f, 0.15f, 1.0f);
    public Vector4 BottomColor { get; set; } = new(0.12f, 0.08f, 0.20f, 1.0f);
    public bool EnableParticles { get; set; } = true;
    public bool EnableBottomGradient { get; set; } = true;

    /// <summary>
    /// Draws the animated header with some customizable content
    /// </summary>
    /// <param name="width">Width of the header</param>
    /// <param name="drawContent">Action to draw custom content inside the header</param>
    public void Draw(float width, Action<Vector2, Vector2> drawContent)
    {
        var windowPos = ImGui.GetWindowPos();
        var windowPadding = ImGui.GetStyle().WindowPadding;

        var headerStart = windowPos + new Vector2(windowPadding.X, windowPadding.Y);
        var headerEnd = headerStart + new Vector2(width, Height);
        var extendedParticleSize = new Vector2(width, Height + _extendedParticleHeight);

        DrawGradientBackground(headerStart, headerEnd);
        
        if (EnableParticles)
        {
            DrawParticleEffects(headerStart, extendedParticleSize);
        }
        
        drawContent(headerStart, headerEnd);
        
        if (EnableBottomGradient)
        {
            DrawBottomGradient(headerStart, headerEnd, width);
        }
    }

    /// <summary>
    /// Draws a simple animated header with title and subtitle.
    /// </summary>
    public void DrawSimple(float width, string title, string subtitle, IFontHandle? titleFont = null, Vector4? titleColor = null, Vector4? subtitleColor = null)
    {
        Draw(width, (headerStart, headerEnd) =>
        {
            var textX = 20f;
            var textY = 30f;

            ImGui.SetCursorScreenPos(headerStart + new Vector2(textX, textY));

            if (titleFont != null)
            {
                using (titleFont.Push())
                {
                    ImGui.TextColored(titleColor ?? new Vector4(0.95f, 0.95f, 0.95f, 1.0f), title);
                }
            }
            else
            {
                ImGui.TextColored(titleColor ?? new Vector4(0.95f, 0.95f, 0.95f, 1.0f), title);
            }

            ImGui.SetCursorScreenPos(headerStart + new Vector2(textX, textY + 45f));
            ImGui.TextColored(subtitleColor ?? UIColors.Get("LightlessBlue"), subtitle);
        });
    }

    /// <summary>
    /// Draws a header with title, subtitle, and action buttons in the top-right corner.
    /// </summary>
    public void DrawWithButtons(float width, string title, string subtitle, List<HeaderButton> buttons, IFontHandle? titleFont = null)
    {
        Draw(width, (headerStart, headerEnd) =>
        {
            // Draw title and subtitle
            var textX = 20f;
            var textY = 30f;

            ImGui.SetCursorScreenPos(headerStart + new Vector2(textX, textY));

            if (titleFont != null)
            {
                using (titleFont.Push())
                {
                    ImGui.TextColored(new Vector4(0.95f, 0.95f, 0.95f, 1.0f), title);
                }
            }
            else
            {
                ImGui.TextColored(new Vector4(0.95f, 0.95f, 0.95f, 1.0f), title);
            }

            ImGui.SetCursorScreenPos(headerStart + new Vector2(textX, textY + 45f));
            ImGui.TextColored(UIColors.Get("LightlessBlue"), subtitle);

            // Draw buttons
            if (buttons.Count > 0)
            {
                DrawHeaderButtons(headerStart, width, buttons);
            }
        });
    }

    private void DrawGradientBackground(Vector2 headerStart, Vector2 headerEnd)
    {
        var drawList = ImGui.GetWindowDrawList();

        drawList.AddRectFilledMultiColor(
            headerStart,
            headerEnd,
            ImGui.GetColorU32(TopColor),
            ImGui.GetColorU32(TopColor),
            ImGui.GetColorU32(BottomColor),
            ImGui.GetColorU32(BottomColor)
        );

        // Draw static background stars
        var random = new Random(42);
        for (int i = 0; i < 50; i++)
        {
            var starPos = headerStart + new Vector2(
                (float)random.NextDouble() * (headerEnd.X - headerStart.X),
                (float)random.NextDouble() * (headerEnd.Y - headerStart.Y)
            );
            var brightness = 0.3f + (float)random.NextDouble() * 0.4f;
            drawList.AddCircleFilled(starPos, 1f, ImGui.GetColorU32(new Vector4(1f, 1f, 1f, brightness)));
        }
    }

    private void DrawBottomGradient(Vector2 headerStart, Vector2 headerEnd, float width)
    {
        var drawList = ImGui.GetWindowDrawList();
        var gradientHeight = 60f;

        for (int i = 0; i < gradientHeight; i++)
        {
            var progress = i / gradientHeight;
            var smoothProgress = progress * progress;
            var r = BottomColor.X + (0.0f - BottomColor.X) * smoothProgress;
            var g = BottomColor.Y + (0.0f - BottomColor.Y) * smoothProgress;
            var b = BottomColor.Z + (0.0f - BottomColor.Z) * smoothProgress;
            var alpha = 1f - smoothProgress;
            var gradientColor = new Vector4(r, g, b, alpha);
            drawList.AddLine(
                new Vector2(headerStart.X, headerEnd.Y + i),
                new Vector2(headerStart.X + width, headerEnd.Y + i),
                ImGui.GetColorU32(gradientColor),
                1f
            );
        }
    }

    private void DrawHeaderButtons(Vector2 headerStart, float headerWidth, List<HeaderButton> buttons)
    {
        var spacing = 8f * ImGuiHelpers.GlobalScale;
        var rightPadding = 15f * ImGuiHelpers.GlobalScale;
        var topPadding = 15f * ImGuiHelpers.GlobalScale;
        var buttonY = headerStart.Y + topPadding;

        using (ImRaii.PushFont(UiBuilder.IconFont))
        {
            // Calculate button size (assuming all buttons are the same size)
            var buttonSize = ImGui.CalcTextSize(FontAwesomeIcon.Globe.ToIconString());
            buttonSize += ImGui.GetStyle().FramePadding * 2;

            float currentX = headerStart.X + headerWidth - rightPadding - buttonSize.X;

            using (ImRaii.PushColor(ImGuiCol.Button, new Vector4(0, 0, 0, 0)))
            using (ImRaii.PushColor(ImGuiCol.ButtonHovered, UIColors.Get("LightlessPurple") with { W = 0.3f }))
            using (ImRaii.PushColor(ImGuiCol.ButtonActive, UIColors.Get("LightlessPurpleActive") with { W = 0.5f }))
            {
                for (int i = buttons.Count - 1; i >= 0; i--)
                {
                    var button = buttons[i];
                    ImGui.SetCursorScreenPos(new Vector2(currentX, buttonY));

                    if (ImGui.Button(button.Icon.ToIconString()))
                    {
                        button.OnClick?.Invoke();
                    }

                    if (ImGui.IsItemHovered() && !string.IsNullOrEmpty(button.Tooltip))
                    {
                        ImGui.PushFont(UiBuilder.DefaultFont);
                        ImGui.SetTooltip(button.Tooltip);
                        ImGui.PopFont();
                    }

                    currentX -= buttonSize.X + spacing;
                }
            }
        }
    }

    private void DrawParticleEffects(Vector2 bannerStart, Vector2 bannerSize)
    {
        var deltaTime = ImGui.GetIO().DeltaTime;
        _particleSpawnTimer += deltaTime;

        if (_particleSpawnTimer > _particleSpawnInterval && _particles.Count < _maxParticles)
        {
            SpawnParticle(bannerSize);
            _particleSpawnTimer = 0f;
        }

        if (_random.NextDouble() < 0.003)
        {
            SpawnShootingStar(bannerSize);
        }

        var drawList = ImGui.GetWindowDrawList();

        for (int i = _particles.Count - 1; i >= 0; i--)
        {
            var particle = _particles[i];

            var screenPos = bannerStart + particle.Position;

            if (particle.Type == ParticleType.ShootingStar && particle.Trail != null)
            {
                particle.Trail.Insert(0, particle.Position);
                if (particle.Trail.Count > _maxTrailLength)
                    particle.Trail.RemoveAt(particle.Trail.Count - 1);
            }

            if (particle.Type == ParticleType.TwinklingStar)
            {
                particle.Twinkle += 0.005f * particle.Depth;
            }

            particle.Position += particle.Velocity * deltaTime;
            particle.Life -= deltaTime;

            var isOutOfBounds = particle.Position.X < -50 || particle.Position.X > bannerSize.X + 50 ||
                                particle.Position.Y < -50 || particle.Position.Y > bannerSize.Y + 50;

            if (particle.Life <= 0 || (particle.Type != ParticleType.TwinklingStar && isOutOfBounds))
            {
                _particles.RemoveAt(i);
                continue;
            }

            if (particle.Type == ParticleType.TwinklingStar)
            {
                if (particle.Position.X < 0 || particle.Position.X > bannerSize.X)
                    particle.Velocity = particle.Velocity with { X = -particle.Velocity.X };
                if (particle.Position.Y < 0 || particle.Position.Y > bannerSize.Y)
                    particle.Velocity = particle.Velocity with { Y = -particle.Velocity.Y };
            }

            var fadeIn = Math.Min(1f, (particle.MaxLife - particle.Life) / 20f);
            var fadeOut = Math.Min(1f, particle.Life / 20f);
            var lifeFade = Math.Min(fadeIn, fadeOut);

            var edgeFadeX = Math.Min(
                Math.Min(1f, (particle.Position.X + _edgeFadeDistance) / _edgeFadeDistance),
                Math.Min(1f, (bannerSize.X - particle.Position.X + _edgeFadeDistance) / _edgeFadeDistance)
            );
            var edgeFadeY = Math.Min(
                Math.Min(1f, (particle.Position.Y + _edgeFadeDistance) / _edgeFadeDistance),
                Math.Min(1f, (bannerSize.Y - particle.Position.Y + _edgeFadeDistance) / _edgeFadeDistance)
            );
            var edgeFade = Math.Min(edgeFadeX, edgeFadeY);

            var baseAlpha = lifeFade * edgeFade;
            var finalAlpha = particle.Type == ParticleType.TwinklingStar
                ? baseAlpha * (0.6f + 0.4f * MathF.Sin(particle.Twinkle))
                : baseAlpha;

            if (particle.Type == ParticleType.ShootingStar && particle.Trail != null && particle.Trail.Count > 1)
            {
                var cyanColor = new Vector4(0.4f, 0.8f, 1.0f, 1.0f);

                for (int t = 1; t < particle.Trail.Count; t++)
                {
                    var trailProgress = (float)t / particle.Trail.Count;
                    var trailAlpha = Math.Min(1f, (1f - trailProgress) * finalAlpha * 1.8f);
                    var trailWidth = (1f - trailProgress) * 3f + 1f;

                    var glowAlpha = trailAlpha * 0.4f;
                    drawList.AddLine(
                        bannerStart + particle.Trail[t - 1],
                        bannerStart + particle.Trail[t],
                        ImGui.GetColorU32(cyanColor with { W = glowAlpha }),
                        trailWidth + 4f
                    );

                    drawList.AddLine(
                        bannerStart + particle.Trail[t - 1],
                        bannerStart + particle.Trail[t],
                        ImGui.GetColorU32(cyanColor with { W = trailAlpha }),
                        trailWidth
                    );
                }
            }
            else if (particle.Type == ParticleType.TwinklingStar)
            {
                DrawTwinklingStar(drawList, screenPos, particle.Size, particle.Hue, finalAlpha, particle.Depth);
            }

            _particles[i] = particle;
        }
    }

    private static void DrawTwinklingStar(ImDrawListPtr drawList, Vector2 position, float size, float hue, float alpha, float depth)
    {
        var color = HslToRgb(hue, 1.0f, 0.85f);
        color.W = alpha;

        drawList.AddCircleFilled(position, size, ImGui.GetColorU32(color));

        var glowColor = color with { W = alpha * 0.3f };
        drawList.AddCircleFilled(position, size * (1.2f + depth * 0.3f), ImGui.GetColorU32(glowColor));
    }

    private static Vector4 HslToRgb(float h, float s, float l)
    {
        h = h / 360f;
        float c = (1 - MathF.Abs(2 * l - 1)) * s;
        float x = c * (1 - MathF.Abs((h * 6) % 2 - 1));
        float m = l - c / 2;

        float r, g, b;
        if (h < 1f / 6f)
        {
            r = c; g = x; b = 0;
        }
        else if (h < 2f / 6f)
        {
            r = x; g = c; b = 0;
        }
        else if (h < 3f / 6f)
        {
            r = 0; g = c; b = x;
        }
        else if (h < 4f / 6f)
        {
            r = 0; g = x; b = c;
        }
        else if (h < 5f / 6f)
        {
            r = x; g = 0; b = c;
        }
        else
        {
            r = c; g = 0; b = x;
        }

        return new Vector4(r + m, g + m, b + m, 1.0f);
    }

    private void SpawnParticle(Vector2 bannerSize)
    {
        var position = new Vector2(
            (float)_random.NextDouble() * bannerSize.X,
            (float)_random.NextDouble() * bannerSize.Y
        );

        var depthLayers = new[] { 0.5f, 1.0f, 1.5f };
        var depth = depthLayers[_random.Next(depthLayers.Length)];

        var velocity = new Vector2(
            ((float)_random.NextDouble() - 0.5f) * 0.05f * depth,
            ((float)_random.NextDouble() - 0.5f) * 0.05f * depth
        );

        var isBlue = _random.NextDouble() < 0.5;
        var hue = isBlue ? 220f + (float)_random.NextDouble() * 30f : 270f + (float)_random.NextDouble() * 40f;
        var size = (0.5f + (float)_random.NextDouble() * 2f) * depth;
        var maxLife = 120f + (float)_random.NextDouble() * 60f;

        _particles.Add(new Particle
        {
            Position = position,
            Velocity = velocity,
            Life = maxLife,
            MaxLife = maxLife,
            Size = size,
            Type = ParticleType.TwinklingStar,
            Trail = null,
            Twinkle = (float)_random.NextDouble() * MathF.PI * 2,
            Depth = depth,
            Hue = hue
        });
    }

    private void SpawnShootingStar(Vector2 bannerSize)
    {
        var maxLife = 80f + (float)_random.NextDouble() * 40f;
        var startX = bannerSize.X * (0.3f + (float)_random.NextDouble() * 0.6f);
        var startY = -10f;

        _particles.Add(new Particle
        {
            Position = new Vector2(startX, startY),
            Velocity = new Vector2(
                -50f - (float)_random.NextDouble() * 40f,
                30f + (float)_random.NextDouble() * 40f
            ),
            Life = maxLife,
            MaxLife = maxLife,
            Size = 2.5f,
            Type = ParticleType.ShootingStar,
            Trail = new List<Vector2>(),
            Twinkle = 0,
            Depth = 1.0f,
            Hue = 270f
        });
    }

    /// <summary>
    /// Clears all active particles. Useful when closing or hiding a window with an animated header.
    /// </summary>
    public void ClearParticles()
    {
        _particles.Clear();
        _particleSpawnTimer = 0f;
    }
}

/// <summary>
/// Represents a button in the animated header.
/// </summary>
public record HeaderButton(FontAwesomeIcon Icon, string Tooltip, Action? OnClick = null);
