using System.Numerics;
using Dalamud;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.FontIdentifier;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using HellionChat.Code;
using HellionChat.Resources;
using HellionChat.Themes;
using HellionChat.Util;
using Microsoft.Extensions.Logging;

namespace HellionChat.Ui.SettingsTabs;

internal sealed class Appearance : ISettingsTab
{
    private Plugin Plugin { get; }
    private Configuration Mutable { get; }
    private readonly ILogger<Appearance> _logger;

    private string? _applyDismissedFor;

    public string Name => HellionStrings.Settings_Tab_Appearance + "###tabs-appearance";

    internal Appearance(Plugin plugin, Configuration mutable, ILogger<Appearance> logger)
    {
        Plugin = plugin;
        Mutable = mutable;
        _logger = logger;
    }

    public void Draw(bool sectionJustEntered)
    {
        DrawThemeSection(sectionJustEntered);
        ImGui.Spacing();
        DrawFontsSection(sectionJustEntered);
        ImGui.Spacing();
        DrawColoursSection(sectionJustEntered);
        ImGui.Spacing();
        DrawWindowStyleSection(sectionJustEntered);
        ImGui.Spacing();
        DrawTimestampSection(sectionJustEntered);
        ImGui.Spacing();
        DrawAnimationsSection(sectionJustEntered);
    }

    // ── Theme ──────────────────────────────────────────────────────────────

    private void DrawThemeSection(bool sectionJustEntered)
    {
        if (sectionJustEntered)
            ImGui.SetNextItemOpen(false);
        using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_Theme);
        if (!tree.Success)
            return;

        using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
        {
            var registry = Plugin.ThemeRegistry;
            var active = registry.Get(Mutable.Theme);

            ImGui.TextUnformatted(
                string.Format(HellionStrings.Settings_Themes_Active, active.Name)
            );
            using (ImRaii.PushColor(ImGuiCol.Text, 0xFF8FA3B5u))
                ImGui.TextUnformatted(active.Author);

            DrawChatColorsApplyBanner(active);

            ImGui.Spacing();
            ImGui.Separator();
            ImGui.Spacing();

            ImGui.TextUnformatted(HellionStrings.Settings_Themes_BuiltIns);
            ImGui.Spacing();
            DrawThemeGrid(registry.AllBuiltIns(), active.Slug);

            var customs = registry.AllCustom().ToList();
            if (customs.Count > 0)
            {
                ImGui.Spacing();
                ImGui.Separator();
                ImGui.Spacing();
                ImGui.TextUnformatted(HellionStrings.Settings_Themes_Custom);
                ImGui.Spacing();
                DrawThemeGrid(customs, active.Slug);
            }

            ImGui.Spacing();
            ImGui.Separator();
            ImGui.Spacing();

            if (ImGui.Button(HellionStrings.Settings_Themes_OpenFolder))
            {
                var dir = Path.Combine(Plugin.Interface.ConfigDirectory.FullName, "themes");
                Directory.CreateDirectory(dir);
                Plugin.PlatformUtil.OpenLink(dir);
            }

            ImGui.SameLine();
            if (ImGui.Button(HellionStrings.Settings_Themes_ExportActive))
            {
                var dir = Path.Combine(Plugin.Interface.ConfigDirectory.FullName, "themes");
                Directory.CreateDirectory(dir);
                var fileName = $"{active.Slug}.export.json";
                var path = Path.Combine(dir, fileName);
                var json = ThemeJsonWriter.Serialize(active);
                File.WriteAllText(path, json);
                _logger.LogInformation($"Exported active theme '{active.Slug}' to {path}");
            }
        }
    }

    private void DrawThemeGrid(IEnumerable<Theme> themes, string activeSlug)
    {
        var avail = ImGui.GetContentRegionAvail();
        var columns = avail.X >= 700f ? 3 : 2;
        var cardWidth = (avail.X - (columns - 1) * 8f) / columns;
        var cardHeight = 140f;

        var list = themes.ToList();
        for (var i = 0; i < list.Count; i++)
        {
            DrawThemeCard(list[i], activeSlug, cardWidth, cardHeight);

            if ((i + 1) % columns != 0 && i != list.Count - 1)
                ImGui.SameLine();
        }
    }

    private void DrawThemeCard(Theme theme, string activeSlug, float w, float h)
    {
        ImGui.BeginGroup();

        var isActive = string.Equals(theme.Slug, activeSlug, StringComparison.OrdinalIgnoreCase);
        var cursorBefore = ImGui.GetCursorScreenPos();
        var clicked = ImGui.InvisibleButton($"##theme-card-{theme.Slug}", new Vector2(w, h));
        var hovered = ImGui.IsItemHovered();

        var draw = ImGui.GetWindowDrawList();
        var bg = ColourUtil.RgbaToAbgr(theme.Colors.WindowBg | 0xFFu);
        draw.AddRectFilled(cursorBefore, cursorBefore + new Vector2(w, h), bg, 4f);

        if (isActive)
        {
            var border = ColourUtil.RgbaToAbgr(theme.Colors.Primary);
            draw.AddRect(
                cursorBefore,
                cursorBefore + new Vector2(w, h),
                border,
                4f,
                ImDrawFlags.None,
                2f
            );
        }
        else if (hovered)
        {
            var border = ColourUtil.RgbaToAbgr(theme.Colors.PrimaryLight & 0xFFFFFF99u);
            draw.AddRect(
                cursorBefore,
                cursorBefore + new Vector2(w, h),
                border,
                4f,
                ImDrawFlags.None,
                1f
            );
        }

        var mockupOrigin = cursorBefore + new Vector2(12f, 12f);
        var mockupSize = new Vector2(w - 24f, 60f);
        ThemeMockup.Draw(mockupOrigin, mockupSize, theme);

        var textColor = ColourUtil.RgbaToAbgr(theme.Colors.TextPrimary);
        var mutedColor = ColourUtil.RgbaToAbgr(theme.Colors.TextMuted);
        draw.AddText(cursorBefore + new Vector2(12f, 80f), textColor, theme.Name);
        draw.AddText(cursorBefore + new Vector2(12f, 100f), mutedColor, theme.Author);

        ImGui.EndGroup();

        if (clicked)
        {
            Mutable.Theme = theme.Slug;
            Plugin.ThemeRegistry.Switch(theme.Slug);
            _applyDismissedFor = null;
        }
    }

    private void DrawChatColorsApplyBanner(Theme active)
    {
        if (active.ChatColors is not { Channels.Count: > 0 } themeChatColors)
            return;

        if (_applyDismissedFor == active.Slug)
            return;

        var alreadyMatching = themeChatColors.Channels.All(kvp =>
            Mutable.ChatColours.TryGetValue(kvp.Key, out var current) && current == kvp.Value
        );
        if (alreadyMatching)
            return;

        ImGui.Spacing();

        var border = ColourUtil.RgbaToAbgr(active.Colors.Primary);
        var bgFill = ColourUtil.RgbaToAbgr((active.Colors.Surface & 0xFFFFFF00u) | 0xCCu);
        var origin = ImGui.GetCursorScreenPos();
        var width = ImGui.GetContentRegionAvail().X;
        var height = 64f;
        var draw = ImGui.GetWindowDrawList();
        draw.AddRectFilled(origin, origin + new Vector2(width, height), bgFill, 4f);
        draw.AddRect(origin, origin + new Vector2(width, height), border, 4f, ImDrawFlags.None, 1f);

        var textColor = ColourUtil.RgbaToAbgr(active.Colors.TextPrimary);
        draw.AddText(
            origin + new Vector2(12f, 10f),
            textColor,
            HellionStrings.Settings_Themes_ApplyChatColors_Hint
        );

        using (ImRaii.PushColor(ImGuiCol.Button, active.Colors.Primary))
        using (ImRaii.PushColor(ImGuiCol.ButtonHovered, active.Colors.PrimaryLight))
        using (ImRaii.PushColor(ImGuiCol.ButtonActive, active.Colors.PrimaryDark))
        {
            ImGui.SetCursorScreenPos(origin + new Vector2(12f, 32f));
            if (ImGui.Button(HellionStrings.Settings_Themes_ApplyChatColors_Apply))
            {
                foreach (var kvp in themeChatColors.Channels)
                    Mutable.ChatColours[kvp.Key] = kvp.Value;
                _applyDismissedFor = active.Slug;
            }
        }

        ImGui.SameLine();
        if (ImGui.Button(HellionStrings.Settings_Themes_ApplyChatColors_Keep))
        {
            _applyDismissedFor = active.Slug;
        }

        ImGui.SetCursorScreenPos(origin + new Vector2(0f, height + 8f));

        ImGui.Spacing();
    }

    // ── Fonts ──────────────────────────────────────────────────────────────
    // R3 deliberately NOT applied here — the UseHellionFont/FontsEnabled
    // visibility chain has priority over type grouping (R4).

    private void DrawFontsSection(bool sectionJustEntered)
    {
        if (sectionJustEntered)
            ImGui.SetNextItemOpen(false);
        using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_Fonts);
        if (!tree.Success)
            return;

        using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
        {
            if (
                ImGui.Checkbox(HellionStrings.Theme_UseHellionFont_Name, ref Mutable.UseHellionFont)
            )
            {
                if (Mutable.UseHellionFont)
                    Mutable.FontsEnabled = false;
            }
            ImGuiUtil.HelpMarker(HellionStrings.Theme_UseHellionFont_Description);
            ImGui.Spacing();

            if (Mutable.UseHellionFont)
            {
                // Bundled-font path: only the base font size matters; the
                // global / japanese / italic chooser pickers do not apply.
                ImGuiUtil.FontSizeCombo(Language.Options_FontSize_Name, ref Mutable.FontSizeV2);
                ImGui.Spacing();
            }
            else
            {
                ImGui.Checkbox(Language.Options_FontsEnabled, ref Mutable.FontsEnabled);
                ImGui.Spacing();
            }

            var unused = false;
            if (!Mutable.UseHellionFont && !Mutable.FontsEnabled)
            {
                ImGuiUtil.FontSizeCombo(Language.Options_FontSize_Name, ref Mutable.FontSizeV2);
            }
            else if (!Mutable.UseHellionFont)
            {
                var globalChooser = ImGuiUtil.FontChooser(
                    Language.Options_Font_Name,
                    Mutable.GlobalFontV2,
                    false,
                    ref unused
                );
                globalChooser?.ResultTask.ContinueWith(r =>
                {
                    if (r.IsCompletedSuccessfully)
                    {
                        Plugin.Framework.Run(() => Mutable.GlobalFontV2 = r.Result);
                    }
                });
                ImGui.SameLine();
                if (ImGui.Button("Reset##global"))
                {
                    Mutable.GlobalFontV2 = new SingleFontSpec
                    {
                        FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkRegular),
                        SizePt = 12.75f,
                    };
                }

                ImGuiUtil.HelpMarker(
                    string.Format(Language.Options_Font_Description, Plugin.PluginName)
                );
                ImGuiUtil.WarningText(Language.Options_Font_Warning);
                ImGui.Spacing();

                var japaneseChooser = ImGuiUtil.FontChooser(
                    Language.Options_JapaneseFont_Name,
                    Mutable.JapaneseFontV2,
                    false,
                    ref unused,
                    id => !id.LocaleNames?.ContainsKey("ja-jp") ?? false,
                    "いろはにほへと   ちりぬるを"
                );
                japaneseChooser?.ResultTask.ContinueWith(r =>
                {
                    if (r.IsCompletedSuccessfully)
                    {
                        Plugin.Framework.Run(() => Mutable.JapaneseFontV2 = r.Result);
                    }
                });
                ImGui.SameLine();
                if (ImGui.Button("Reset##japanese"))
                {
                    Mutable.JapaneseFontV2 = new SingleFontSpec
                    {
                        FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkMedium),
                        SizePt = 12.75f,
                    };
                }

                ImGuiUtil.HelpMarker(
                    string.Format(Language.Options_JapaneseFont_Description, Plugin.PluginName)
                );
                ImGui.Spacing();

                var italicChooser = ImGuiUtil.FontChooser(
                    Language.Options_ItalicFont_Name,
                    Mutable.ItalicFontV2,
                    true,
                    ref Mutable.ItalicEnabled
                );
                italicChooser?.ResultTask.ContinueWith(r =>
                {
                    if (r.IsCompletedSuccessfully)
                    {
                        Plugin.Framework.Run(() => Mutable.ItalicFontV2 = r.Result);
                    }
                });
                ImGui.SameLine();
                if (ImGui.Button("Reset##italic"))
                {
                    Mutable.ItalicEnabled = false;
                    Mutable.ItalicFontV2 = new SingleFontSpec
                    {
                        FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkRegular),
                        SizePt = 12.75f,
                    };
                }

                ImGuiUtil.HelpMarker(
                    string.Format(Language.Options_Italic_Description, Plugin.PluginName)
                );
                ImGui.Spacing();
            }

            // v1.5.3: ExtraGlyphRanges is an atlas-wide property and stays
            // reachable regardless of UseHellionFont / FontsEnabled state so
            // users can verify or override the auto-activation on language change.
            ImGui.Spacing();
            if (ImGui.CollapsingHeader(Language.Options_ExtraGlyphs_Name))
            {
                ImGuiUtil.HelpMarker(
                    string.Format(Language.Options_ExtraGlyphs_Description, Plugin.PluginName)
                );

                var range = (int)Mutable.ExtraGlyphRanges;
                foreach (var extra in Enum.GetValues<ExtraGlyphRanges>())
                {
                    ImGui.CheckboxFlags(extra.Name(), ref range, (int)extra);
                }

                Mutable.ExtraGlyphRanges = (ExtraGlyphRanges)range;
            }

            ImGuiUtil.FontSizeCombo(
                Language.Options_SymbolsFontSize_Name,
                ref Mutable.SymbolsFontSizeV2
            );
            ImGuiUtil.HelpMarker(Language.Options_SymbolsFontSize_Description);

            ImGui.Spacing();
        }
    }

    // ── Colours ────────────────────────────────────────────────────────────

    private void DrawColoursSection(bool sectionJustEntered)
    {
        if (sectionJustEntered)
            ImGui.SetNextItemOpen(false);
        using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_Colours);
        if (!tree.Success)
            return;

        using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
        {
            DrawColourPresetButtons();
            ImGui.TextDisabled(HellionStrings.Settings_Appearance_Colours_PresetsHint);
            ImGui.Spacing();
            ImGui.Separator();
            ImGui.Spacing();

            ImGui.Checkbox(
                Language.Options_ColorSelectedInputChannelButton_Name,
                ref Mutable.ColorSelectedInputChannelButton
            );
            ImGuiUtil.HelpMarker(Language.Options_ColorSelectedInputChannelButton_Description);
            ImGui.Spacing();

            foreach (var (_, types) in ChatTypeExt.SortOrder)
            {
                foreach (var type in types)
                {
                    if (
                        ImGuiUtil.IconButton(
                            FontAwesomeIcon.UndoAlt,
                            $"{type}",
                            Language.Options_ChatColours_Reset
                        )
                    )
                    {
                        Mutable.ChatColours.Remove(type);
                    }

                    ImGui.SameLine();

                    if (
                        ImGuiUtil.IconButton(
                            FontAwesomeIcon.LongArrowAltDown,
                            $"{type}",
                            Language.Options_ChatColours_Import
                        )
                    )
                    {
                        var gameColour = Plugin.Functions.Chat.GetChannelColor(type);
                        Mutable.ChatColours[type] = gameColour ?? type.DefaultColor() ?? 0;
                    }

                    ImGui.SameLine();

                    var vec = Mutable.ChatColours.TryGetValue(type, out var colour)
                        ? ColourUtil.RgbaToVector3(colour)
                        : ColourUtil.RgbaToVector3(type.DefaultColor() ?? 0);
                    if (ImGui.ColorEdit3(type.Name(), ref vec, ImGuiColorEditFlags.NoInputs))
                    {
                        Mutable.ChatColours[type] = ColourUtil.Vector3ToRgba(vec);
                    }
                }
            }

            ImGui.Spacing();
        }
    }

    private void DrawColourPresetButtons()
    {
        var first = true;
        foreach (var (_, preset) in ChatColourPresets.All)
        {
            if (!first)
            {
                ImGui.SameLine();
            }
            first = false;

            if (preset.IsBrandPreset)
            {
                var border = ColourUtil.RgbaToVector3(ColourUtil.ComponentsToRgba(255, 128, 200));
                var btn = ColourUtil.RgbaToVector3(ColourUtil.ComponentsToRgba(74, 42, 106));
                ImGui.PushStyleColor(
                    ImGuiCol.Border,
                    new System.Numerics.Vector4(border.X, border.Y, border.Z, 1f)
                );
                ImGui.PushStyleColor(
                    ImGuiCol.Button,
                    new System.Numerics.Vector4(btn.X, btn.Y, btn.Z, 1f)
                );
                ImGui.PushStyleVar(ImGuiStyleVar.FrameBorderSize, 1.5f);
            }

            if (ImGui.Button(GetPresetLabel(preset)))
            {
                ApplyPreset(preset);
            }

            if (preset.IsBrandPreset)
            {
                ImGui.PopStyleVar();
                ImGui.PopStyleColor(2);
            }
        }
    }

    private static string GetPresetLabel(ChatColourPreset preset)
    {
        var localized = HellionStrings.ResourceManager.GetString(
            preset.LocalizationKey,
            HellionStrings.Culture
        );
        return string.IsNullOrEmpty(localized) ? preset.DisplayName : localized;
    }

    private void ApplyPreset(ChatColourPreset preset)
    {
        foreach (var (channel, colour) in preset.Colours)
        {
            Mutable.ChatColours[channel] = colour;
        }
        Plugin.SaveConfig();
        GlobalParametersCache.Refresh();
        _logger.LogDebug($"Applied chat colour preset: {preset.DisplayName}");
    }

    // ── Window style ───────────────────────────────────────────────────────

    private void DrawWindowStyleSection(bool sectionJustEntered)
    {
        if (sectionJustEntered)
            ImGui.SetNextItemOpen(false);
        using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_WindowStyle);
        if (!tree.Success)
            return;

        using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
        {
            ImGui.Checkbox(Language.Options_ShowTitleBar_Name, ref Mutable.ShowTitleBar);

            ImGui.Checkbox(
                Language.Options_ShowPopOutTitleBar_Name,
                ref Mutable.ShowPopOutTitleBar
            );

            ImGui.Checkbox(Language.Options_ShowHideButton_Name, ref Mutable.ShowHideButton);
            ImGuiUtil.HelpMarker(Language.Options_ShowHideButton_Description);

            ImGui.Checkbox(Language.Options_SidebarTabView_Name, ref Mutable.SidebarTabView);
            ImGuiUtil.HelpMarker(
                string.Format(Language.Options_SidebarTabView_Description, Plugin.PluginName)
            );

            if (Mutable.SidebarTabView)
            {
                var sidebarWidth = Mutable.SidebarWidth;
                if (
                    ImGui.SliderInt(
                        HellionStrings.Settings_ThemeAndLayout_SidebarWidth_Name,
                        ref sidebarWidth,
                        44,
                        160,
                        $"{sidebarWidth} px"
                    )
                )
                {
                    Mutable.SidebarWidth = sidebarWidth;
                }
                ImGuiUtil.HelpMarker(
                    HellionStrings.Settings_ThemeAndLayout_SidebarWidth_Description
                );
            }

            ImGui.Spacing();
            ImGui.Separator();
            ImGui.Spacing();

            // Slider range 50-100% maps to 0.5-1.0 internally. Floor at 50% prevents
            // accidentally hiding the chat background (v1.2.0 bug at WindowAlpha=0).
            var opacityPercent = Mutable.WindowOpacity * 100f;
            if (
                ImGuiUtil.DragFloatVertical(
                    HellionStrings.Settings_ThemeAndLayout_WindowOpacity_Name,
                    ref opacityPercent,
                    .25f,
                    50f,
                    100f,
                    $"{opacityPercent:N0}%%",
                    ImGuiSliderFlags.AlwaysClamp
                )
            )
            {
                Mutable.WindowOpacity = opacityPercent / 100f;
            }
            ImGuiUtil.HelpMarker(HellionStrings.Settings_ThemeAndLayout_WindowOpacity_Description);

            // UI-12: inactive-window opacity, same 50-100% range and clamp.
            var inactiveOpacityPercent = Mutable.WindowOpacityInactive * 100f;
            if (
                ImGuiUtil.DragFloatVertical(
                    HellionStrings.Settings_ThemeAndLayout_WindowOpacityInactive_Name,
                    ref inactiveOpacityPercent,
                    .25f,
                    50f,
                    100f,
                    $"{inactiveOpacityPercent:N0}%%",
                    ImGuiSliderFlags.AlwaysClamp
                )
            )
            {
                Mutable.WindowOpacityInactive = inactiveOpacityPercent / 100f;
            }
            ImGuiUtil.HelpMarker(
                HellionStrings.Settings_ThemeAndLayout_WindowOpacityInactive_Description
            );
        }
    }

    // ── Timestamps ─────────────────────────────────────────────────────────

    private void DrawTimestampSection(bool sectionJustEntered)
    {
        if (sectionJustEntered)
            ImGui.SetNextItemOpen(false);
        using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_Timestamps);
        if (!tree.Success)
            return;

        using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
        {
            ImGui.Checkbox(
                Language.Options_PrettierTimestamps_Name,
                ref Mutable.PrettierTimestamps
            );
            ImGuiUtil.HelpMarker(Language.Options_PrettierTimestamps_Description);

            if (Mutable.PrettierTimestamps)
            {
                ImGui.Checkbox(
                    Language.Options_MoreCompactPretty_Name,
                    ref Mutable.MoreCompactPretty
                );
                ImGuiUtil.HelpMarker(Language.Options_MoreCompactPretty_Description);

                ImGui.Checkbox(
                    HellionStrings.Appearance_UseCompactDensity_Name,
                    ref Mutable.UseCompactDensity
                );
                ImGuiUtil.HelpMarker(HellionStrings.Appearance_UseCompactDensity_Description);

                ImGui.Checkbox(
                    Language.Options_HideSameTimestamps_Name,
                    ref Mutable.HideSameTimestamps
                );
                ImGuiUtil.HelpMarker(Language.Options_HideSameTimestamps_Description);
            }

            ImGui.Checkbox(Language.Options_Use24HourClock_Name, ref Mutable.Use24HourClock);
            ImGuiUtil.HelpMarker(Language.Options_Use24HourClock_Description);
        }
    }

    // ── Animations ─────────────────────────────────────────────────────────

    private void DrawAnimationsSection(bool sectionJustEntered)
    {
        if (sectionJustEntered)
            ImGui.SetNextItemOpen(false);
        using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_Animations);
        if (!tree.Success)
            return;

        using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
        {
            // Master accessibility toggle for the v1.5.4 motion work: the
            // theme crossfade, the sidebar/card hover lerps and the
            // unread-tab pulse all read Config.ReduceMotion and snap
            // instantly when it is on.
            ImGui.Checkbox(
                HellionStrings.Settings_ThemeAndLayout_ReduceMotion_Name,
                ref Mutable.ReduceMotion
            );
            ImGuiUtil.HelpMarker(HellionStrings.Settings_ThemeAndLayout_ReduceMotion_Description);
        }
    }
}
