using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Dalamud.Game.ClientState.GamePad;
using Dalamud.Game.ClientState.Keys;
using Dalamud.Interface.Windowing;
using HSUI.Config;
using HSUI.Interface.GeneralElements;
using Dalamud.Bindings.ImGui;

namespace HSUI.Config.Windows
{
    public class ControllerBarKeybindsWindow : Window
    {
        private ControllerBarKeybindsConfig? _config;
        private int _selectedBar = 1;
        private int _listeningBar = -1;
        private int _listeningSlot = -1;
        private HashSet<VirtualKey>? _listeningLastKeys;
        private HashSet<GamepadButtons>? _listeningLastPads;

        private const int SlotsPerBar = 8;
        private static readonly GamepadButtons[] ModifierButtons = { GamepadButtons.L1, GamepadButtons.L2, GamepadButtons.R1, GamepadButtons.R2 };
        private static readonly VirtualKey[] ModifierKeys = { VirtualKey.LSHIFT, VirtualKey.RSHIFT, VirtualKey.LCONTROL, VirtualKey.RCONTROL, VirtualKey.LMENU, VirtualKey.RMENU };
        private const float CellSize = 44;
        private const float Pad = 4;
        private const float Gap = 8;

        public ControllerBarKeybindsWindow() : base("Controller bar keybinds")
        {
            Size = new Vector2(520, 380);
            SizeCondition = ImGuiCond.FirstUseEver;
            Flags = ImGuiWindowFlags.NoScrollbar;
        }

        public override void Draw()
        {
            _config = ConfigurationManager.Instance?.GetConfigObject<ControllerBarKeybindsConfig>();
            if (_config == null) return;

            ImGui.Text("Assign keyboard or gamepad buttons to controller cross bar slots.");
            ImGui.Spacing();

            // Bar selector
            ImGui.Text("Cross bar:");
            ImGui.SameLine(100);
            for (int b = 1; b <= 8; b++)
            {
                if (b > 1) ImGui.SameLine();
                if (ImGui.Button($"{(b == _selectedBar ? "[" : " ")}{b}{(b == _selectedBar ? "]" : " ")}", new Vector2(32, 0)))
                    _selectedBar = b;
            }
            ImGui.Spacing();
            ImGui.Separator();
            ImGui.Spacing();

            if (_listeningBar >= 0 && _listeningSlot >= 0)
            {
                ImGui.TextColored(new Vector4(1, 0.8f, 0, 1), "Hold trigger (L2/R2/L1/R1) or modifier (Shift/Ctrl/Alt), then press a key or button. Or press a key/button alone.");
                ImGui.SameLine();
                if (ImGui.Button("Cancel"))
                {
                    _listeningBar = -1;
                    _listeningSlot = -1;
                    _listeningLastKeys = null;
                    _listeningLastPads = null;
                }
                PollAndAssign();
            }
            else
            {
                ImGui.Text($"Bar {_selectedBar} — click a slot, then hold modifiers (e.g. L2) and press a button. Right-click to clear.");
            }

            ImGui.Spacing();

            // Two crosses side by side (same layout as CrossBarHud)
            float leftCrossW = 3 * (CellSize + Pad);
            float startX = ImGui.GetCursorScreenPos().X;
            float startY = ImGui.GetCursorScreenPos().Y;

            for (int slot = 0; slot < SlotsPerBar; slot++)
            {
                (int col, int row) = GetCrossSlotLayout(slot);
                float x = startX + (slot < 4 ? col * (CellSize + Pad) : leftCrossW + Gap + col * (CellSize + Pad));
                float y = startY + row * (CellSize + Pad);

                ImGui.SetCursorScreenPos(new Vector2(x, y));
                string current = _config.GetKeybind(_selectedBar, slot);
                string label = string.IsNullOrEmpty(current) ? "None" : GetKeybindDisplayName(current);
                bool listening = _listeningBar == _selectedBar && _listeningSlot == slot;

                if (listening)
                    ImGui.PushStyleColor(ImGuiCol.Button, new Vector4(0.4f, 0.6f, 0.2f, 0.8f));
                if (ImGui.Button($"##bar{_selectedBar}_slot{slot}", new Vector2(CellSize, CellSize)))
                {
                    _listeningBar = _selectedBar;
                    _listeningSlot = slot;
                }
                if (listening) ImGui.PopStyleColor();

                if (ImGui.IsItemHovered())
                {
                    if (ImGui.IsMouseClicked(ImGuiMouseButton.Right))
                    {
                        _config.SetKeybind(_selectedBar, slot, "");
                        ConfigurationManager.Instance?.SaveConfigurations();
                    }
                }

                string shortLabel = label.Length > 6 ? label.Substring(0, 6) + "…" : label;
                Vector2 textSize = ImGui.CalcTextSize(shortLabel);
                ImGui.SetCursorScreenPos(new Vector2(x + (CellSize - textSize.X) * 0.5f, y + (CellSize - textSize.Y) * 0.5f));
                ImGui.Text(shortLabel);
            }

            ImGui.SetCursorScreenPos(new Vector2(startX, startY + 3 * (CellSize + Pad) + 20));
            ImGui.Spacing();
            if (ImGui.Button("Close"))
                IsOpen = false;
        }

        private static (int col, int row) GetCrossSlotLayout(int slotIndex)
        {
            if (slotIndex < 4)
                return slotIndex switch { 0 => (1, 0), 1 => (0, 1), 2 => (2, 1), _ => (1, 2) };
            int i = slotIndex - 4;
            return i switch { 0 => (1, 0), 1 => (0, 1), 2 => (2, 1), _ => (1, 2) };
        }

        private void PollAndAssign()
        {
            var currentKeys = new HashSet<VirtualKey>();
            var currentPads = new HashSet<GamepadButtons>();

            if (Plugin.KeyState != null)
            {
                foreach (var vk in Plugin.KeyState.GetValidVirtualKeys())
                {
                    if (vk == VirtualKey.ESCAPE) continue;
                    if (Plugin.KeyState[vk])
                        currentKeys.Add(vk);
                }
            }
            if (Plugin.GamepadState != null)
            {
                foreach (var btn in GetGamepadButtonsToPoll())
                {
                    if (Plugin.GamepadState.Raw(btn) > 0.5f)
                        currentPads.Add(btn);
                }
            }

            // First frame of listening: record state as "last" and wait for next frame
            if (_listeningLastKeys == null)
            {
                _listeningLastKeys = new HashSet<VirtualKey>(currentKeys);
                _listeningLastPads = new HashSet<GamepadButtons>(currentPads);
                return;
            }

            var modifierKeySet = new HashSet<VirtualKey>(ModifierKeys);
            var modifierPadSet = new HashSet<GamepadButtons>(ModifierButtons);

            var heldModifierKeys = new HashSet<VirtualKey>(currentKeys.Where(k => modifierKeySet.Contains(k) && _listeningLastKeys.Contains(k)));
            var heldModifierPads = new HashSet<GamepadButtons>(currentPads.Where(b => modifierPadSet.Contains(b) && _listeningLastPads!.Contains(b)));

            string? triggerPart = null;
            foreach (var vk in Plugin.KeyState?.GetValidVirtualKeys() ?? Array.Empty<VirtualKey>())
            {
                if (vk == VirtualKey.ESCAPE) continue;
                if (currentKeys.Contains(vk) && !_listeningLastKeys.Contains(vk))
                {
                    triggerPart = "Key:" + vk;
                    break;
                }
            }
            if (triggerPart == null && Plugin.GamepadState != null)
            {
                foreach (var btn in GetGamepadButtonsToPoll())
                {
                    if (currentPads.Contains(btn) && !_listeningLastPads!.Contains(btn))
                    {
                        triggerPart = "Pad:" + btn;
                        break;
                    }
                }
            }

            _listeningLastKeys = new HashSet<VirtualKey>(currentKeys);
            _listeningLastPads = new HashSet<GamepadButtons>(currentPads);

            if (triggerPart == null) return;

            var parts = new List<string>();
            foreach (var vk in heldModifierKeys.OrderBy(k => k.ToString()))
                parts.Add("Key:" + vk);
            foreach (var btn in heldModifierPads.OrderBy(b => b.ToString()))
                parts.Add("Pad:" + btn);
            parts.Add(triggerPart);

            string assigned = string.Join("+", parts);
            if (_listeningBar >= 0 && _listeningSlot >= 0 && _config != null)
            {
                _config.SetKeybind(_listeningBar, _listeningSlot, assigned);
                ConfigurationManager.Instance?.SaveConfigurations();
                _listeningBar = -1;
                _listeningSlot = -1;
                _listeningLastKeys = null;
                _listeningLastPads = null;
            }
        }

        private static IEnumerable<GamepadButtons> GetGamepadButtonsToPoll()
        {
            return new[]
            {
                GamepadButtons.DpadUp, GamepadButtons.DpadDown, GamepadButtons.DpadLeft, GamepadButtons.DpadRight,
                GamepadButtons.North, GamepadButtons.South, GamepadButtons.West, GamepadButtons.East,
                GamepadButtons.L1, GamepadButtons.L2, GamepadButtons.L3,
                GamepadButtons.R1, GamepadButtons.R2, GamepadButtons.R3,
                GamepadButtons.Start, GamepadButtons.Select
            };
        }

        private static string GetKeybindDisplayName(string value)
        {
            if (string.IsNullOrEmpty(value)) return "None";
            var parts = value.Split('+');
            var display = new List<string>();
            foreach (var p in parts)
            {
                string s = p.Trim();
                if (s.StartsWith("Key:", StringComparison.OrdinalIgnoreCase))
                    display.Add(s.Length > 4 ? s.Substring(4).Replace("VK_", "") : s);
                else if (s.StartsWith("Pad:", StringComparison.OrdinalIgnoreCase))
                    display.Add(s.Length > 4 ? s.Substring(4) : s);
                else
                    display.Add(s);
            }
            return string.Join(" + ", display);
        }
    }
}
