using FL.Battle.Skills;
using System;
using System.Collections.Generic;
using UnityEngine;
using XGame;
using XGame.Database;
using XGame.Framework;
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 Dictionary<int, ISkillActor> _usingActors;

        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 List<int> _skillQueue = new List<int>();
        private Dictionary<int, long> _buffSkillInfos;
        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.AddState(EEntityState.Battle);
            Target = target;
            StartSkill();
        }
        public void Prepare()
        {
            //初始化技能
            foreach (var skillId in Context.Entity.ActiveSkillIds)
            {
                EnqueueSkill(skillId);
            }
            //选择目标
            Context.FindTargetAsync(OnSelectedTarget);
        }

        #endregion
        public bool FindSkillByElementType(EElementType elementType, out int skillId)
        {
            foreach (var item in Context.Entity.ActiveSkillIds)
            {
                var table = SkillTableRepo.Get(item);
                if (table.Element == elementType)
                {
                    skillId = item;
                    return true;
                }
            }
            skillId = 0;
            return false;
        }
        /// <summary>
        /// 返回技能的最小剩余时间
        /// </summary>
        /// <returns></returns>
        public int GetMinCooldown()
        {
            if (_skillCDTimers.Count == 0)
                return 0;
            var remain = int.MaxValue;
            foreach (var skillId in Context.Entity.ActiveSkillIds)
            { // 有主动技能在队列里
                if (_skillQueue.Contains(skillId))
                    return 0;
                if (_skillCDTimers.TryGetValue(skillId, out var timer))
                {
                    var temp = (timer as Timer).RemainTime;
                    if (temp < remain)
                        remain = temp;
                }
            }
            return remain;
        }
        /// <summary>
        /// 冷却时间到的技能进入队列等待播放
        /// </summary>
        /// <param name="skillId"></param>
        private bool EnqueueSkill(int skillId)
        {
            if (Context.Entity.IsDead)
                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.Add(skillId);
            return true;
        }
        private void StartSkill()
        {
            if (_playingSkill != -1)
                return;
            // 没有技能 || 已死亡 || 移动中
            if (_skillQueue.Count == 0 || (Context.Entity.IsState(EEntityState.SkillBroken | EEntityState.Moving)))
                return;
            var skillId = _skillQueue[0];
            var isNoTarget = IsNoTarget(skillId);
            // 召唤类技能，不需要选目标
            if (isNoTarget == false && (Target == null || Target.IsDead))
            { // 没有目标或目标已死亡
                Context.FindTargetAsync(OnSelectedTarget);
                return;
            }
            _skillQueue.RemoveAt(0);
            _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)
        {
            RemoveActor(skillId);
            //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);
                    if (RemoveTickSkill(skillId) == false)
                    { // 非触发类技能，加入技能队列
                        EnqueueSkill(skillId);
                    }
                    StartSkill();
                });
                _skillCDTimers.Add(skillId, cdTimer);
            }
            _playingSkill = NO_SKILL;
            StartSkill();
        }

        public void Stop(bool isDispose)
        {
            if (_usingActors != null)
            {
                foreach (var actor in _usingActors.Values)
                {
                    ObjectPool.Recycle(actor);
                }
                _usingActors.Clear();
            }
            _buffSkillInfos?.Clear();
            _skillQueue.Clear();
            _playingSkill = NO_SKILL;
            foreach (var item in _skillCDTimers)
            {
                item.Value.Cancel();
            }
            _skillCDTimers.Clear();
            Target = 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 (skillId == 0)
            //{
            //    return _attackActor ??= new AttackActor() { Context = Context };
            //}
            //return _skillActor ??= new SimpleSkillActor() { Context = Context };
            if (_usingActors == null)
            {
                _usingActors = new Dictionary<int, ISkillActor>();
            }
            var actor = ObjectPool.Acquire<SimpleSkillActor>();
            actor.Context = Context;
            _usingActors.Add(skillId, actor);
            return actor;
        }
        private void RemoveActor(int skillId)
        {
            if (_usingActors?.TryGetValue(skillId, out var actor) ?? false)
            {
                _usingActors.Remove(skillId);
                ObjectPool.Recycle(actor);
            }
        }
        #region 触发类技能
        public void TryTickSkill(int skillId, long targetId, int probability = 10000)
        {
            if (_buffSkillInfos == null)
                _buffSkillInfos = new Dictionary<int, long>();
            if (_buffSkillInfos.ContainsKey(skillId))
            { // 已经触发 或 还在cd中
                return;
            }
            if (MathUtils.Random(probability) == false)
            {
                return;
            }
            // 触发技能
            var skillVfxs = SkillVfxsTableRepo.Get(skillId);
            if (skillVfxs.AniName >= EAnimationName.attack)
            { // 有动作，昏迷等状态不触发
                if (Context.Entity.IsState(EEntityState.SkillBroken))
                {
                    return;
                }
                // 加到技能播放队列
                if (EnqueueSkill(skillId))
                {
                    _buffSkillInfos.Add(skillId, targetId);
                }
            }
            else
            { // 无动作，直接播放
                var target = Context.Selector.GetTarget(targetId);
                var isNoTarget = IsNoTarget(skillId);
                // 召唤类技能，不需要选目标
                if (isNoTarget == false && (target == null || target.IsDead))
                { // 没有目标或目标已死亡
                    return;
                }
                Log.Debug($"触发技能 SkillId:{skillId} entity:{Context.Entity.EntityId} targetId:{targetId}");
                _buffSkillInfos.Add(skillId, targetId);
                GetActor(skillId).Play(skillId, isNoTarget ? null : target, OnTickSkillCompleted);
            }
        }
        private void OnTickSkillCompleted(int skillId, int skillCD)
        {
            RemoveActor(skillId);
            //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);
                    RemoveTickSkill(skillId);
                });
                _skillCDTimers.Add(skillId, cdTimer);
            }
        }
        /// <summary>
        /// 移除触发类技能
        /// </summary>
        /// <param name="skillId"></param>
        /// <returns></returns>
        private bool RemoveTickSkill(int skillId)
        {
            return _buffSkillInfos?.Remove(skillId) ?? false;
        }
        #endregion

        #region 被动技能
        //public void TryTickPassiveSkills(Func<SkillTable, bool> extenCondition)
        //{
        //    if (Context.Entity.PassiveSkillIds == null) return;
        //    foreach(var skillId in  Context.Entity.PassiveSkillIds)
        //    {
        //        var skill = SkillTableRepo.Get(skillId);
        //        if (skill == null) continue;
        //        if (!extenCondition(skill)) continue;
        //        TryTickSkill(skillId, Context.Entity.EntityId);
        //    }
        //}
        #endregion
        #region IPauseable 实现
        public void Pause(EEntityState state)
        {
            if (state is EEntityState.Palsy)
            {
                foreach (var item in _skillCDTimers.Values)
                { //TODO 冷却中的技能cd回退到80%
                    var timer = item as Timer;
                    timer.ResetForAxis(Mathf.RoundToInt(timer.TimeAxis * KeyValue.ParalysisDelayFac.ToRealFloat()));
                }
            }
            if (_playingSkill != NO_SKILL)
            { // 有技能正在施法，直接进入cd
                var skillId = _playingSkill;
                var skill = SkillTableRepo.Get(skillId);
                OnSkillCompleted(skillId, skill.RestTime);
            }
            if (_buffSkillInfos != null)
            { // 移除队列里的触发类技能
                for (var i = _skillQueue.Count - 1; i >= 0; i--)
                {
                    var skillId = _skillQueue[i];
                    if (_buffSkillInfos.ContainsKey(skillId))
                    {
                        _buffSkillInfos.Remove(skillId);
                        _skillQueue.RemoveAt(i);
                    }
                }
            }
        }

        public void Resume()
        {
            StartSkill();
        }
        #endregion
    }
}
