using System;
using System.Collections.Generic;
using Intrepid.Core;
using Intrepid.Utilities;
using UnityEngine;

namespace Intrepid.Gameplay.Entities.Player.Sentinel.Inputs
{
    internal enum SentinelBufferedActionType
    {
        None,
        Dash,
    }

    [CreateAssetMenu(fileName = "Sentinel Input Resolver", menuName = "GON/Services/Sentinel Input Resolver")]
    public sealed class SentinelInputResolver : InputResolver, SentinelInputs
    {
        [GroupConfigurations, SerializeField, Range(0, 5)] private float inputLifespan = .5f;
        
        private Dictionary<SentinelBufferedActionType, float> IntentBuffer { get; set; }
        private SentinelInputActions SentinelInputActions { get; set; }

        public override void Setup()
        {
            IntentBuffer = new Dictionary<SentinelBufferedActionType, float>();
            SentinelInputActions = SentinelInputActions.BuildSentinelInputActions();
        }
        
        public void CaptureAndPrune(float elapsedTime)
        {   
            IntentBuffer.RemoveAll((_, at) => elapsedTime - at > inputLifespan);
            if (SentinelInputActions.Dash())
            {
                IntentBuffer.TryAdd(SentinelBufferedActionType.Dash, elapsedTime);
            }
        }

        public void Clear()
        {
            InnerClear();
        }

        public void ClearDash()
        {
            InnerClear(SentinelBufferedActionType.Dash);
        }
        
        private void InnerClear(SentinelBufferedActionType bufferedActionType = SentinelBufferedActionType.None)
        {
            if (bufferedActionType == SentinelBufferedActionType.None)
            {
                IntentBuffer.Clear();
                return;
            }
            IntentBuffer.Remove(bufferedActionType);
        }
        
        private bool TryPeek(SentinelBufferedActionType bufferedActionType, out float actionTypeTimeAt)
        {
            if (IntentBuffer.TryGetValue(bufferedActionType, out actionTypeTimeAt))
            {
                return true;
            }
            
            actionTypeTimeAt = 0f;
            return false;
        }

        private bool TryConsume(SentinelBufferedActionType bufferedActionType, out float actionTypeTimeAt)
        {
            if (!TryPeek(bufferedActionType, out actionTypeTimeAt))
            {
                return false;
            }

            IntentBuffer.Remove(bufferedActionType);
            return true;
        }
        
        // Buffered Input Actions
        public bool TryPeekDash(out float dashTimeAt) => TryPeek(SentinelBufferedActionType.Dash, out dashTimeAt);
        public bool TryConsumeDash(out float dashTimeAt) => TryConsume(SentinelBufferedActionType.Dash, out dashTimeAt);
        
        // No Buffered Input Actions
        public Func<Vector2> Movement => SentinelInputActions.Movement;
        public Func<bool> Interact => SentinelInputActions.Interact;
        public Func<bool> DPadLeft => SentinelInputActions.DPadLeft;
        public Func<bool> DPadRight => SentinelInputActions.DPadRight;
        public Func<bool> Reset => SentinelInputActions.Reset;
        public Func<bool> EnterPDA => SentinelInputActions.EnterPDA;
    }
}