using System;
using System.Collections.Generic;
using Intrepid.Diagnostics;
using Intrepid.FSM;
using Intrepid.Structures;
using Intrepid.Utilities;
using Sirenix.OdinInspector;
using UnityEngine;

namespace Intrepid.Core.Entities
{
    public abstract class EntityStateMachine<TKey, TImplementation> : MonoBehaviour where TKey : IComparable
    {
        protected FStateMachine<TKey, TImplementation> StateMachine { get; } = new();
        [ShowInInspector, ReadOnly, GroupDebug] private TKey StartState { get; set; }
        [SerializeField, ReadOnly, GroupDebug] private TKey currentState;
        [SerializeField, ReadOnly, GroupDebug] private TKey lastState;

        private FixedSizeQueue<TKey> Queue { get; } = new(10);

        public TImplementation CurrentImplementation => StateMachine.GetCurrentState().Implementation();

        public TKey CurrentState
        {
            get => currentState;
            private set => currentState = value;
        }
        
        public TKey LastState
        {
            get => lastState;
            private set => lastState = value;
        }
        
        private void Awake()
        {
            RefreshStates();
        }

        [GroupDebug, Button]
        private void RefreshStates()
        {
            StateMachine.DropStates();
            var StartStates = new List<TKey>();
            foreach (var state in GetComponentsInChildren<EntityState<TKey, TImplementation>>())
            {
                if (state.IsStartState) StartStates.Add(state.GetStateKey());
                StateMachine.AddState(state);
            }

            switch (StartStates.Count)
            {
                case 0:
                    DLogger.LogError("no start state detected, please fix it");
                    break;
                case 1:
                    StartState = StartStates[0];
                    break;
                case > 1:
                    DLogger.LogError("many start state detected, please fix it");
                    break;
            }
        }

        public void StartMachine()
        {
            StateMachine.InitializeWith(StartState);
            StateMachine.Start();
            LastState = StartState;
            Queue.Enqueue(StartState);
        }

        [GroupDebug, Button(ButtonStyle.FoldoutButton)]
        public bool ChangeState(TKey state, params Parameter[] parameters)
        {
            LastState = StateMachine.GetCurrentState().GetStateKey();
            Queue.Enqueue(state);
            return StateMachine.ChangeState(state, parameters);
        }

        public void UpdateStateMachine()
        {
            CurrentState = StateMachine.GetCurrentState().GetStateKey();
        }
    }
}