#if INPUTSYSTEM_SUPPORT
using System;
using System.Globalization;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
using UnityEngine.InputSystem;

public static class InputDeviceWatcher
{
    public enum InputDeviceCategory
    {
        Keyboard,
        Xbox,
        PlayStation,
        Other
    }

    static readonly float DebounceWindow = 1f;
    public static InputDeviceCategory CurrentCategory = InputDeviceCategory.Keyboard;
    public static string CurrentDeviceName = "";

    private static InputAction _anyInputAction;
    private static int _lastDeviceId = -1;
    private static float _lastInputTime = -Mathf.Infinity;

    private static InputDeviceCategory _lastEmittedCategory = InputDeviceCategory.Keyboard;

    public static event Action<InputDeviceCategory> OnDeviceChanged;

    private static bool initialized = false;

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    public static void Initialize()
    {
        if (initialized) return;
        initialized = true;

        CurrentCategory = InputDeviceCategory.Keyboard;
        CurrentDeviceName = "";
        _lastEmittedCategory = CurrentCategory; // 初始化同步

        _anyInputAction = new InputAction("AnyDevice", InputActionType.PassThrough);
        _anyInputAction.AddBinding("<Keyboard>/anyKey");
        _anyInputAction.AddBinding("<Gamepad>/*");
        _anyInputAction.AddBinding("<Joystick>/*");

        _anyInputAction.performed += OnAnyInputPerformed;
        _anyInputAction.Enable();

        InputSystem.onDeviceChange += OnDeviceChange;
#if UNITY_EDITOR
        EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
#endif
    }

#if UNITY_EDITOR
    static void OnPlayModeStateChanged(PlayModeStateChange state)
    {
        if (state == PlayModeStateChange.ExitingPlayMode)
        {
            Dispose();
        }

        EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
    }
#endif

    public static void Dispose()
    {
        if (!initialized) return;
        CurrentCategory = InputDeviceCategory.Keyboard;
        _anyInputAction.performed -= OnAnyInputPerformed;
        _anyInputAction.Disable();
        _anyInputAction.Dispose();

        InputSystem.onDeviceChange -= OnDeviceChange;

        OnDeviceChanged = null;
        initialized = false;

        _lastEmittedCategory = InputDeviceCategory.Keyboard;
    }

    private static void OnAnyInputPerformed(InputAction.CallbackContext ctx)
    {
        if (ctx.control == null || ctx.control.device == null) return;

        var device = ctx.control.device;

        if (!IsRelevantDevice(device)) return;

        int curId = device.deviceId;
        float now = Time.realtimeSinceStartup;

        if (curId == _lastDeviceId) return;
        if (DebounceWindow > 0f && (now - _lastInputTime) < DebounceWindow) return;

        _lastInputTime = now;
        _lastDeviceId = curId;

        CurrentCategory = DetermineCategoryFromDevice(device);
        CurrentDeviceName = device.displayName ?? $"Device_{curId}";

        EmitChange();
    }

    // ------------------ 监听设备变更 --------------------
    private static void OnDeviceChange(InputDevice device, InputDeviceChange change)
    {
        if (change == InputDeviceChange.Removed || change == InputDeviceChange.Disconnected)
        {
            if (device.deviceId == _lastDeviceId)
            {
                _lastDeviceId = -1;
                _lastInputTime = -Mathf.Infinity;
                CurrentDeviceName = "";
                CurrentCategory = InputDeviceCategory.Keyboard;
                EmitChange();
            }
        }
    }

    // ------------------ 分类逻辑 --------------------
    private static InputDeviceCategory DetermineCategoryFromDevice(InputDevice device)
    {
        if (device == null) return InputDeviceCategory.Keyboard;
        // 重要：鼠标不再被视为键盘类（避免鼠标触发时回退到 Keyboard）
        if (device is Keyboard) return InputDeviceCategory.Keyboard;
        if (device is Mouse) return InputDeviceCategory.Other; // 明确忽略鼠标
        if (IsGamepadLike(device)) return GetGamepadCategory(device);

        string combined = $"{device.description.interfaceName} {device.layout} {device.description.product} {device.description.manufacturer} {device.displayName}".ToLower();

        if (combined.Contains("xbox") || combined.Contains("xinput")) return InputDeviceCategory.Xbox;
        if (combined.Contains("dualshock") || combined.Contains("dualsense") || combined.Contains("playstation")) return InputDeviceCategory.PlayStation;

        return InputDeviceCategory.Other;
    }

    private static bool IsGamepadLike(InputDevice device)
    {
        if (device is Gamepad) return true;
        if (device is Joystick) return true;

        var layout = (device.layout ?? "").ToLower();
        // 这里保留 controller/gamepad/joystick 的识别，但忽略 mouse/touch 等
        if (layout.Contains("mouse") || layout.Contains("touch") || layout.Contains("pen")) return false;
        return layout.Contains("gamepad") || layout.Contains("controller") || layout.Contains("joystick");
    }

    private static bool IsRelevantDevice(InputDevice device)
    {
        if (device == null) return false;
        if (device is Keyboard) return true;
        if (IsGamepadLike(device)) return true;
        return false;
    }

    private static InputDeviceCategory GetGamepadCategory(InputDevice device)
    {
        if (device == null) return InputDeviceCategory.Other;

        var iface = (device.description.interfaceName ?? "").ToLower();
        if (iface.Contains("xinput")) return InputDeviceCategory.Xbox;

        if (TryParseVidPidFromCapabilities(device.description.capabilities, out int vendorId, out int _))
        {
            if (vendorId == 0x045E || vendorId == 1118) return InputDeviceCategory.Xbox;
            if (vendorId == 0x054C || vendorId == 1356) return InputDeviceCategory.PlayStation;
        }

        string combined = $"{device.description.interfaceName} {device.layout} {device.description.product} {device.description.manufacturer} {device.displayName}".ToLower();
        if (combined.Contains("xbox")) return InputDeviceCategory.Xbox;
        if (combined.Contains("dualshock") || combined.Contains("playstation")) return InputDeviceCategory.PlayStation;

        return InputDeviceCategory.Other;
    }

    // ------------------ VID/PID 解析 --------------------
    private static bool TryParseVidPidFromCapabilities(string capabilities, out int vendorId, out int productId)
    {
        vendorId = 0;
        productId = 0;
        if (string.IsNullOrEmpty(capabilities)) return false;

        try
        {
            var decVendor = Regex.Match(capabilities, "\"vendorId\"\\s*:\\s*(\\d+)", RegexOptions.IgnoreCase);
            var decProduct = Regex.Match(capabilities, "\"productId\"\\s*:\\s*(\\d+)", RegexOptions.IgnoreCase);

            if (decVendor.Success) int.TryParse(decVendor.Groups[1].Value, out vendorId);
            if (decProduct.Success) int.TryParse(decProduct.Groups[1].Value, out productId);

            return vendorId != 0 || productId != 0;
        }
        catch
        {
            return false;
        }
    }

    private static void EmitChange()
    {
        if (CurrentCategory == _lastEmittedCategory)
        {
            return;
        }

        int vid = GetVendorId();
        int pid = GetProductId();

#if UNITY_EDITOR
        Debug.Log($"输入设备变更 -> {CurrentCategory} 触发设备: {CurrentDeviceName} vid=0x{vid:X} pid=0x{pid:X}");
#endif

        OnDeviceChanged?.Invoke(CurrentCategory);
        _lastEmittedCategory = CurrentCategory;
    }

    private static int GetVendorId()
    {
        foreach (var d in InputSystem.devices)
        {
            if ((d.displayName ?? "") == CurrentDeviceName &&
                TryParseVidPidFromCapabilities(d.description.capabilities, out int v, out int _))
                return v;
        }

        return 0;
    }

    private static int GetProductId()
    {
        foreach (var d in InputSystem.devices)
        {
            if ((d.displayName ?? "") == CurrentDeviceName &&
                TryParseVidPidFromCapabilities(d.description.capabilities, out int _, out int p))
                return p;
        }

        return 0;
    }
}

#endif
