using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.Haptics;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.InputSystem.Utilities;
using System.Runtime.InteropServices;

namespace OkonaPad.Input
{
    /// <summary>
    /// State structure for the OkonaPad controller.
    /// Defines the memory layout and available controls.
    /// Size: 12 bytes, with proper 4-byte alignment for Vector2.
    /// </summary>
    [StructLayout(LayoutKind.Explicit, Size = 12)]
    public struct OkonaPadControllerState : IInputStateTypeInfo
    {
        public FourCC format => new('O', 'K', 'P', 'D');

        // Button states at offset 0 (16 bits for buttons)
        [FieldOffset(0)]
        [InputControl(name = "buttonA", layout = "Button", bit = 0, displayName = "A", shortDisplayName = "A")]
        [InputControl(name = "buttonB", layout = "Button", bit = 1, displayName = "B", shortDisplayName = "B")]
        [InputControl(name = "buttonX", layout = "Button", bit = 2, displayName = "X", shortDisplayName = "X")]
        [InputControl(name = "buttonY", layout = "Button", bit = 3, displayName = "Y", shortDisplayName = "Y")]
        [InputControl(name = "startButton", layout = "Button", bit = 4, displayName = "Start", shortDisplayName = "Start")]
        [InputControl(name = "selectButton", layout = "Button", bit = 5, displayName = "Select", shortDisplayName = "Select")]
        [InputControl(name = "dpad", layout = "Dpad", bit = 6, sizeInBits = 4)]
        [InputControl(name = "dpad/up", layout = "Button", bit = 6)]
        [InputControl(name = "dpad/down", layout = "Button", bit = 7)]
        [InputControl(name = "dpad/left", layout = "Button", bit = 8)]
        [InputControl(name = "dpad/right", layout = "Button", bit = 9)]
        public ushort buttons;

        // 2 bytes padding (offsets 2-3) for proper alignment

        // Joystick values at offset 4 (8 bytes for stick, now 4-byte aligned)
        [FieldOffset(4)]
        [InputControl(name = "leftStick", layout = "Stick", displayName = "Left Stick",
                      processors = "stickDeadzone(min=0.1)")]
        public Vector2 leftStick;

        // Button bit flags
        public const int ButtonA = 0;
        public const int ButtonB = 1;
        public const int ButtonX = 2;
        public const int ButtonY = 3;
        public const int ButtonStart = 4;
        public const int ButtonSelect = 5;
        public const int DpadUp = 6;
        public const int DpadDown = 7;
        public const int DpadLeft = 8;
        public const int DpadRight = 9;

        public OkonaPadControllerState WithButton(int button, bool state)
        {
            if (button < 0 || button > 15)
                throw new System.ArgumentOutOfRangeException(nameof(button), $"Button index must be between 0 and 15, got {button}");

            if (state)
                buttons |= (ushort)(1 << button);
            else
                buttons &= (ushort)~(1 << button);
            return this;
        }

        public bool IsButtonPressed(int button)
        {
            return (buttons & (1 << button)) != 0;
        }
    }

    /// <summary>
    /// Custom input device for OkonaPad controllers.
    /// Provides gamepad controls for mobile web-based input via Okona Online.
    /// </summary>
    [InputControlLayout(stateType = typeof(OkonaPadControllerState), displayName = "OkonaPad")]
#if UNITY_EDITOR
    [UnityEditor.InitializeOnLoad]
#endif
    public class OkonaPadControllerDevice : InputDevice, IDualMotorRumble
    {
        // Control references
        public ButtonControl ButtonA { get; private set; }
        public ButtonControl ButtonB { get; private set; }
        public ButtonControl ButtonX { get; private set; }
        public ButtonControl ButtonY { get; private set; }
        public ButtonControl StartButton { get; private set; }
        public ButtonControl SelectButton { get; private set; }
        public DpadControl DPad { get; private set; }
        public StickControl LeftStick { get; private set; }

        /// <summary>
        /// Controller ID for identification (1-6).
        /// </summary>
        public int ControllerId { get; set; }

        // Rumble support
        private float _lowFrequencyMotorSpeed;
        private float _highFrequencyMotorSpeed;

        // Static layout registration flag
        private static bool _layoutRegistered;
        private static readonly object LayoutLock = new();

        static OkonaPadControllerDevice()
        {
            // Register immediately on domain load to avoid timing issues with editor UI (Input Actions window)
            RegisterLayout();
        }

#if UNITY_EDITOR
        [UnityEditor.InitializeOnLoadMethod]
        private static void EditorEnsureRegistered()
        {
            // Extra safeguard in editor: ensure layout is registered synchronously
            RegisterLayout();
        }
#endif

        [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
        public static void RegisterLayoutForRuntime()
        {
            // Ensure registration at runtime (player and editor play mode)
            RegisterLayout();
        }

        public static void RegisterLayout()
        {
            lock (LayoutLock)
            {
                if (_layoutRegistered) return;

                // Remove any existing registration first (safe call)
                if (InputSystem.LoadLayout("OkonaPad") != null)
                {
                    try { InputSystem.RemoveLayout("OkonaPad"); }
                    catch { /* Ignore removal failures */ }
                }

                try
                {
                    InputSystem.RegisterLayout<OkonaPadControllerDevice>(
                        "OkonaPad",
                        matches: new InputDeviceMatcher()
                            .WithInterface("OkonaPad")
                            .WithDeviceClass("OkonaPad")
                            .WithManufacturer("Okona Online")
                            .WithProduct("OkonaPad Controller")
                    );

                    _layoutRegistered = true;
                }
                catch (System.Exception e)
                {
                    Debug.LogError($"[OkonaPad] Failed to register layout: {e.Message}");
                }
            }
        }

        /// <summary>
        /// Resets the layout registration flag. Used by tests when InputTestFixture resets the Input System.
        /// </summary>
        internal static void ResetLayoutRegistration()
        {
            lock (LayoutLock)
            {
                _layoutRegistered = false;
            }
        }

        protected override void FinishSetup()
        {
            base.FinishSetup();

            // Initialize control references
            ButtonA = GetChildControl<ButtonControl>("buttonA");
            ButtonB = GetChildControl<ButtonControl>("buttonB");
            ButtonX = GetChildControl<ButtonControl>("buttonX");
            ButtonY = GetChildControl<ButtonControl>("buttonY");
            StartButton = GetChildControl<ButtonControl>("startButton");
            SelectButton = GetChildControl<ButtonControl>("selectButton");
            DPad = GetChildControl<DpadControl>("dpad");
            LeftStick = GetChildControl<StickControl>("leftStick");

            Debug.Log($"[OkonaPad] Device setup completed for {name} (ID: {deviceId})");
        }

        public static OkonaPadControllerDevice Create(int controllerId = 1)
        {
            // Check registration status with proper synchronization (don't re-register)
            bool isRegistered;
            lock (LayoutLock)
            {
                isRegistered = _layoutRegistered;
            }

            if (!isRegistered)
            {
                Debug.LogError($"[OkonaPad] Layout not registered, cannot create device for controller {controllerId}");
                return null;
            }

            var deviceDescription = new InputDeviceDescription
            {
                interfaceName = "OkonaPad",
                product = "OkonaPad Controller",
                manufacturer = "Okona Online",
                version = "1.0.0",
                deviceClass = "OkonaPad"
            };

            Debug.Log($"[OkonaPad] Creating device for controller {controllerId}");

            try
            {
                var device = InputSystem.AddDevice(deviceDescription) as OkonaPadControllerDevice;
                if (device != null)
                {
                    device.ControllerId = controllerId;
                    device.displayName = $"OkonaPad {controllerId}";

                    // Verify device was properly added
                    if (device.deviceId == InputDevice.InvalidDeviceId)
                    {
                        Debug.LogError($"[OkonaPad] Device created but has invalid ID for controller {controllerId}");
                        return null;
                    }

                    // Wait for device to be fully initialized
                    if (!device.enabled)
                    {
                        Debug.LogWarning($"[OkonaPad] Device created but not enabled for controller {controllerId}");
                    }

                    Debug.Log($"[OkonaPad] Successfully created device for controller {controllerId} with deviceId={device.deviceId}, name={device.name}, enabled={device.enabled}");
                }
                else
                {
                    Debug.LogError($"[OkonaPad] Failed to create device for controller {controllerId} - AddDevice returned null");
                }

                return device;
            }
            catch (System.Exception e)
            {
                Debug.LogError($"[OkonaPad] Exception creating device for controller {controllerId}: {e.Message}");
                return null;
            }
        }

        /// <summary>
        /// Updates the device state with a new OkonaPadControllerState.
        /// </summary>
        public void UpdateState(OkonaPadControllerState newState)
        {
            // Validate device is ready for state updates
            if (deviceId == InputDevice.InvalidDeviceId)
            {
                Debug.LogWarning($"[OkonaPad] Cannot update state - device {ControllerId} has invalid ID");
                return;
            }

            if (!enabled)
            {
                Debug.LogWarning($"[OkonaPad] Cannot update state - device {ControllerId} is not enabled");
                return;
            }

            try
            {
                InputSystem.QueueStateEvent(this, newState);
            }
            catch (System.Exception e)
            {
                Debug.LogError($"[OkonaPad] Failed to queue state event for controller {ControllerId}: {e.Message}");
            }
        }

        // IDualMotorRumble implementation
        public void SetMotorSpeeds(float lowFrequency, float highFrequency)
        {
            _lowFrequencyMotorSpeed = lowFrequency;
            _highFrequencyMotorSpeed = highFrequency;

            // Notify the system to send rumble to the controller
            OnRumbleRequested?.Invoke(ControllerId, lowFrequency, highFrequency);
        }

        public void PauseHaptics()
        {
            SetMotorSpeeds(0f, 0f);
        }

        public void ResumeHaptics()
        {
            // Resume with previous speeds
            SetMotorSpeeds(_lowFrequencyMotorSpeed, _highFrequencyMotorSpeed);
        }

        public void ResetHaptics()
        {
            _lowFrequencyMotorSpeed = 0f;
            _highFrequencyMotorSpeed = 0f;
            SetMotorSpeeds(0f, 0f);
        }

        /// <summary>
        /// Event for rumble requests. Subscribe to this to forward haptic feedback to the web controller.
        /// Parameters: controllerId, lowFrequency, highFrequency
        /// </summary>
        public static event System.Action<int, float, float> OnRumbleRequested;

#if UNITY_EDITOR
        [UnityEditor.MenuItem("Tools/OkonaPad/Validate Device Registration")]
        public static void ValidateDeviceRegistration()
        {
            try
            {
                var layout = InputSystem.LoadLayout("OkonaPad");
                if (layout != null)
                {
                    Debug.Log("[OkonaPad] Layout is properly registered and available");

                    var devices = InputSystem.devices;
                    int okonaPadCount = 0;
                    foreach (var device in devices)
                    {
                        if (device is OkonaPadControllerDevice)
                        {
                            okonaPadCount++;
                            Debug.Log($"[OkonaPad] Found active device: {device.name} (ID: {device.deviceId})");
                        }
                    }

                    if (okonaPadCount == 0)
                    {
                        Debug.Log("[OkonaPad] No active OkonaPad devices found");
                    }
                }
                else
                {
                    Debug.LogWarning("[OkonaPad] Layout not found - triggering re-registration");
                    _layoutRegistered = false;
                    RegisterLayout();
                }
            }
            catch (System.Exception e)
            {
                Debug.LogError($"[OkonaPad] Error validating device registration: {e.Message}");
            }
        }

        [UnityEditor.MenuItem("Tools/OkonaPad/Force Re-register Device Layout")]
        public static void ForceReregisterLayout()
        {
            Debug.Log("[OkonaPad] Forcing layout re-registration...");
            _layoutRegistered = false;
            RegisterLayout();
            Debug.Log("[OkonaPad] Re-registration complete");
        }
#endif
    }
}
