/*
 * ActionChatLinkHelper: Shift+click on actions in the Actions & Traits menu to insert
 * "You should check out {ActionName}" into the chat input (or clipboard if chat not focused).
 */

using Dalamud.Bindings.ImGui;
using Dalamud.Plugin.Services;
using Dalamud.Game.Gui;
using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Component.GUI;
using HSUI.Config;
using System;
using System.Numerics;
using LuminaAction = Lumina.Excel.Sheets.Action;

namespace HSUI.Helpers
{
    public sealed class ActionChatLinkHelper : IDisposable
    {
        public static ActionChatLinkHelper? Instance { get; private set; }

        private readonly IFramework _framework;

        /// <summary>Store last hovered action when ActionMenu is visible and shift held, so we have it at click time.</summary>
        private uint _pendingActionId;

        private ActionChatLinkHelper(IFramework framework)
        {
            _framework = framework;
            _framework.Update += OnFrameworkUpdate;
            ConfigurationManager.Instance.ResetEvent += OnConfigReset;
            OnConfigReset(ConfigurationManager.Instance);
        }

        private WorldObjectTooltipConfig _config = null!;

        public static void Initialize()
        {
            if (Instance != null) return;
            Instance = new ActionChatLinkHelper(Plugin.Framework);
        }

        public void Dispose()
        {
            _framework.Update -= OnFrameworkUpdate;
            try { ConfigurationManager.Instance.ResetEvent -= OnConfigReset; } catch { }
            Instance = null;
        }

        private void OnConfigReset(ConfigurationManager _) =>
            _config = ConfigurationManager.Instance.GetConfigObject<WorldObjectTooltipConfig>();

        private void OnFrameworkUpdate(IFramework framework)
        {
            try
            {
                if (!_config.ActionChatLinkEnabled) return;

                bool shiftHeld = ImGui.IsKeyDown(ImGuiKey.LeftShift) || ImGui.IsKeyDown(ImGuiKey.RightShift);
                bool leftClicked = ImGui.IsMouseClicked(ImGuiMouseButton.Left);

                // ActionMenu must be visible
                var addon = Plugin.GameGui.GetAddonByName("ActionMenu", 1);
                if (addon == null || addon.Address == IntPtr.Zero)
                {
                    _pendingActionId = 0;
                    return;
                }

                // Capture action: 1) Addon list (ActionList or TraitList); 2) HoveredAction only when mouse is over ActionMenu (excludes hotbars)
                if (shiftHeld)
                {
                    if (TryGetHoveredActionFromAddon(addon.Address, out uint fromAddon))
                        _pendingActionId = fromAddon;
                    else if (IsMouseOverActionMenu(addon.Address) && !ActionBarsHitTestHelper.IsMouseOverAnyHSUIHotbar())
                    {
                        var ha = Plugin.GameGui.HoveredAction;
                        if (ha != null && ha.ActionKind != HoverActionKind.None && ha.ActionID != 0)
                            _pendingActionId = ha.ActionID;
                        else
                            _pendingActionId = 0;
                        }
                    else
                        _pendingActionId = 0;
                }
                else
                    _pendingActionId = 0;

                // On shift+click, use captured action or HoveredAction as fallback
                if (!shiftHeld || !leftClicked) return;

                if (_pendingActionId == 0) return;
                uint idToUse = _pendingActionId;

                string? actionName = GetActionName(idToUse);
                if (string.IsNullOrWhiteSpace(actionName)) return;

                string text = $"You should check out {actionName}";
                InsertOrCopyToChat(text);
                _pendingActionId = 0;
            }
            catch (Exception ex)
            {
                _pendingActionId = 0;
                Plugin.Logger.Warning($"[ActionChatLink] OnFrameworkUpdate: {ex.Message}");
            }
        }

        /// <summary>Read action ID from AddonActionMenu. Tries ActionList and TraitList, HoveredItemIndex and HeldItemIndex.</summary>
        private static unsafe bool TryGetHoveredActionFromAddon(IntPtr addonAddress, out uint actionId)
        {
            actionId = 0;
            if (addonAddress == IntPtr.Zero) return false;

            try
            {
                var addon = (AddonActionMenu*)addonAddress;
                byte* basePtr = (byte*)addonAddress;

                foreach (var list in new[] { addon->ActionList, addon->TraitList })
                {
                    if (list == null) continue;
                    foreach (int idx in new[] { list->HoveredItemIndex, list->HeldItemIndex })
                    {
                        if (idx < 0 || idx >= 80) continue;
                        actionId = *(uint*)(basePtr + 0x338 + idx * 0x38 + 0x14);
                        if (actionId != 0) return true;
                    }
                }
            }
            catch (Exception ex)
            {
                Plugin.Logger.Verbose($"[ActionChatLink] TryGetHoveredActionFromAddon: {ex.Message}");
            }
            return false;
        }

        /// <summary>True if mouse is within ActionMenu addon bounds (so we can safely use HoveredAction — excludes hotbars).</summary>
        private static unsafe bool IsMouseOverActionMenu(IntPtr addonAddress)
        {
            if (addonAddress == IntPtr.Zero) return false;
            try
            {
                var addon = (AtkUnitBase*)addonAddress;
                var root = addon->RootNode;
                if (root == null || !addon->IsVisible) return false;

                var mp = ImGui.GetMousePos();
                float x = root->ScreenX, y = root->ScreenY, w = root->Width, h = root->Height;
                return mp.X >= x && mp.X < x + w && mp.Y >= y && mp.Y < y + h;
            }
            catch (Exception ex)
            {
                Plugin.Logger.Verbose($"[ActionChatLink] IsMouseOverActionMenu: {ex.Message}");
                return false;
            }
        }

        private static string? GetActionName(uint actionId)
        {
            try
            {
                var sheet = Plugin.DataManager.GetExcelSheet<LuminaAction>();
                var row = sheet?.GetRow(actionId);
                return row.HasValue ? row.Value.Name.ToString() : null;
            }
            catch { return null; }
        }

        private static void InsertOrCopyToChat(string text)
        {
            if (TryInsertIntoActiveTextInput(text))
                return;

            // Fallback: copy to clipboard and notify (leave text on clipboard for user to paste)
            try
            {
                ImGui.SetClipboardText(text);
                Plugin.Chat.Print("[HSUI] Copied to clipboard — open chat and press Ctrl+V to paste.");
            }
            catch (Exception ex)
            {
                Plugin.Logger.Warning($"[ActionChatLink] Failed to copy to clipboard: {ex.Message}");
            }
        }

        private static unsafe bool TryInsertIntoActiveTextInput(string text)
        {
            try
            {
                var raptureAtk = RaptureAtkModule.Instance();
                if (raptureAtk == null || !raptureAtk->IsTextInputActive())
                    return false;

                var stage = AtkStage.Instance();
                if (stage == null) return false;

                var focus = stage->GetFocus();
                if (focus == null) return false;

                // Traverse up to find the text input component
                var node = focus;
                while (node != null)
                {
                    if (node->Type == NodeType.Component)
                    {
                        var componentNode = (AtkComponentNode*)node;
                        var component = componentNode->Component;
                        if (component != null)
                        {
                            if (component->GetComponentType() == ComponentType.TextInput)
                            {
                                var textInput = (AtkComponentTextInput*)component;
                                textInput->InsertText(text, false);
                                return true;
                            }
                        }
                    }
                    node = node->ParentNode;
                }

                return false;
            }
            catch (Exception ex)
            {
                Plugin.Logger.Warning($"[ActionChatLink] InsertText failed: {ex.Message}");
                return false;
            }
        }
    }
}
