using System.Text;
using OkonaPad.Input;
using UnityEngine;
using UnityEngine.InputSystem;

namespace OkonaPad.Samples
{
    /// <summary>
    /// Debug UI overlay that displays OkonaPad controller connection status and input states.
    /// Add this component to any GameObject in your scene to enable the debug display.
    /// </summary>
    public class OkonaDebugUI : MonoBehaviour
    {
        [Header("Display Settings")]
        [SerializeField] private int fontSize = 11;
        [SerializeField] private Color backgroundColor = new Color(0, 0, 0, 0.7f);
        [SerializeField] private Color textColor = Color.white;
        [SerializeField] private Color connectedColor = Color.green;
        [SerializeField] private Color disconnectedColor = new Color(1f, 0.5f, 0f); // Orange
        [SerializeField] private Color pressedColor = Color.yellow;

        private const int MaxSlots = 6;

        private GUIStyle _boxStyle;
        private GUIStyle _labelStyle;
        private GUIStyle _headerStyle;
        private GUIStyle _buttonPressedStyle;
        private GUIStyle _buttonReleasedStyle;
        private GUIStyle _buttonInactiveStyle;
        private GUIStyle _inactiveLabelStyle;
        private Texture2D _backgroundTexture;
        private Texture2D _pressedTexture;
        private Texture2D _inactiveTexture;
        private readonly StringBuilder _sb = new StringBuilder();
        private bool _visible = true;
        private float _selectHeldTime;
        private bool _selectToggled;
        private const float LongPressDuration = 1.0f;

        private void Start()
        {
            // Subscribe to connection events for logging
            if (OkonaInputBridge.Instance != null)
            {
                OkonaInputBridge.Instance.OnDeviceConnected += OnDeviceConnected;
                OkonaInputBridge.Instance.OnDeviceDisconnected += OnDeviceDisconnected;
                OkonaInputBridge.Instance.OnDeviceReconnected += OnDeviceReconnected;
                OkonaInputBridge.Instance.OnDeviceRemoved += OnDeviceRemoved;
            }
        }

        private void OnDestroy()
        {
            if (OkonaInputBridge.Instance != null)
            {
                OkonaInputBridge.Instance.OnDeviceConnected -= OnDeviceConnected;
                OkonaInputBridge.Instance.OnDeviceDisconnected -= OnDeviceDisconnected;
                OkonaInputBridge.Instance.OnDeviceReconnected -= OnDeviceReconnected;
                OkonaInputBridge.Instance.OnDeviceRemoved -= OnDeviceRemoved;
            }

            if (_backgroundTexture != null) Destroy(_backgroundTexture);
            if (_pressedTexture != null) Destroy(_pressedTexture);
            if (_inactiveTexture != null) Destroy(_inactiveTexture);
        }

        private void OnDeviceConnected(int controllerId, OkonaPadControllerDevice device)
        {
            // Event received - UI will update automatically
        }

        private void OnDeviceDisconnected(int controllerId)
        {
            // Event received - UI will update automatically
        }

        private void OnDeviceReconnected(int controllerId, OkonaPadControllerDevice device)
        {
            // Event received - UI will update automatically
        }

        private void OnDeviceRemoved(int controllerId)
        {
            // Event received - UI will update automatically
        }

        private void Update()
        {
            bool selectHeld = IsAnySelectPressed();

            if (selectHeld)
            {
                _selectHeldTime += Time.unscaledDeltaTime;
                if (_selectHeldTime >= LongPressDuration && !_selectToggled)
                {
                    _visible = !_visible;
                    _selectToggled = true;
                }
            }
            else
            {
                _selectHeldTime = 0f;
                _selectToggled = false;
            }
        }

        private bool IsAnySelectPressed()
        {
            var bridge = OkonaInputBridge.Instance;
            if (bridge != null)
            {
                var ids = bridge.GetAllControllerIds();
                foreach (var id in ids)
                {
                    var device = bridge.GetDevice(id);
                    if (device != null && device.SelectButton.isPressed)
                        return true;
                }
            }

#if UNITY_EDITOR
            foreach (var gp in Gamepad.all)
            {
                if (gp.selectButton.isPressed)
                    return true;
            }
#endif

            return false;
        }

        private void InitStyles()
        {
            if (_boxStyle != null) return;

            _backgroundTexture = new Texture2D(1, 1);
            _backgroundTexture.SetPixel(0, 0, backgroundColor);
            _backgroundTexture.Apply();

            _pressedTexture = new Texture2D(1, 1);
            _pressedTexture.SetPixel(0, 0, pressedColor);
            _pressedTexture.Apply();

            _inactiveTexture = new Texture2D(1, 1);
            _inactiveTexture.SetPixel(0, 0, new Color(0.15f, 0.15f, 0.15f, 0.5f));
            _inactiveTexture.Apply();

            _boxStyle = new GUIStyle(GUI.skin.box)
            {
                normal = { background = _backgroundTexture }
            };

            _labelStyle = new GUIStyle(GUI.skin.label)
            {
                fontSize = fontSize,
                normal = { textColor = textColor },
                wordWrap = false
            };

            _headerStyle = new GUIStyle(_labelStyle)
            {
                fontStyle = FontStyle.Bold,
                fontSize = fontSize + 1
            };

            _buttonPressedStyle = new GUIStyle(GUI.skin.box)
            {
                normal = { background = _pressedTexture, textColor = Color.black },
                fontSize = fontSize - 2,
                alignment = TextAnchor.MiddleCenter,
                fontStyle = FontStyle.Bold
            };

            _buttonReleasedStyle = new GUIStyle(GUI.skin.box)
            {
                fontSize = fontSize - 2,
                alignment = TextAnchor.MiddleCenter,
                normal = { textColor = new Color(0.5f, 0.5f, 0.5f) }
            };

            _buttonInactiveStyle = new GUIStyle(GUI.skin.box)
            {
                normal = { background = _inactiveTexture, textColor = new Color(0.3f, 0.3f, 0.3f, 0.5f) },
                fontSize = fontSize - 2,
                alignment = TextAnchor.MiddleCenter
            };

            _inactiveLabelStyle = new GUIStyle(_labelStyle)
            {
                normal = { textColor = new Color(0.3f, 0.3f, 0.3f, 0.5f) }
            };
        }

        private void OnGUI()
        {
            InitStyles();

            float margin = 10;

            if (!_visible)
            {
                _labelStyle.normal.textColor = Color.gray;
                GUI.Label(new Rect(margin, margin, 300, 20),
                    "Long press Select to show debug menu", _labelStyle);
                _labelStyle.normal.textColor = textColor;
                return;
            }

            float slotColumnWidth = 195;
            float panelWidth = slotColumnWidth * 3 + 20;
            float panelHeight = 360;

            // Draw panel in top-left corner
            GUILayout.BeginArea(new Rect(margin, margin, panelWidth, panelHeight), _boxStyle);

            // Top status bar
            DrawStatusBar();
            GUILayout.Space(3);

            // 6 controller slots: 3 columns x 2 rows
            DrawSlotColumns(slotColumnWidth);

            GUILayout.FlexibleSpace();
            _labelStyle.normal.textColor = Color.gray;
            GUILayout.Label("Long press Select to hide debug menu", _labelStyle);
            _labelStyle.normal.textColor = textColor;

            GUILayout.EndArea();
        }

        private void DrawStatusBar()
        {
            GUILayout.BeginHorizontal();
            GUILayout.Label("OkonaPad Debug", _headerStyle);
            GUILayout.FlexibleSpace();

            var pim = PlayerInputManager.instance;
            if (pim != null)
                GUILayout.Label($"PIM: {pim.joinBehavior}  Players: {pim.playerCount}", _labelStyle);
            else
            {
                _labelStyle.normal.textColor = Color.red;
                GUILayout.Label("PIM: NOT FOUND", _labelStyle);
                _labelStyle.normal.textColor = textColor;
            }
            GUILayout.EndHorizontal();

            GUILayout.BeginHorizontal();

            var bridge = OkonaInputBridge.Instance;
            if (bridge != null)
            {
                var stats = bridge.GetDiagnostics();
                GUILayout.Label($"Connects: {stats.connectionAttempts}  Msgs: {stats.messagesReceived}  Active: {stats.connectedDevices}  Disconn: {stats.disconnectedDevices}", _labelStyle);

                if (!string.IsNullOrEmpty(stats.lastError))
                {
                    _labelStyle.normal.textColor = Color.red;
                    GUILayout.Label($"Err: {stats.lastError}", _labelStyle);
                    _labelStyle.normal.textColor = textColor;
                }
            }
            else
            {
                _labelStyle.normal.textColor = Color.red;
                GUILayout.Label("Bridge: NULL", _labelStyle);
                _labelStyle.normal.textColor = textColor;
            }

            GUILayout.EndHorizontal();
        }

        private void DrawSlotColumns(float columnWidth)
        {
            var bridge = OkonaInputBridge.Instance;

            GUILayout.BeginHorizontal();
            for (int col = 0; col < 3; col++)
            {
                GUILayout.BeginVertical(GUILayout.Width(columnWidth));
                for (int row = 0; row < 2; row++)
                {
                    int id = col * 2 + row + 1;
                    DrawSlotInput(bridge, id);
                    GUILayout.Space(8);
                }
                GUILayout.EndVertical();

                if (col < 2)
                    GUILayout.Space(5);
            }
            GUILayout.EndHorizontal();
        }

        private void DrawSlotInput(OkonaInputBridge bridge, int id)
        {
            // Check for OkonaPad device first
            if (bridge != null)
            {
                var device = bridge.GetDevice(id);
                var state = bridge.GetConnectionState(id);

                if (state == OkonaInputBridge.DeviceConnectionState.Connected && device != null)
                {
                    _labelStyle.normal.textColor = connectedColor;
                    GUILayout.Label($"Slot {id}: Connected", _labelStyle);
                    _labelStyle.normal.textColor = textColor;

                    var leftStick = device.LeftStick.ReadValue();
                    DrawGamepadButtons(
                        device.DPad.up.isPressed, device.DPad.down.isPressed,
                        device.DPad.left.isPressed, device.DPad.right.isPressed,
                        device.ButtonA.isPressed, device.ButtonB.isPressed,
                        device.ButtonX.isPressed, device.ButtonY.isPressed,
                        device.StartButton.isPressed, device.SelectButton.isPressed,
                        leftStick, true
                    );
                    return;
                }

                if (state == OkonaInputBridge.DeviceConnectionState.Disconnected)
                {
                    _labelStyle.normal.textColor = disconnectedColor;
                    GUILayout.Label($"Slot {id}: Reconnecting", _labelStyle);
                    _labelStyle.normal.textColor = textColor;

                    DrawGamepadButtons(
                        false, false, false, false,
                        false, false, false, false,
                        false, false, Vector2.zero, false
                    );
                    return;
                }
            }

#if UNITY_EDITOR
            // In Editor, fill empty slots with physical gamepads
            int gamepadIndex = id - 1;
            if (gamepadIndex < Gamepad.all.Count)
            {
                var gp = Gamepad.all[gamepadIndex];
                _labelStyle.normal.textColor = connectedColor;
                GUILayout.Label($"Slot {id}: {gp.displayName}", _labelStyle);
                _labelStyle.normal.textColor = textColor;

                var leftStick = gp.leftStick.ReadValue();
                DrawGamepadButtons(
                    gp.dpad.up.isPressed, gp.dpad.down.isPressed,
                    gp.dpad.left.isPressed, gp.dpad.right.isPressed,
                    gp.buttonSouth.isPressed, gp.buttonEast.isPressed,
                    gp.buttonWest.isPressed, gp.buttonNorth.isPressed,
                    gp.startButton.isPressed, gp.selectButton.isPressed,
                    leftStick, true
                );
                return;
            }
#endif

            // Empty slot
            _labelStyle.normal.textColor = Color.gray;
            GUILayout.Label($"Slot {id}: Empty", _labelStyle);
            _labelStyle.normal.textColor = textColor;

            DrawGamepadButtons(
                false, false, false, false,
                false, false, false, false,
                false, false, Vector2.zero, false
            );
        }

        private const float GamepadGroupWidth = 185f;
        private const float GamepadGroupHeight = 84f;
        private const float BtnW = 24f;
        private const float BtnH = 18f;

        private void DrawGamepadButtons(
            bool dpadUp, bool dpadDown, bool dpadLeft, bool dpadRight,
            bool btnA, bool btnB, bool btnX, bool btnY,
            bool start, bool select, Vector2 stick, bool active)
        {
            // Reserve space in GUILayout flow for the absolute-positioned group
            var rect = GUILayoutUtility.GetRect(GamepadGroupWidth, GamepadGroupHeight);

            GUI.BeginGroup(rect);

            // D-pad cross (left side)
            DrawButtonAbsolute(new Rect(13, 0, BtnW, BtnH), "U", dpadUp, active);
            DrawButtonAbsolute(new Rect(0, 20, BtnW, BtnH), "L", dpadLeft, active);
            DrawButtonAbsolute(new Rect(26, 20, BtnW, BtnH), "R", dpadRight, active);
            DrawButtonAbsolute(new Rect(13, 40, BtnW, BtnH), "D", dpadDown, active);

            // Start/Select (center)
            DrawButtonAbsolute(new Rect(66, 20, 28, BtnH), "Sel", select, active);
            DrawButtonAbsolute(new Rect(97, 20, 24, BtnH), "St", start, active);

            // Face buttons diamond (right side)
            DrawButtonAbsolute(new Rect(143, 0, BtnW, BtnH), "Y", btnY, active);
            DrawButtonAbsolute(new Rect(130, 20, BtnW, BtnH), "X", btnX, active);
            DrawButtonAbsolute(new Rect(156, 20, BtnW, BtnH), "B", btnB, active);
            DrawButtonAbsolute(new Rect(143, 40, BtnW, BtnH), "A", btnA, active);

            // Stick text
            _sb.Clear();
            _sb.Append("Stick: ");
            _sb.Append(stick.x.ToString("F1"));
            _sb.Append(", ");
            _sb.Append(stick.y.ToString("F1"));
            GUI.Label(new Rect(0, 62, GamepadGroupWidth, 20), _sb.ToString(),
                active ? _labelStyle : _inactiveLabelStyle);

            GUI.EndGroup();
        }

        private void DrawButtonAbsolute(Rect position, string label, bool pressed, bool active)
        {
            GUIStyle style;
            if (!active)
                style = _buttonInactiveStyle;
            else
                style = pressed ? _buttonPressedStyle : _buttonReleasedStyle;

            GUI.Box(position, label, style);
        }

    }
}
