using System;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Dalamud.Game.ClientState.Objects.Types;
using HSUI.Config;
using HSUI.Enums;
using HSUI.Helpers;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
using FFXIVClientStructs.FFXIV.Component.GUI;
using static FFXIVClientStructs.FFXIV.Client.Game.ActionManager;
using LuminaAction = Lumina.Excel.Sheets.Action;

namespace HSUI.Interface.GeneralElements
{
    /// <summary>Draws one controller cross hotbar (8 slots in cross layout). Visible only when controller hotbars are enabled and trigger state matches.</summary>
    public class CrossBarHud : DraggableHudElement, IHudElementWithActor, IHudElementWithVisibilityConfig
    {
        private ControllerBarConfig Config => (ControllerBarConfig)_config;

        private bool _wasDragging;
        private uint _dragId;
        private RaptureHotbarModule.HotbarSlotType _dragSlotType;
        private int _pendingSlotIconIndex = -1;
        private uint _pendingSlotIconId;
        private int _pendingSlotIconFramesLeft;
        private int _lastFrameDroppedOnSlot = -1;
        private bool _suppressSlotClicksAfterDragRelease;

        public IGameObject? Actor { get; set; }

        public VisibilityConfig VisibilityConfig => Config.VisibilityConfig;

        public CrossBarHud(ControllerBarConfig config, string displayName) : base(config, displayName) { }

        protected override (List<Vector2>, List<Vector2>) ChildrenPositionsAndSizes()
        {
            var size = BarSize();
            return (new List<Vector2> { Config.Position }, new List<Vector2> { size });
        }

        private Vector2 BarSize()
        {
            // Two crosses side by side: each cross is 3 cells (slot+pad); gap between crosses
            float cellW = Config.SlotSize.X + Config.SlotPadding;
            float cellH = Config.SlotSize.Y + Config.SlotPadding;
            float gap = Config.SlotPadding * 2;
            float w = 3 * cellW + gap + 3 * cellW;
            float h = 3 * cellH;
            return new Vector2(w, h);
        }

        /// <summary>Cross layout like the game: left cross (slots 0-3) = top/left/right/bottom around center; right cross (slots 4-7) same.</summary>
        private Vector2 GetSlotPositionCross(int slotIndex, Vector2 slotSize, int pad)
        {
            float cellW = slotSize.X + pad;
            float cellH = slotSize.Y + pad;
            float leftCrossWidth = 3 * cellW;
            float gap = pad * 2f;

            if (slotIndex < 4)
            {
                // Left cross: 3x3 grid, slots at (1,0), (0,1), (2,1), (1,2)
                int col = slotIndex switch { 0 => 1, 1 => 0, 2 => 2, _ => 1 };
                int row = slotIndex switch { 0 => 0, 1 => 1, 2 => 1, _ => 2 };
                return new Vector2(col * cellW, row * cellH);
            }
            else
            {
                // Right cross: same 3x3 pattern, offset by left cross + gap
                int i = slotIndex - 4;
                int col = i switch { 0 => 1, 1 => 0, 2 => 2, _ => 1 };
                int row = i switch { 0 => 0, 1 => 1, 2 => 1, _ => 2 };
                return new Vector2(leftCrossWidth + gap + col * cellW, row * cellH);
            }
        }

        private const float DropMargin = 24f;

        /// <summary>Hit test: returns slot index 0-7 if pos is over a cross slot, else -1.</summary>
        private int GetSlotIndexAtPositionCross(Vector2 topLeft, Vector2 slotSize, int pad, Vector2 pos)
        {
            Vector2 barSize = new Vector2(
                3 * (slotSize.X + pad) + pad * 2f + 3 * (slotSize.X + pad),
                3 * (slotSize.Y + pad));
            if (pos.X < topLeft.X || pos.X >= topLeft.X + barSize.X || pos.Y < topLeft.Y || pos.Y >= topLeft.Y + barSize.Y)
                return -1;
            int best = -1;
            float bestDistSq = float.MaxValue;
            for (int i = 0; i < ControllerBarConfig.CrossSlotCount; i++)
            {
                var slotOffset = GetSlotPositionCross(i, slotSize, pad);
                float cx = topLeft.X + slotOffset.X + slotSize.X * 0.5f;
                float cy = topLeft.Y + slotOffset.Y + slotSize.Y * 0.5f;
                float dx = pos.X - cx, dy = pos.Y - cy;
                float slotHalfW = slotSize.X * 0.5f + DropMargin;
                float slotHalfH = slotSize.Y * 0.5f + DropMargin;
                if (Math.Abs(dx) <= slotHalfW && Math.Abs(dy) <= slotHalfH)
                {
                    float distSq = dx * dx + dy * dy;
                    if (distSq < bestDistSq) { bestDistSq = distSq; best = i; }
                }
            }
            return best;
        }

        private unsafe void HandleDragDropCross(Vector2 topLeft, Vector2 barSize, Vector2 slotSize, int pad)
        {
            if (_pendingSlotIconIndex >= 0 && _pendingSlotIconFramesLeft > 0)
            {
                _pendingSlotIconFramesLeft--;
                if (_pendingSlotIconFramesLeft <= 0)
                    _pendingSlotIconIndex = -1;
            }
            else if (_pendingSlotIconIndex >= 0)
                _pendingSlotIconIndex = -1;

            var stage = AtkStage.Instance();
            if (stage == null) return;
            var dm = (AtkDragDropManager*)Unsafe.AsPointer(ref stage->DragDropManager);
            bool dragging = dm->IsDragging;

            if (dragging)
            {
                if (ActionBarsHud.GetHotbarsConfig()?.GeneralOptions?.EnableDragDropFromGame != false &&
                    ActionBarsHud.TryGetDragPayload(out var slotType, out var id))
                {
                    _dragSlotType = slotType;
                    _dragId = id;
                    _wasDragging = true;

                    var mp = ImGui.GetMousePos();
                    int idx = GetSlotIndexAtPositionCross(topLeft, slotSize, pad, mp);
                    if (idx >= 0)
                        InputsHelper.SuppressExecuteSlotByIdAfterDrop((uint)(ActionBarsManager.ScratchBarIndex - 1), (uint)ActionBarsManager.ScratchSlotIndex, 500);

                    if (ImGui.IsMouseReleased(ImGuiMouseButton.Left))
                    {
                        int releaseIdx = GetSlotIndexAtPositionCross(topLeft, slotSize, pad, mp);
                        if (releaseIdx >= 0 && slotType != RaptureHotbarModule.HotbarSlotType.Empty &&
                            (id != 0 || slotType == RaptureHotbarModule.HotbarSlotType.Macro || slotType == RaptureHotbarModule.HotbarSlotType.GearSet))
                        {
                            if (!ImGui.GetIO().KeyShift)
                            {
                                try
                                {
                                    ActionBarsManager.Instance?.PlacePayloadOnControllerSlot(Config.HotbarIndex, releaseIdx, slotType, id);
                                    uint iconId = ActionBarsHud.GetIconIdForPayload(slotType, id);
                                    if (iconId != 0)
                                    {
                                        _pendingSlotIconIndex = releaseIdx;
                                        _pendingSlotIconId = iconId;
                                        _pendingSlotIconFramesLeft = 300;
                                    }
                                    _lastFrameDroppedOnSlot = releaseIdx;
                                    InputsHelper.SuppressExecuteSlotByIdAfterDrop((uint)(ActionBarsManager.ScratchBarIndex - 1), (uint)ActionBarsManager.ScratchSlotIndex, 300);
                                    if (slotType == RaptureHotbarModule.HotbarSlotType.Action ||
                                        slotType == RaptureHotbarModule.HotbarSlotType.GeneralAction ||
                                        slotType == RaptureHotbarModule.HotbarSlotType.CraftAction ||
                                        slotType == RaptureHotbarModule.HotbarSlotType.PetAction)
                                        InputsHelper.SuppressUseActionAfterDrop(id, 300);
                                }
                                catch (Exception ex)
                                {
                                    Plugin.Logger.Warning($"[HSUI CrossBar DragDrop] Drop failed: {ex.Message}");
                                }
                            }
                            try { dm->CancelDragDrop(true, true); }
                            catch { }
                        }
                        else
                        {
                            try { dm->CancelDragDrop(true, true); }
                            catch { }
                        }
                        _wasDragging = false;
                    }
                }
                else
                {
                    _dragSlotType = RaptureHotbarModule.HotbarSlotType.Empty;
                    _dragId = 0;
                    _wasDragging = true;
                    if (ImGui.IsMouseReleased(ImGuiMouseButton.Left))
                    {
                        try { dm->CancelDragDrop(true, true); }
                        catch { }
                        _wasDragging = false;
                    }
                }
                return;
            }

            if (_wasDragging)
            {
                _suppressSlotClicksAfterDragRelease = true;
                var mp = ImGui.GetMousePos();
                int idx = GetSlotIndexAtPositionCross(topLeft, slotSize, pad, mp);
                if (idx >= 0)
                {
                    var slotType = _dragSlotType;
                    var id = _dragId;
                    bool hasValidPayload = slotType != RaptureHotbarModule.HotbarSlotType.Empty &&
                        (id != 0 || slotType == RaptureHotbarModule.HotbarSlotType.Macro || slotType == RaptureHotbarModule.HotbarSlotType.GearSet);
                    if (hasValidPayload)
                    {
                        if (ImGui.GetIO().KeyShift)
                        {
                            try { dm->CancelDragDrop(true, true); }
                            catch { }
                            ActionBarsManager.Instance?.ClearControllerSlot(Config.HotbarIndex, idx);
                            _lastFrameDroppedOnSlot = idx;
                            _wasDragging = false;
                            return;
                        }
                        try
                        {
                            ActionBarsManager.Instance?.PlacePayloadOnControllerSlot(Config.HotbarIndex, idx, slotType, id);
                            try { dm->CancelDragDrop(true, true); }
                            catch { }
                            uint iconId = ActionBarsHud.GetIconIdForPayload(slotType, id);
                            if (iconId != 0)
                            {
                                _pendingSlotIconIndex = idx;
                                _pendingSlotIconId = iconId;
                                _pendingSlotIconFramesLeft = 300;
                            }
                            _lastFrameDroppedOnSlot = idx;
                            InputsHelper.SuppressExecuteSlotByIdAfterDrop((uint)(ActionBarsManager.ScratchBarIndex - 1), (uint)ActionBarsManager.ScratchSlotIndex, 300);
                            if (slotType == RaptureHotbarModule.HotbarSlotType.Action ||
                                slotType == RaptureHotbarModule.HotbarSlotType.GeneralAction ||
                                slotType == RaptureHotbarModule.HotbarSlotType.CraftAction ||
                                slotType == RaptureHotbarModule.HotbarSlotType.PetAction)
                                InputsHelper.SuppressUseActionAfterDrop(id, 300);
                        }
                        catch (Exception ex)
                        {
                            Plugin.Logger.Warning($"[HSUI CrossBar DragDrop] Drop failed: {ex.Message}");
                        }
                    }
                }
                _wasDragging = false;
            }
        }

        public override void DrawChildren(Vector2 origin)
        {
            if (!ControllerHotbarsConfig.GetEffectiveControllerHotbarsEnabled() || Actor == null)
                return;
            if (ControllerHotbarHelper.GetCurrentTrigger() != Config.Trigger)
                return;
            if (ActionBarsManager.Instance == null)
                return;

            _lastFrameDroppedOnSlot = -1;
            _suppressSlotClicksAfterDragRelease = false;

            var barSize = BarSize();
            var topLeft = Utils.GetAnchoredPosition(origin + Config.Position, barSize, Config.Anchor);
            var slotSize = Config.SlotSize;
            int pad = Config.SlotPadding;
            bool showCd = Config.ShowCooldownOverlay;
            bool showCdNumbers = Config.ShowCooldownNumbers;
            bool showBorder = Config.ShowBorder;

            AddDrawAction(Config.StrataLevel, () =>
            {
                HandleDragDropCross(topLeft, barSize, slotSize, pad);
                var slots = ActionBarsManager.Instance.GetControllerSlotData(Config.HotbarIndex, ControllerBarConfig.CrossSlotCount);

                void DrawBarContent(ImDrawListPtr drawList)
                {
                    using (FontsManager.Instance.PushFont(FontsConfig.DefaultSmallFontKey))
                    {
                        for (int i = 0; i < slots.Count; i++)
                        {
                            var slot = slots[i];
                            uint displayIconId = slot.IconId;
                            bool isEmpty = slot.IsEmpty;
                            bool usePending = _pendingSlotIconIndex == i && _pendingSlotIconId != 0;
                            if (usePending)
                            {
                                displayIconId = _pendingSlotIconId;
                                isEmpty = false;
                            }

                            var slotOffset = GetSlotPositionCross(i, slotSize, pad);
                            var pos = topLeft + slotOffset;
                            var size = new Vector2(slotSize.X, slotSize.Y);
                            bool hovered = !isEmpty && ImGui.IsMouseHoveringRect(pos, pos + size);

                            if (isEmpty)
                            {
                                drawList.AddRectFilled(pos, pos + size, 0x22000000);
                                if (showBorder)
                                    drawList.AddRect(pos, pos + size, 0xFF444444);
                            }
                            else
                            {
                                uint color = hovered ? 0xFFFFFFFF : (slot.IsUsable ? 0xFFFFFFFF : 0xAA888888);
                                bool isComboNext = Config.ShowComboHighlight && IsComboNextAction(slot);
                                DrawHelper.DrawIcon(displayIconId, pos, size, showBorder && !hovered && !isComboNext, color, drawList);

                                if (hovered && showBorder)
                                    drawList.AddRect(pos, pos + size, 0xFF88CCFF, 0, ImDrawFlags.None, 2);
                                else if (isComboNext)
                                {
                                    var ch = Config.ComboHighlightConfig;
                                    DrawHelper.DrawComboHighlightRect(drawList, pos, pos + size,
                                        ch.Color.Base, ch.Thickness, ch.ShowGlow, ch.LineStyle);
                                }

                                if (showCd && slot.CooldownPercent > 0 && _pendingSlotIconIndex != i)
                                {
                                    float total = 100f;
                                    float elapsed = total - slot.CooldownPercent;
                                    DrawHelper.DrawIconCooldown(pos, size, elapsed, total, drawList);
                                    if (showCdNumbers && slot.CooldownSecondsLeft > 0)
                                    {
                                        string cdText = slot.CooldownSecondsLeft.ToString();
                                        Vector2 textSize = ImGui.CalcTextSize(cdText);
                                        Vector2 textPos = pos + (size - textSize) * 0.5f;
                                        DrawHelper.DrawOutlinedText(cdText, textPos, 0xFFFFFFFF, 0xFF000000, drawList, 1);
                                    }
                                }

                                if (Config.ShowKeypressFlash)
                                {
                                    float flashAlpha = InputsHelper.GetKeypressFlashAlpha(Config.HotbarIndex, i, 0.2);
                                    if (flashAlpha > 0.001f)
                                    {
                                        byte a = (byte)(flashAlpha * 0.5f * 255);
                                        drawList.AddRectFilled(pos, pos + size, (uint)(a << 24 | 0xFFFFFF));
                                    }
                                }
                            }

                            if (Config.ShowSlotNumbers && !slot.IsEmpty)
                            {
                                string label = !string.IsNullOrWhiteSpace(slot.KeybindHint)
                                    ? slot.KeybindHint
                                    : ActionBarsManager.GetDefaultKeybindLabel(Config.HotbarIndex, i);
                                Vector2 labelSize = ImGui.CalcTextSize(label);
                                Vector2 labelPos = new Vector2(pos.X + size.X - labelSize.X - 2, pos.Y + size.Y - labelSize.Y - 2);
                                DrawHelper.DrawOutlinedText(label, labelPos, 0xFFFFFFFF, 0xFF000000, drawList, 1);
                            }

                            if (Config.ShowChargeCount && !slot.IsEmpty && slot.MaxCharges > 1)
                            {
                                string chargeText = slot.CurrentCharges.ToString();
                                Vector2 chargeSize = ImGui.CalcTextSize(chargeText);
                                Vector2 chargePos = new Vector2(pos.X + 2, pos.Y + 2);
                                DrawHelper.DrawOutlinedText(chargeText, chargePos, 0xFFFFFFFF, 0xFF000000, drawList, 1);
                            }
                        }
                    }
                }

                DrawHelper.DrawInWindow(ID + "_crossbar", topLeft, barSize, false, DrawBarContent);
            });

            // Overlay for click, drag-drop and tooltip
            AddDrawAction(StrataLevel.HIGHEST, () =>
            {
                var slots = ActionBarsManager.Instance.GetControllerSlotData(Config.HotbarIndex, ControllerBarConfig.CrossSlotCount);
                var flags = ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoBackground
                    | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoSavedSettings
                    | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoBringToFrontOnFocus
                    | ImGuiWindowFlags.NoNav;

                string overlayId = $"HSUI_CrossBar{Config.HotbarIndex}_input";
                ImGui.SetCursorScreenPos(topLeft);
                if (!ImGui.BeginChild(overlayId, barSize, false, flags))
                {
                    ImGui.EndChild();
                    return;
                }

                int frame = ImGui.GetFrameCount();
                if (frame != ActionBarsHud._lastOverlayFrame)
                {
                    ActionBarsHud._lastOverlayFrame = frame;
                    ActionBarsHud._anyHotbarAcceptedDrop = false;
                    if (ActionBarsHud._pendingReleaseOutsideSlotId >= 0 && ActionBarsHud.GetHotbarsConfig()?.GeneralOptions?.EnableReleaseOutsideToClear != false)
                    {
                        var (bar, slot) = ActionBarsHud.FromSlotId(ActionBarsHud._pendingReleaseOutsideSlotId);
                        if (bar >= 1 && bar <= 8 && slot >= 0 && slot < ControllerBarConfig.CrossSlotCount)
                            ActionBarsManager.Instance?.ClearControllerSlot(bar, slot);
                        ActionBarsHud._pendingReleaseOutsideSlotId = -1;
                    }
                }

                bool acceptedDrop = false;
                for (int i = 0; i < ControllerBarConfig.CrossSlotCount; i++)
                {
                    var slotOffset = GetSlotPositionCross(i, slotSize, pad);
                    var slotPos = topLeft + slotOffset;
                    ImGui.SetCursorScreenPos(slotPos);
                    bool clicked = ImGui.InvisibleButton($"##{overlayId}_slot_{i}", slotSize);
                    bool isHovered = ImGui.IsItemHovered();
                    bool isActive = ImGui.IsItemActive();

                    bool shiftHeld = ImGui.GetIO().KeyShift;
                    int targetSlotId = ActionBarsHud.ToSlotId(Config.HotbarIndex, i);

                    if (ActionBarsHud.GetHotbarsConfig()?.GeneralOptions?.EnableShiftDragToRearrange != false && ImGui.BeginDragDropTarget())
                    {
                        var payload = ImGui.AcceptDragDropPayload(ActionBarsHud.SlotPayloadType);
                        if (payload != ImGuiPayloadPtr.Null && payload.IsDataType(ActionBarsHud.SlotPayloadType) && ActionBarsHud.TryGetSlotPayload(payload, out var src))
                        {
                            acceptedDrop = true;
                            ActionBarsHud._anyHotbarAcceptedDrop = true;
                            ActionBarsHud._imGuiDragSourceSlotId = -1;
                            ActionBarsHud._pendingReleaseOutsideSlotId = -1;
                            ActionBarsHud._lastLoggedPickupSlotId = -1;
                            if (src.SlotId != targetSlotId)
                            {
                                var (srcBar, srcSlot) = ActionBarsHud.FromSlotId(src.SlotId);
                                if (srcBar >= 1 && srcSlot >= 0 && srcBar <= 8 && srcSlot < ControllerBarConfig.CrossSlotCount)
                                    ActionBarsManager.Instance?.SwapControllerSlots(srcBar, srcSlot, Config.HotbarIndex, i);
                            }
                        }
                        ImGui.EndDragDropTarget();
                    }

                    if (ActionBarsHud.GetHotbarsConfig()?.GeneralOptions?.EnableShiftDragToRearrange != false && !ActionBarsHud.IsGameDragging() && shiftHeld && i < slots.Count && !slots[i].IsEmpty && isActive && ImGui.IsMouseDragging(ImGuiMouseButton.Left))
                    {
                        if (ImGui.BeginDragDropSource(ImGuiDragDropFlags.None))
                        {
                            ActionBarsHud._imGuiDragSourceSlotId = targetSlotId;
                            ActionBarsHud.SetSlotPayload(new ActionBarsHud.SlotDragPayload { SlotId = targetSlotId });
                            var icon = TexturesHelper.GetTextureFromIconId(slots[i].IconId);
                            if (icon != null)
                                ImGui.Image(icon.Handle, new Vector2(32, 32));
                            ImGui.EndDragDropSource();
                        }
                    }

                    if (clicked && !acceptedDrop && !ImGui.IsMouseDragging(ImGuiMouseButton.Left) && i < slots.Count && !slots[i].IsEmpty)
                    {
                        if (_suppressSlotClicksAfterDragRelease || i == _lastFrameDroppedOnSlot)
                        {
                            _suppressSlotClicksAfterDragRelease = false;
                            _lastFrameDroppedOnSlot = -1;
                        }
                        else if (shiftHeld)
                        {
                            ActionBarsManager.Instance?.ClearControllerSlot(Config.HotbarIndex, i);
                        }
                        else
                        {
                            ActionBarsManager.Instance?.ExecuteControllerSlot(Config.HotbarIndex, i);
                        }
                    }

                    if (Config.ShowTooltips && isHovered && i < slots.Count)
                    {
                        var slot = slots[i];
                        if (!slot.IsEmpty)
                        {
                            var (title, text) = ActionBarsHud.GetSlotTooltipPublic(slot);
                            if (!string.IsNullOrEmpty(title) || !string.IsNullOrEmpty(text))
                            {
                                string body = string.IsNullOrEmpty(text) ? title : text;
                                var tooltipConfig = ConfigurationManager.Instance?.GetConfigObject<TooltipsConfig>();
                                var formatted = tooltipConfig != null ? TooltipsHelper.BuildFormattedActionTooltipBody(body, tooltipConfig) : null;
                                TooltipsHelper.Instance.ShowTooltipOnCursor(body, title, slot.ActionId, "", slot.IconId > 0 ? slot.IconId : null, TooltipIdKind.Action, formatted);
                            }
                        }
                    }
                }

                if (ActionBarsHud.GetHotbarsConfig()?.GeneralOptions?.EnableReleaseOutsideToClear != false && ImGui.GetIO().KeyShift && ActionBarsHud._imGuiDragSourceSlotId >= 0 &&
                    ImGui.IsMouseReleased(ImGuiMouseButton.Left) && !ActionBarsHud._anyHotbarAcceptedDrop)
                {
                    ActionBarsHud._pendingReleaseOutsideSlotId = ActionBarsHud._imGuiDragSourceSlotId;
                    ActionBarsHud._imGuiDragSourceSlotId = -1;
                    ActionBarsHud._lastLoggedPickupSlotId = -1;
                }

                ImGui.EndChild();
            });
        }

        private static bool IsComboNextAction(ActionBarsManager.SlotInfo slot)
        {
            if (slot.IsEmpty || slot.ActionId == 0) return false;
            try
            {
                var slotType = slot.SlotType;
                var actionType = slotType switch
                {
                    RaptureHotbarModule.HotbarSlotType.Action => ActionType.Action,
                    RaptureHotbarModule.HotbarSlotType.GeneralAction => ActionType.GeneralAction,
                    RaptureHotbarModule.HotbarSlotType.CraftAction => ActionType.CraftAction,
                    RaptureHotbarModule.HotbarSlotType.PetAction => ActionType.PetAction,
                    _ => (ActionType?)null
                };
                if (!actionType.HasValue) return false;
                return SpellHelper.Instance.IsActionHighlighted(slot.ActionId, actionType.Value);
            }
            catch { return false; }
        }
    }
}
