using Games.Battles.BattleEntities;
using Games.Battles.BattleEntities.BattleUnits;
using Games.Battles.Managers.BattleUnitHandlerActionData;
using Games.Battles.Managers;
using Games.Battles.Signals;
using Sirenix.OdinInspector;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Zenject;
using Games.Battles.Calculations;
using MEC;
using System.Linq;
using Views.Games.Battles.Positioning;
using Unity.Burst;

namespace Games.Battles.PredictedCombat
{ 
    public class PredictCombat : MonoBehaviour
    {
        private SignalBus _signalBus;
        private DamageCalculator _damageCalculator;

        private PredictCombatGrid _grid;
        private Vector2Int _size = new Vector2Int(12, 8);

        private List<BattleEntity> _battleEntities = new();

        [Inject]
        public void Construct(SignalBus signalBus, DamageCalculator damageCalculator)
        {
            _signalBus = signalBus;
            _damageCalculator = damageCalculator;
        }

        public void OnCombatUnitCreate(CombatUnitCreateSignal combatUnitCreateSignal)
        {
            _battleEntities.Add(combatUnitCreateSignal.BattleEntity);
        }

        [Button(name: "Predict Combat")]
        public void StartPrediction()
        {
            _grid = new(_size);

            foreach (var entity in _battleEntities)
            {
                _grid.Hexes[entity.PathNavigator.HexNode.HexCoordinates].entity = entity;
            }

            Timing.RunCoroutine(EvaluateCombat());
        }

        private IEnumerator<float> EvaluateCombat()
        {
            BattleTeamType turn = BattleTeamType.Attacker;

            Queue<BattleEntity> attackerQueue = new();
            Queue<BattleEntity> deffenderQueue = new();  

            foreach (var entity in _battleEntities)
            {
                if(entity.BattleTeamType == BattleTeamType.Attacker)
                {
                    attackerQueue.Enqueue(entity);
                }
                else
                {
                    deffenderQueue.Enqueue(entity);
                }
            }

            BattleEntity currentUnit = null;

            while (!CombatEnded())
            {
                if(turn == BattleTeamType.Attacker)
                {
                    foreach (var entity in attackerQueue)
                    {
                        if(entity.IsAlive)
                        {
                            currentUnit = attackerQueue.Dequeue();
                            attackerQueue.Enqueue(currentUnit);
                        }
                        else
                        {
                            attackerQueue.Dequeue();
                        }
                    }
                }
                else
                {
                    foreach (var entity in deffenderQueue)
                    {
                        if (entity.IsAlive)
                        {
                            currentUnit = deffenderQueue.Dequeue();
                            deffenderQueue.Enqueue(currentUnit);
                        }
                        else
                        {
                            deffenderQueue.Dequeue();
                        }
                    }
                }

                var enemiesInRange = GetEnemiesInRange(currentUnit);
                if(enemiesInRange.Count > 0)
                {
                    enemiesInRange.OrderBy(x => x.statistics.Health);
                    HandleCombat(currentUnit, enemiesInRange[0]);
                }
                else
                {
                    var walkableNodes = _grid.GetWalkableNodes(_grid.GetNodeByEntity(currentUnit), currentUnit);
                    var enemies = GetEnemies(currentUnit);

                    PredictCombatHexNode origin = _grid.GetNodeByEntity(currentUnit);

                    Dictionary<int, PredictCombatHexNode> nodesByDistance = new();

                    foreach (var node in walkableNodes)
                    {
                        int weight = 0;

                        foreach (var enemy in enemies)
                        {
                            int currentDistance = origin.GetDistance(_grid.GetNodeByEntity(enemy));
                            int distanceAfterMove = node.GetDistance(_grid.GetNodeByEntity(enemy));

                            weight += currentDistance - distanceAfterMove;
                            
                        }
                        nodesByDistance.Add(weight, node);
                    }

                    var sortedNodes = new SortedDictionary<int, PredictCombatHexNode>(nodesByDistance);

                    _grid.MoveUnit(origin, sortedNodes.ElementAt(0).Value);
                }

                turn = turn == BattleTeamType.Attacker ? BattleTeamType.Defender : BattleTeamType.Attacker;
            }

            yield return 0;
        }


        private void HandleCombat(BattleEntity attacker, BattleEntity defender)
        {
            AttackActionData attackActionData = new(defender.PathNavigator.HexNode, attacker.PathNavigator.HexNode);
            CombatContext combatContext = new(attacker, BattleUnitCombatManager.GetAttackTarget(attackActionData, attacker), attackActionData.AttackFrom, false);

            var attackResult = _damageCalculator.GetAttackResult(
                combatContext.Attacker,
                combatContext.Target,
                combatContext.BattleUnitWeapon);

            defender.statistics.TakeDamage(attackResult.DamageDone);
            defender.SetCount(defender.statistics.GetUnitsCount());

            if (!defender.IsAlive)
            {
                _grid.Hexes[_grid.GetNodeByEntity(defender).hexCoordintes].entity = null;
                _battleEntities.RemoveAt(_battleEntities.IndexOf(defender));
            }
        }

        private List<BattleEntity> GetEnemies(BattleEntity origin)
        {
            return _battleEntities.Where(x => x.BattleTeamType != origin.BattleTeamType).ToList();
        }

        private List<BattleEntity> GetEnemiesInRange(BattleEntity origin)
        {
            var enemies = GetEnemies(origin);
            List<BattleEntity> enemiesInRange = new();

            PredictCombatHexNode originNode = _grid.GetNodeByEntity(origin);

            foreach (var enemy in enemies)
            {
                if(originNode.GetDistance(_grid.GetNodeByEntity(enemy)) < origin.GetWeapon(enemy).range)
                {
                    enemiesInRange.Add(enemy);
                }
            }

            return enemiesInRange;
        }

        private bool CombatEnded()
        {
            if (_battleEntities.Where(x => x.BattleTeamType == BattleTeamType.Attacker && x.IsAlive).Count() < 1)
            {
                Debug.Log($"<color=green><b>Defender won!</b></color>");
                return true;
            }
            else if (_battleEntities.Where(x => x.BattleTeamType == BattleTeamType.Defender && x.IsAlive).Count() < 1)
            {
                Debug.Log($"<color=green><b>Attacker won!</b></color>");
                return true;
            }

            return false;
        }
    }
}