using System;
using System.Linq;
using Intrepid.Core.Camera;
using Intrepid.Core.Entities;
using Intrepid.Core.Entities.Messages;
using Intrepid.Core.TimeControl;
using Intrepid.GameManagers;
using Intrepid.Gameplay.Entities.Player.ActionBelt;
using Intrepid.Gameplay.Entities.Player.Sentinel.Cadence;
using Intrepid.Gameplay.Entities.Player.Sentinel.StateMachine;
using Intrepid.Gameplay.Flooring.Events;
using Intrepid.Gameplay.Journey.Messages.Events;
using Intrepid.Gameplay.Messages.Events;
using Intrepid.Structures;
using Intrepid.Utilities;
using UnityEngine;

namespace Intrepid.Gameplay.Entities.Player.Sentinel
{
    public class SentinelBrain : EntityBrain
    {
        [GroupInjections, SerializeField] private SentinelConfig config;
        [GroupInjections, SerializeField] private PlayerInputResolver inputResolver;
        [GroupInjections, SerializeField] private SentinelControls controls;
        [GroupInjections, SerializeField] private ActionBeltManager actionBeltManager;
        [GroupInjections, SerializeField] private Cadences cadences;
        
        private bool StateFirstFrame { get; set; }
        protected SentinelStateMachine StateMachine { get; private set; }
        
        public SentinelEntity Entity => RootEntity as SentinelEntity;
        public SentinelStateType CurrentState => StateMachine.CurrentImplementation.GetStateKey();
        public SentinelConfig Config => config;
        public PlayerInputResolver InputResolver => inputResolver;
        public SentinelControls Controls => controls;
        public ActionBeltManager ActionBeltManager => actionBeltManager;
        public Cadences Cadences => cadences;
        public static bool IsCoreStable => !CoreStabilityService.Instance.IsCollapsed;
        public bool IsEnabled { get; private set; }
        
        protected override void InitializeVariables()
        {
            StateMachine = GetComponentInChildren<SentinelStateMachine>();
            foreach (var sentinelState in GetComponentsInChildren<SentinelState>())
            {
                sentinelState.Brain = this;
            }
        }

        protected override void OnBootstrap()
        {
            StateMachine.StartMachine();
            IsEnabled = false;
        }

        protected override void Subscriptions()
        {
            Subscribe<EntityFaillingToAbyssEvent>(e =>
            {
                if (!string.Equals(Entity.Id, e.EntityId, StringComparison.Ordinal)) return;
                StateMachine.ChangeState(SentinelStateType.FallToAbyss);
            });
            Subscribe<EntityLeaveStableGroundEvent>(e =>
            {
                if (!string.Equals(Entity.Id, e.EntityId, StringComparison.Ordinal)) return;
                if (CurrentState == SentinelStateType.Fall) return;
                StateMachine.ChangeState(SentinelStateType.Fall);
            });
            Subscribe<ChangeStageStartEvent>(_ =>
            {
                Entity.RetreatEntity();
                IsEnabled = false;
            });
            Subscribe<ChangeStageTouchedEvent>(_ =>
            {
                ChangeState(SentinelStateType.Nop);
            });
            Subscribe<StageDispatchDeploymentPointEvent>(e =>
            {
                Entity.DeployEntity(e.DeploymentPoint, e.DeploymentDirection);
                CameraSystem.Instance.ForceFocusOn(e.DeploymentPoint);
                IsEnabled = true;
                Proxy.Instance.EventBus.Raise(new EmitFloorDetectionEvent
                {
                    FloorDetection = Entity.FloorDetection
                });
                ChangeState(SentinelStateType.Idle);
            });
            Subscribe<SaveStationActionExecutedEvent>(e =>
            {
                ChangeState(SentinelStateType.SavingStation, ("SaveStationActionExecutedEvent", e));
            });
        }

        public override void UpdateFromUpdatable()
        {
            if (!IsEnabled) return;
            
            StateMachine.UpdateStateMachine();
            
            InputResolver.Sentinel.CaptureAndPrune(TimeSimulation.Instance.ScaledElapsedTime);

            if (StateFirstFrame)
            {
                StateMachine.CurrentImplementation.RunOnFirstFrameOnly();
                StateFirstFrame = false;
            }
            StateMachine.CurrentImplementation.RunBeforeUpdate();
            StateMachine.CurrentImplementation.RunOnUpdate();
            StateMachine.CurrentImplementation.RunAfterUpdate();

            CadenceService.Instance.UpdateCadence(Timer.ScaledTime);
            CoreStabilityService.Instance.UpdateCore(Timer.ScaledTime);
            
            // state machine could take this responsibility in the future...
            if (InputResolver.Sentinel.Reset())
            {
                Entity.ResetLastSafeGroundPosition();
                StateMachine.ChangeState(SentinelStateType.FallLanding);
            }
        }
        
        public override void LateUpdateFromUpdatable()
        {
            if (!IsEnabled) return;
            
            StateMachine.CurrentImplementation.RunOnLateUpdate();
        }

        public override void FixedUpdateFromUpdatable()
        {
            if (!IsEnabled) return;
            
            StateMachine.CurrentImplementation.RunOnFixedUpdate();
        }

        public void ChangeState(SentinelStateType stateType, params (string, object)[] ps)
        {
            var parameters = ps.Select(p => Parameter.With(p.Item1, p.Item2)).ToArray();
            if (StateMachine.ChangeState(stateType, parameters))
            {
                StateFirstFrame = true;
            }
        }
    }
}