using Intrepid.Core.Entities;
using Intrepid.Core.TimeControl;
using Intrepid.Diagnostics;
using Intrepid.GameManagers;
using Intrepid.Gameplay.Entities.Player.ActionBelt.Events;
using Intrepid.Gameplay.Entities.Player.Sentinel.Cadence;

namespace Intrepid.Gameplay.Entities.Player.Sentinel.StateMachine
{
    public abstract class SentinelState : EntityState<SentinelStateType, SentinelState>, IControllableEntityBrain
    {
        public SentinelBrain Brain { get; set; }

        public override bool CanEnterState => true;
        public override bool CanExitState => true;
        
        public override SentinelState Implementation()
        {
            return this;
        }
        
        public virtual void RunOnFirstFrameOnly()
        {
        }

        public abstract void RunOnUpdate();

        public virtual void RunBeforeUpdate()
        {
        }

        public virtual void RunAfterUpdate()
        {
        }

        public virtual void RunOnLateUpdate()
        {
        }

        public virtual void RunOnFixedUpdate()
        {
        }
        
        protected void VerifyInteraction()
        {   
            if (!Brain.ActionBeltManager.IsOperational) return;
            if (Brain.ActionBeltManager.IsEmpty) return;
            if (Brain.InputResolver.Sentinel.Interact())
            {
                Proxy.Instance.EventBus.Raise(new ActionBeltTakeActionEvent
                {
                    ActionBeltManger = Brain.ActionBeltManager.ActionBeltManagerTag,
                });
                return;
            }

            var left = Brain.InputResolver.Sentinel.DPadLeft();
            var right = Brain.InputResolver.Sentinel.DPadRight();
            
            if (left && right) return;
            if (left)
            {
                Proxy.Instance.EventBus.Raise(new ActionBeltNextLeftEvent
                {
                    ActionBeltManger = Brain.ActionBeltManager.ActionBeltManagerTag,
                });
            }
            if (right)
            {
                Proxy.Instance.EventBus.Raise(new ActionBeltNextRightEvent
                {
                    ActionBeltManger = Brain.ActionBeltManager.ActionBeltManagerTag,
                });
            }
        }
        
        protected void KeepCadenceWithInputAt(CadenceProfile nextProfile, float targetTime, float inputAt)
        {
            var previousEvaluation = CadenceService.Instance.TryEndCadenceProfileWithInputAt(inputAt);
            if (previousEvaluation.Band != CadenceBand.None)
            {
                DLogger.LogDebug(
                    $"CADENCE CONNECTED: {previousEvaluation.Band} " +
                    $"set={previousEvaluation.SettlementFactor} " +
                    $"rec={previousEvaluation.Recovery}"
                );

                Brain.Controls.CadenceConnectedCounterInc();
                CoreStabilityService.Instance.Settle(previousEvaluation);
            }
            else
            {
                Brain.Controls.ExpireCadence();
            }

            CoreStabilityService.Instance.ProjectCost(nextProfile.MaxCoreCost);
            CadenceService.Instance.BeginCadence(nextProfile, targetTime);
        }
        
        protected void EndCadence()
        {
            var evaluation = CadenceService.Instance.EndCadence();
            DLogger.LogDebug(
                $"CADENCE ENDED: {evaluation.Band} " +
                $"set={evaluation.SettlementFactor} " +
                $"rec={evaluation.Recovery}"
            );
            CoreStabilityService.Instance.Settle(evaluation);
            Brain.Controls.ExpireCadence();
        }
        
        protected bool TryConsumeDash()
        {
            if (Brain.InputResolver.Sentinel.TryConsumeDash(out var dashTimeAt)) // dash with no ground: this could be a feature to be unlocked
            //if (Brain.Entity.IsGrounded && Brain.InputResolver.TryConsumeDash(out var dashTimeAt))
            {
                var targetTime = Brain.Config.DashImpulse.Duration + Brain.Config.DashRecovery.Duration;
                KeepCadenceWithInputAt(Brain.Cadences.DashProfile, targetTime, dashTimeAt);
                return true;
            }
            return false;
        }

        protected bool CheckPDA()
        {
            if (Brain.InputResolver.Sentinel.EnterPDA())
            {
                Brain.ChangeState(SentinelStateType.PDA);
                return true;
            }
            return false;
        }

    }
}