using Dalamud.Bindings.ImGui;
using Dalamud.Interface.ImGuiSeStringRenderer;
using Dalamud.Interface.Textures.TextureWraps;
using Dalamud.Interface.Utility;
using LightlessSync.Utils;
using Microsoft.Extensions.Logging;
using System.Numerics;

namespace LightlessSync.UI.Tags;

internal static class ProfileTagRenderer
{
    public static Vector2 MeasureTag(
        ProfileTagDefinition tag,
        float scale,
        ImGuiStylePtr style,
        Vector4 fallbackBackground,
        Vector4 fallbackBorder,
        uint defaultTextColorU32,
        List<SeStringUtils.SeStringSegment> segmentBuffer,
        Func<uint, IDalamudTextureWrap?> iconResolver,
        ILogger? logger)
        => RenderTagInternal(tag, Vector2.Zero, scale, default, style, fallbackBackground, fallbackBorder, defaultTextColorU32, segmentBuffer, iconResolver, logger, draw: false);

    public static Vector2 RenderTag(
        ProfileTagDefinition tag,
        Vector2 screenMin,
        float scale,
        ImDrawListPtr drawList,
        ImGuiStylePtr style,
        Vector4 fallbackBackground,
        Vector4 fallbackBorder,
        uint defaultTextColorU32,
        List<SeStringUtils.SeStringSegment> segmentBuffer,
        Func<uint, IDalamudTextureWrap?> iconResolver,
        ILogger? logger)
        => RenderTagInternal(tag, screenMin, scale, drawList, style, fallbackBackground, fallbackBorder, defaultTextColorU32, segmentBuffer, iconResolver, logger, draw: true);

    private static Vector2 RenderTagInternal(
        ProfileTagDefinition tag,
        Vector2 screenMin,
        float scale,
        ImDrawListPtr drawList,
        ImGuiStylePtr style,
        Vector4 fallbackBackground,
        Vector4 fallbackBorder,
        uint defaultTextColorU32,
        List<SeStringUtils.SeStringSegment> segmentBuffer,
        Func<uint, IDalamudTextureWrap?> iconResolver,
        ILogger? logger,
        bool draw)
    {
        segmentBuffer.Clear();

        var padding = new Vector2(10f * scale, 6f * scale);
        var rounding = style.FrameRounding > 0f ? style.FrameRounding : 6f * scale;

        var backgroundColor = tag.BackgroundColor ?? fallbackBackground;
        var borderColor = tag.BorderColor ?? fallbackBorder;
        var textColor = tag.TextColor ?? style.Colors[(int)ImGuiCol.Text];
        var textColorU32 = tag.TextColor.HasValue ? ImGui.ColorConvertFloat4ToU32(tag.TextColor.Value) : defaultTextColorU32;

        string? textContent = tag.Text;
        Vector2 textSize = string.IsNullOrWhiteSpace(textContent) ? Vector2.Zero : ImGui.CalcTextSize(textContent);

        var sePayload = tag.SeStringPayload;
        bool hasSeString = !string.IsNullOrWhiteSpace(sePayload);
        bool useTextureSegments = hasSeString && tag.UseTextureSegments;
        bool useSeRenderer = hasSeString && !useTextureSegments;
        Vector2 seSize = Vector2.Zero;
        List<SeStringUtils.SeStringSegment>? seSegments = null;

        if (hasSeString)
        {
            if (useSeRenderer)
            {
                try
                {
                    var drawParams = new SeStringDrawParams
                    {
                        TargetDrawList = draw ? drawList : default,
                        ScreenOffset = draw ? screenMin + padding : Vector2.Zero,
                        WrapWidth = float.MaxValue
                    };

                    var measure = ImGuiHelpers.CompileSeStringWrapped(sePayload!, drawParams);
                    seSize = measure.Size;
                    if (seSize.Y <= 0f)
                        seSize.Y = ImGui.GetTextLineHeight();

                    textContent = null;
                    textSize = Vector2.Zero;
                }
                catch (Exception ex)
                {
                    logger?.LogDebug(ex, "Failed to compile SeString payload '{Payload}' for profile tag", sePayload);
                    useSeRenderer = false;
                }
            }

            if (!useSeRenderer && useTextureSegments)
            {
                segmentBuffer.Clear();
                if (SeStringUtils.TryResolveSegments(sePayload!, scale, iconResolver, segmentBuffer, out seSize) && segmentBuffer.Count > 0)
                {
                    seSegments = segmentBuffer;
                    textContent = null;
                    textSize = Vector2.Zero;
                }
                else
                {
                    segmentBuffer.Clear();
                    var fallback = SeStringUtils.StripMarkup(sePayload!);
                    if (!string.IsNullOrWhiteSpace(fallback))
                    {
                        textContent = fallback;
                        textSize = ImGui.CalcTextSize(fallback);
                    }
                }
            }
            else if (!useSeRenderer && string.IsNullOrWhiteSpace(textContent))
            {
                var fallback = SeStringUtils.StripMarkup(sePayload!);
                if (!string.IsNullOrWhiteSpace(fallback))
                {
                    textContent = fallback;
                    textSize = ImGui.CalcTextSize(fallback);
                }
            }
        }

        bool drewSeString = useSeRenderer || seSegments is { Count: > 0 };
        var contentHeight = drewSeString ? seSize.Y : textSize.Y;
        if (contentHeight <= 0f)
            contentHeight = ImGui.GetTextLineHeight();

        var contentWidth = drewSeString ? seSize.X : textSize.X;
        if (contentWidth <= 0f)
            contentWidth = textSize.X;
        if (contentWidth <= 0f)
            contentWidth = 40f * scale;

        var tagSize = new Vector2(contentWidth + padding.X * 2f, contentHeight + padding.Y * 2f);

        if (!draw)
        {
            if (seSegments is not null)
                seSegments.Clear();
            return tagSize;
        }

        var rectMin = screenMin;
        var rectMax = rectMin + tagSize;
        drawList.AddRectFilled(rectMin, rectMax, ImGui.ColorConvertFloat4ToU32(backgroundColor), rounding);
        drawList.AddRect(rectMin, rectMax, ImGui.ColorConvertFloat4ToU32(borderColor), rounding);

        var contentStart = rectMin + padding;
        var verticalOffset = (tagSize.Y - padding.Y * 2f - contentHeight) * 0.5f;
        var basePos = new Vector2(contentStart.X, contentStart.Y + MathF.Max(verticalOffset, 0f));

        if (useSeRenderer && sePayload is { Length: > 0 })
        {
            var drawParams = new SeStringDrawParams
            {
                TargetDrawList = drawList,
                ScreenOffset = basePos,
                WrapWidth = float.MaxValue
            };

            try
            {
                ImGuiHelpers.CompileSeStringWrapped(sePayload!, drawParams);
            }
            catch (Exception ex)
            {
                logger?.LogDebug(ex, "Failed to draw SeString payload '{Payload}' for profile tag", sePayload);
                var fallback = !string.IsNullOrWhiteSpace(textContent) ? textContent : SeStringUtils.StripMarkup(sePayload!);
                if (!string.IsNullOrWhiteSpace(fallback))
                    drawList.AddText(basePos, textColorU32, fallback);
            }
        }
        else if (seSegments is { Count: > 0 })
        {
            var segmentX = basePos.X;
            foreach (var segment in seSegments)
            {
                var segmentPos = new Vector2(segmentX, basePos.Y + (contentHeight - segment.Size.Y) * 0.5f);
                switch (segment.Type)
                {
                    case SeStringUtils.SeStringSegmentType.Icon:
                        if (segment.Texture != null)
                        {
                            drawList.AddImage(segment.Texture.Handle, segmentPos, segmentPos + segment.Size);
                        }
                        else if (!string.IsNullOrEmpty(segment.Text))
                        {
                            drawList.AddText(segmentPos, textColorU32, segment.Text);
                        }
                        break;
                    case SeStringUtils.SeStringSegmentType.Text:
                        var colorU32 = segment.Color.HasValue
                            ? ImGui.ColorConvertFloat4ToU32(segment.Color.Value)
                            : textColorU32;
                        drawList.AddText(segmentPos, colorU32, segment.Text ?? string.Empty);
                        break;
                }

                segmentX += segment.Size.X;
            }

            seSegments.Clear();
        }
        else if (!string.IsNullOrWhiteSpace(textContent))
        {
            drawList.AddText(basePos, textColorU32, textContent);
        }
        else
        {
            drawList.AddText(basePos, textColorU32, string.Empty);
        }

        return tagSize;
    }
}
