using UnityEngine;
using EventsAppQuest.Core;

namespace EventsAppQuest.Presentation
{
    /// <summary>
    /// Resolve o alvo principal actual considerando hit XR, hover, foco e seleção, com stickiness
    /// para reduzir oscilações. Prepara para futura integração com gaze real.
    /// </summary>
    public class QuestTargetResolverController : MonoBehaviour
    {
        [Tooltip("Número mínimo de frames que o raw detectado deve permanecer igual antes de trocar o resolvido.")]
        [SerializeField] private int _minStableFramesToSwitch = 4;

        [Tooltip("Número de frames sem hit válido antes de limpar o resolvido (perda de alvo).")]
        [SerializeField] private int _minFramesToLose = 6;

        private QuestTargetCandidate _resolvedCandidate;
        private string _lastRawTargetId;
        private int _rawStableFrames;
        private int _framesWithoutHit;
        private bool _stickinessAppliedThisFrame;

        /// <summary>Candidato resolvido actual (pode ser null).</summary>
        public QuestTargetCandidate ResolvedCandidate => _resolvedCandidate;

        /// <summary>Id do alvo resolvido (null se nenhum).</summary>
        public string ResolvedTargetId => _resolvedCandidate?.TargetId;

        /// <summary>Se true, no último Resolve foi aplicada stickiness (manteve alvo anterior).</summary>
        public bool StickinessAppliedThisFrame => _stickinessAppliedThisFrame;

        /// <summary>Define o número mínimo de frames estáveis para trocar de alvo.</summary>
        public void SetMinStableFramesToSwitch(int frames)
        {
            _minStableFramesToSwitch = Mathf.Max(1, frames);
        }

        /// <summary>
        /// Resolve o alvo principal a partir do raw detectado, foco e seleção.
        /// Chamar cada frame após actualizar o pointer/raycast.
        /// </summary>
        /// <param name="rawDetectedTargetId">Id do alvo detectado bruto (pointer/raycast).</param>
        /// <param name="rawSourceMode">Origem do raw (SimulatedPointer, XRRaycast, etc.).</param>
        /// <param name="focusTargetType">Painel em foco (para fallback quando raw é null).</param>
        /// <param name="selectedTargetId">Id do alvo selecionado (para prioridade/fallback).</param>
        /// <param name="pointableTargets">Lista de alvos apontáveis (para tipo e prioridade).</param>
        /// <returns>Candidato resolvido (pode ser null).</returns>
        public QuestTargetCandidate Resolve(
            string rawDetectedTargetId,
            QuestTargetSourceMode rawSourceMode,
            QuestPresentationFocusTargetType focusTargetType,
            string selectedTargetId,
            System.Collections.Generic.IReadOnlyList<QuestPointableTarget> pointableTargets)
        {
            _stickinessAppliedThisFrame = false;

            if (pointableTargets == null) pointableTargets = new System.Collections.Generic.List<QuestPointableTarget>(0);

            QuestTargetCandidate rawCandidate = null;
            if (!string.IsNullOrEmpty(rawDetectedTargetId))
            {
                rawCandidate = BuildCandidate(rawDetectedTargetId, rawSourceMode, pointableTargets, isFromRaw: true);
                _framesWithoutHit = 0;
            }
            else
            {
                _framesWithoutHit++;
                if (_framesWithoutHit >= _minFramesToLose)
                {
                    _resolvedCandidate = null;
                    _lastRawTargetId = null;
                    _rawStableFrames = 0;
                    return null;
                }
                var focusFallbackId = GetFocusFallbackTargetId(focusTargetType);
                if (!string.IsNullOrEmpty(focusFallbackId))
                    rawCandidate = BuildCandidate(focusFallbackId, QuestTargetSourceMode.FocusFallback, pointableTargets, isFromRaw: false);
            }

            if (rawCandidate == null)
            {
                if (_framesWithoutHit >= _minFramesToLose)
                    _resolvedCandidate = null;
                else
                    _stickinessAppliedThisFrame = _resolvedCandidate != null;
                return _resolvedCandidate;
            }

            if (rawCandidate.TargetId == _lastRawTargetId)
                _rawStableFrames++;
            else
            {
                _lastRawTargetId = rawCandidate.TargetId;
                _rawStableFrames = 1;
            }

            bool shouldSwitch = _resolvedCandidate == null
                || (_rawStableFrames >= _minStableFramesToSwitch && rawCandidate.TargetId != _resolvedCandidate.TargetId)
                || (_resolvedCandidate.SourceMode == QuestTargetSourceMode.FocusFallback && rawCandidate.SourceMode != QuestTargetSourceMode.FocusFallback);

            if (shouldSwitch)
            {
                rawCandidate.StabilityScore = _rawStableFrames;
                _resolvedCandidate = rawCandidate;
                return _resolvedCandidate;
            }

            _stickinessAppliedThisFrame = true;
            if (_resolvedCandidate != null)
                _resolvedCandidate.StabilityScore = _rawStableFrames;
            return _resolvedCandidate;
        }

        private static QuestTargetCandidate BuildCandidate(
            string targetId,
            QuestTargetSourceMode sourceMode,
            System.Collections.Generic.IReadOnlyList<QuestPointableTarget> pointableTargets,
            bool isFromRaw)
        {
            QuestPointableTarget pointable = null;
            foreach (var t in pointableTargets)
            {
                if (t.TargetId == targetId) { pointable = t; break; }
            }
            var type = pointable?.TargetType ?? QuestPointableTargetType.Panel;
            var priority = GetPriorityScore(type, targetId);
            var visibility = sourceMode == QuestTargetSourceMode.XRRaycast ? 1f : (sourceMode == QuestTargetSourceMode.SimulatedPointer ? 0.7f : 0.5f);
            return new QuestTargetCandidate
            {
                TargetId = targetId,
                TargetType = type,
                PriorityScore = priority,
                StabilityScore = 0f,
                VisibilityScore = visibility,
                IsWorldTarget = false,
                IsUITarget = true,
                SourceMode = sourceMode,
                Notes = isFromRaw ? "Raw" : "Fallback"
            };
        }

        private static float GetPriorityScore(QuestPointableTargetType type, string targetId)
        {
            if (targetId == "action_next" || targetId == "mode_toggle") return 1.2f;
            if (type == QuestPointableTargetType.ActionTarget || type == QuestPointableTargetType.NavigationTarget) return 1.1f;
            if (targetId == "guided_step" || targetId == "hotspot") return 1f;
            return 0.9f;
        }

        private static string GetFocusFallbackTargetId(QuestPresentationFocusTargetType focusTargetType)
        {
            switch (focusTargetType)
            {
                case QuestPresentationFocusTargetType.GuidedStepPanel: return "guided_step";
                case QuestPresentationFocusTargetType.HotspotPanel: return "hotspot";
                case QuestPresentationFocusTargetType.SceneStatePanel: return "scene_state";
                case QuestPresentationFocusTargetType.ProgressPanel: return "action_next";
                case QuestPresentationFocusTargetType.ActionPanel: return "mode_toggle";
                default: return "guided_step";
            }
        }
    }
}
