using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;

namespace EventsAppQuest.Presentation
{
    /// <summary>
    /// Leitura opcional de input XR via reflexão (UnityEngine.XR.InputDevices), sem referência obrigatória ao módulo XR.
    /// Se o módulo não estiver disponível ou não houver dispositivos, retorna false e não quebra.
    /// Mapeamento mínimo: trigger → primário, secondaryButton → secundário, primary2DAxis.y → next/prev.
    /// </summary>
    internal static class QuestXRInputReader
    {
        private static bool? _cachedAvailability;
        private static Type _inputDeviceType;
        private static Type _commonUsagesType;
        private static MethodInfo _getDevicesMethod;
        private static MethodInfo _tryGetBoolMethod;
        private static MethodInfo _tryGetVector2Method;
        private static FieldInfo _triggerButtonField;
        private static FieldInfo _secondaryButtonField;
        private static FieldInfo _primary2DAxisField;
        private static PropertyInfo _characteristicsProperty;
        private static int _controllerFlagValue;

        /// <summary>True se a leitura XR está disponível (módulo e dispositivos encontrados).</summary>
        public static bool IsAvailable
        {
            get
            {
                if (_cachedAvailability.HasValue)
                    return _cachedAvailability.Value;
                EnsureResolved();
                return _cachedAvailability ?? false;
            }
        }

        /// <summary>Tenta ler um controlador XR: trigger (primário), secondary (secundário), axisY (-1 a 1). Retorna true se leitura foi feita.</summary>
        public static bool TryReadControllerInput(out bool trigger, out bool secondary, out float axisY)
        {
            trigger = false;
            secondary = false;
            axisY = 0f;

            if (!EnsureResolved() || _getDevicesMethod == null || _inputDeviceType == null)
                return false;

            try
            {
                Type listType = typeof(List<>).MakeGenericType(_inputDeviceType);
                object list = Activator.CreateInstance(listType);
                _getDevicesMethod.Invoke(null, new[] { list });

                int count = (int)listType.GetProperty("Count").GetValue(list);
                if (count == 0)
                    return false;

                MethodInfo getItem = listType.GetMethod("get_Item");
                for (int i = 0; i < count; i++)
                {
                    object device = getItem.Invoke(list, new object[] { i });
                    if (device == null) continue;

                    if (_characteristicsProperty != null)
                    {
                        object chars = _characteristicsProperty.GetValue(device);
                        if (chars != null)
                        {
                            int c = Convert.ToInt32(chars);
                            if ((c & _controllerFlagValue) == 0)
                                continue;
                        }
                    }

                    if (_triggerButtonField != null && _tryGetBoolMethod != null)
                    {
                        object usage = _triggerButtonField.GetValue(null);
                        object[] args = new object[] { usage, false };
                        _tryGetBoolMethod.Invoke(device, args);
                        trigger = args[1] is bool b && b;
                    }

                    if (_secondaryButtonField != null && _tryGetBoolMethod != null)
                    {
                        object usage = _secondaryButtonField.GetValue(null);
                        object[] args = new object[] { usage, false };
                        _tryGetBoolMethod.Invoke(device, args);
                        secondary = args[1] is bool b && b;
                    }

                    if (_primary2DAxisField != null && _tryGetVector2Method != null)
                    {
                        object usage = _primary2DAxisField.GetValue(null);
                        object[] args = new object[] { usage, Vector2.zero };
                        _tryGetVector2Method.Invoke(device, args);
                        if (args[1] is Vector2 v)
                            axisY = v.y;
                    }

                    _cachedAvailability = true;
                    return true;
                }
            }
            catch
            {
                _cachedAvailability = false;
                return false;
            }

            return false;
        }

        private static bool EnsureResolved()
        {
            if (_cachedAvailability.HasValue && _cachedAvailability.Value)
                return true;
            if (_cachedAvailability == false)
                return false;

            try
            {
                const string ns = "UnityEngine.XR";
                string[] assemblies = { "UnityEngine.XRModule", "UnityEngine" };

                foreach (string asmName in assemblies)
                {
                    _inputDeviceType = Type.GetType(ns + ".InputDevice, " + asmName);
                    if (_inputDeviceType == null) continue;

                    Type inputDevicesType = Type.GetType(ns + ".InputDevices, " + asmName);
                    if (inputDevicesType == null) continue;

                    Type listOfDevice = typeof(List<>).MakeGenericType(_inputDeviceType);
                    _getDevicesMethod = inputDevicesType.GetMethod("GetDevices", BindingFlags.Public | BindingFlags.Static, null, new[] { listOfDevice }, null);
                    if (_getDevicesMethod == null) continue;

                    _commonUsagesType = Type.GetType(ns + ".CommonUsages, " + asmName);
                    if (_commonUsagesType == null) continue;

                    _triggerButtonField = _commonUsagesType.GetField("triggerButton", BindingFlags.Public | BindingFlags.Static);
                    _secondaryButtonField = _commonUsagesType.GetField("secondaryButton", BindingFlags.Public | BindingFlags.Static);
                    _primary2DAxisField = _commonUsagesType.GetField("primary2DAxis", BindingFlags.Public | BindingFlags.Static);

                    _characteristicsProperty = _inputDeviceType.GetProperty("characteristics", BindingFlags.Public | BindingFlags.Instance);
                    Type charType = Type.GetType(ns + ".InputDeviceCharacteristics, " + asmName);
                    if (charType != null)
                    {
                        FieldInfo controllerField = charType.GetField("Controller", BindingFlags.Public | BindingFlags.Static);
                        if (controllerField != null)
                            _controllerFlagValue = Convert.ToInt32(controllerField.GetValue(null));
                    }

                    foreach (var m in _inputDeviceType.GetMethods(BindingFlags.Public | BindingFlags.Instance))
                    {
                        if (m.Name != "TryGetFeatureValue" || !m.IsGenericMethodDefinition) continue;
                        var pars = m.GetParameters();
                        if (pars.Length != 2) continue;
                        try
                        {
                            _tryGetBoolMethod = m.MakeGenericMethod(typeof(bool));
                            _tryGetVector2Method = m.MakeGenericMethod(typeof(Vector2));
                            break;
                        }
                        catch { /* ignore */ }
                    }

                    if (_tryGetBoolMethod != null)
                    {
                        _cachedAvailability = true;
                        return true;
                    }
                }

                _cachedAvailability = false;
                return false;
            }
            catch
            {
                _cachedAvailability = false;
                return false;
            }
        }

        /// <summary>Força reavaliação da disponibilidade (ex.: após subsistema XR arrancar).</summary>
        public static void ResetAvailabilityCache()
        {
            _cachedAvailability = null;
        }
    }
}
