using FL.Battle.Components.Skills;
using System;
using System.Collections.Generic;
using UnityEngine;
using XGame.Database;
using XGame.Framework.Components;
using XGame.Framework.Interfaces;
using XGame.Framework.Time;

namespace FL.Battle.Components
{
    public interface ISkillContext
    {
        IEntity Entity { get; }
        Transform ActionRoot { get; }
        Vector3 Position { get; }
        ITimeModule Time { get; }
        IAnimator Animator { get; }
        VfxComponent Vfx { get; }
        ITargetSelector Selector { get; }
        void FindTargetAsync(Action<ITarget> onSelected);
    }
    public class SkillComponent : Component<ISkillContext>, IReset
    {
        private const int NO_SKILL = -1;
        private ISkillActor _skillActor;
        private ISkillActor _attackActor;

        public ITarget Target { get; private set; }

        private int _playingSkill = NO_SKILL;
        /// <summary>
        /// 技能冷却定时器
        /// key: skillId
        /// value: ITimer
        /// </summary>
        private Dictionary<int, ITimer> _skillCDTimers = new Dictionary<int, ITimer>();
        /// <summary>
        /// 技能队列
        /// 冷却时间到的技能进入队列等待播放
        /// </summary>
        private Queue<int> _skillQueue = new Queue<int>();

        protected override void OnDisable()
        {
            Stop(false);
        }
        protected override void OnDispose()
        {
            Stop(true);
        }
        void IReset.Reset()
        {
            Stop(false);
        }
        #region 目标选择
        ///// <summary>
        ///// 默认的目标类型
        ///// </summary>
        //private EEntityType DefaultTargetType => Context.Entity.EntityType == EEntityType.Monster ? EEntityType.Player : EEntityType.Monster;
        //public void FindTargetAsync(int skillId = 0)
        //{
        //    Context.Selector.FindAsync(this, DefaultTargetType, (target) =>
        //    {
        //        Context.Entity.State = EEntityState.Battle;
        //        Target = target;
        //        EnqueueSkill(skillId);
        //        StartSkill();
        //    });
        //}
        private void OnSelectedTarget(ITarget target)
        {
            Context.Entity.State = EEntityState.Battle;
            Target = target;
            StartSkill();
        }
        public void Prepare()
        {
            //初始化技能
            foreach (var skillId in Context.Entity.SkillIds)
            {
                EnqueueSkill(skillId);
            }
            //选择目标
            Context.FindTargetAsync(OnSelectedTarget);
        }

        #endregion
        /// <summary>
        /// 冷却时间到的技能进入队列等待播放
        /// </summary>
        /// <param name="skillId"></param>
        private bool EnqueueSkill(int skillId)
        {
            if (Context.Entity.State == EEntityState.Dead)
                return false;
            //var entityName = Context.Entity.Attr.Name;
            if (_playingSkill == skillId)
            {
                //Log.Debug($"技能正在施法. Entity:{entityName} Skill:{skillId}");
                return false;
            }
            if (_skillCDTimers.ContainsKey(skillId))
            { // 技能还在cd中
                //Log.Debug($"技能还在冷却中. Entity:{entityName} Skill:{skillId}");
                return false;
            }
            if (_skillQueue.Contains(skillId))
            {
                //Log.Debug($"技能队列重复. Entity:{entityName} Skill:{skillId}");
                return false;
            }
            //Log.Debug($"EnqueueSkill Entity:{entityName} Skill:{skillId} _playingSkill:{_playingSkill}");
            _skillQueue.Enqueue(skillId);
            return true;
        }
        private void StartSkill()
        {
            if (_playingSkill != -1)
                return;
            // 没有技能 || 已死亡 || 移动中
            if (_skillQueue.Count == 0 || (Context.Entity.State is EEntityState.Dead or EEntityState.Protected))
                return;
            var skillId = _skillQueue.Peek();
            var isNoTarget = IsNoTarget(skillId);
            // 召唤类技能，不需要选目标
            if (isNoTarget == false && (Target == null || Target.Entity.State == EEntityState.Dead))
            { // 没有目标或目标已死亡
                Context.FindTargetAsync(OnSelectedTarget);
                return;
            }
            skillId = _skillQueue.Dequeue();
            _playingSkill = skillId;
            //Log.Debug($"StartSkill Entity:{Entity.Attr.Name} Skill:{skillId}");
            GetActor(skillId).Play(skillId, isNoTarget ? null : Target, OnSkillCompleted);
        }
        private void OnSkillCompleted(int skillId, int skillCD)
        {
            //var uid = UIDDefine.New();
            //Log.Debug($"OnSkillCompleted Entity:{Entity.Attr.Name} Skill:{skillId} CD:{skillCD} UID:{uid} start");
            if (skillCD > 0)
            {
                var cdTimer = Context.Time.AddDelayTimer(skillCD, () =>
                {
                    //Log.Debug($"OnSkillCompleted Entity:{Entity.Attr.Name} Skill:{skillId} CD:{skillCD} UID:{uid} remove");
                    _skillCDTimers.Remove(skillId);
                    EnqueueSkill(skillId);
                    StartSkill();
                });
                _skillCDTimers.Add(skillId, cdTimer);
            }
            _playingSkill = NO_SKILL;
            StartSkill();
        }

        public void Stop(bool isDispose)
        {
            _skillActor?.Stop();
            _attackActor?.Stop();

            _skillQueue.Clear();
            _playingSkill = NO_SKILL;
            foreach (var item in _skillCDTimers)
            {
                item.Value.Cancel();
            }
            _skillCDTimers.Clear();
            Target = null;
            if (isDispose)
            {
                _skillActor = null;
                _attackActor = null;
            }
        }
        /// <summary>
        /// 技能是否不需要目标
        /// </summary>
        /// <param name="skillId"></param>
        /// <returns></returns>
        private bool IsNoTarget(int skillId)
        {
            var skillTable = SkillTableRepo.Get(skillId);
            if ((ESkillVfxType)skillTable.SkillVfxType is ESkillVfxType.BulletMortar or ESkillVfxType.Trap or ESkillVfxType.AOE or ESkillVfxType.AOENoMove)
                return true;
            return false;
        }

        private ISkillActor GetActor(int skillId)
        {
            //if (isNoTarget)
            //{
            //    return _noTargetActor ??= new NoTargetSkillActor() { Context = Context };
            //}
            if (skillId == 0)
            {
                return _attackActor ??= new AttackActor() { Context = Context };
            }
            return _skillActor ??= new SimpleSkillActor() { Context = Context };
        }
    }
}
