using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using WingsAPI.Game.Extensions.Quicklist;
using WingsEmu.DTOs.Skills;
using WingsEmu.Game._i18n;
using WingsEmu.Game.Battle;
using WingsEmu.Game.Buffs;
using WingsEmu.Game.Characters;
using WingsEmu.Game.Characters.Events;
using WingsEmu.Game.Configurations;
using WingsEmu.Game.Extensions;
using WingsEmu.Game.Helpers.Damages;
using WingsEmu.Game.Managers.StaticData;
using WingsEmu.Game.Maps;
using WingsEmu.Game.Networking;
using WingsEmu.Game.Skills;
using WingsEmu.Packets.ClientPackets;
using WingsEmu.Packets.Enums;
using WingsEmu.Packets.Enums.Battle;
using WingsEmu.Packets.Enums.Chat;

namespace WingsEmu.Plugins.PacketHandling.Game.Battle;

public class UseAtSkillPacketHandler : GenericGamePacketHandlerBase<UseAtSkillPacket>
{
    private readonly ICardsManager _cardsManager;
    private readonly IGameLanguageService _gameLanguage;
    private readonly RainbowBattleConfiguration _rainbowBattleConfiguration;
    private readonly ISkillsManager _skillsManager;

    public UseAtSkillPacketHandler(IGameLanguageService gameLanguage, ICardsManager cardsManager, ISkillsManager skillsManager, RainbowBattleConfiguration rainbowBattleConfiguration)
    {
        _gameLanguage = gameLanguage;
        _cardsManager = cardsManager;
        _skillsManager = skillsManager;
        _rainbowBattleConfiguration = rainbowBattleConfiguration;
    }

    protected override async Task HandlePacketAsync(IClientSession session, UseAtSkillPacket packet)
    {
        IPlayerEntity character = session.PlayerEntity;

        if (!character.CanFight() || packet == null)
        {
            session.SendCancelPacket(CancelType.NotInCombatMode);
            session.SendDebugMessage("[U_AS] !canFight, packet null");
            return;
        }

        if (session.CurrentMapInstance.IsPvp && session.CurrentMapInstance.PvpZone(session.PlayerEntity.PositionX, session.PlayerEntity.PositionY) &&
            session.CurrentMapInstance.PvpZone(packet.MapX, packet.MapY))
        {
            session.SendCancelPacket(CancelType.NotInCombatMode);
            session.SendDebugMessage("[U_AS] Character no-PvP zone / !map.IsPvp");
            return;
        }

        if (session.CurrentMapInstance.IsBlockedZone(packet.MapX, packet.MapY))
        {
            session.SendDebugMessage("[U_AS] character.IsBlockedZone()");
            session.SendCancelPacket(CancelType.NotInCombatMode);
            return;
        }

        if (session.IsMuted())
        {
            session.SendMuteMessage();
            session.SendCancelPacket(CancelType.NotInCombatMode);
            return;
        }

        if (session.PlayerEntity.IsOnVehicle || session.PlayerEntity.CheatComponent.IsInvisible)
        {
            session.SendDebugMessage("[U_AS] character.IsVehicled, InvisibleGm");
            session.SendCancelPacket(CancelType.NotInCombatMode);
            return;
        }

        if (!session.PlayerEntity.CanPerformAttack())
        {
            session.SendDebugMessage("[U_AS] !CanPerformAttack");
            session.SendCancelPacket(CancelType.NotInCombatMode);
            return;
        }

        if ((DateTime.UtcNow - session.PlayerEntity.LastTransform).TotalSeconds < 3)
        {
            session.SendDebugMessage("[U_AS] Under transformation cooldown");
            session.SendCancelPacket(CancelType.NotInCombatMode);
            session.SendMsg(_gameLanguage.GetLanguage(GameDialogKey.SPECIALIST_SHOUTMESSAGE_CANT_ATTACK_YET, session.UserLanguage), MsgMessageType.Middle);
            return;
        }

        if (character.IsCastingSkill)
        {
            session.SendCancelPacket(CancelType.NotInCombatMode);
            session.SendDebugMessage("[U_AS] Already using a skill");
            return;
        }

        List<IBattleEntitySkill> skills = session.PlayerEntity.Skills;
        var characterSkill = skills.FirstOrDefault(s => s.Skill?.CastId == packet.CastId &&
            (s.Skill?.UpgradeSkill == 0 || s.Skill?.SkillType == SkillType.NormalPlayerSkill) && s.Skill.TargetType == TargetType.NonTarget) as CharacterSkill;
        SkillDTO skill = characterSkill?.Skill;

        if (skill == null)
        {
            session.SendDebugMessage("[U_AS] Skill does not exist");
            session.SendCancelPacket(CancelType.NotInCombatMode);
            return;
        }

        SkillInfo skillInfo = session.PlayerEntity.GetUpgradedSkill(skill, _cardsManager, _skillsManager) ?? skill.GetInfo();

        bool canBeUsed = session.PlayerEntity.CharacterCanCastOrCancel(characterSkill, _gameLanguage, skillInfo, false);
        if (!canBeUsed)
        {
            session.SendDebugMessage("[U_AS] !skill.canBeUsed");
            return;
        }

        if (!session.PlayerEntity.CanPerformAttack(skillInfo))
        {
            session.SendCancelPacket(CancelType.NotInCombatMode);
            return;
        }

        if (session.PlayerEntity.IsSitting)
        {
            await session.RestAsync();
        }

        var position = new Position(packet.MapX, packet.MapY);

        if (!session.PlayerEntity.Position.IsInRange(position, skillInfo.Range + 3))
        {
            session.SendDebugMessage("[U_AS] !character.IsInRange");
            session.SendCancelPacket(CancelType.NotInCombatMode);
            return;
        }

        if (session.PlayerEntity.RainbowBattleComponent.IsFrozen)
        {
            session.SendCancelPacket(CancelType.NotInCombatMode);
            return;
        }

        if (!session.PlayerEntity.CheatComponent.HasGodMode)
        {
            skillInfo.ManaCost = session.PlayerEntity.BCardComponent.HasBCard(BCardType.TimeCircleSkills, (byte)AdditionalTypes.TimeCircleSkills.DisableMPConsumption) ? 0 : skillInfo.ManaCost;
            if (session.PlayerEntity.AdditionalMp > 0)
            {
                int removedAdditionalMp;
                if (session.PlayerEntity.AdditionalMp > skillInfo.ManaCost)
                {
                    removedAdditionalMp = skillInfo.ManaCost;
                }
                else
                {
                    removedAdditionalMp = session.PlayerEntity.AdditionalMp;

                    int overflow = Math.Abs(session.PlayerEntity.AdditionalMp - skillInfo.ManaCost);
                    session.PlayerEntity.Mp -= overflow;
                }

                await session.EmitEventAsync(new RemoveAdditionalHpMpEvent
                {
                    Mp = removedAdditionalMp
                });
            }
            else
            {
                session.PlayerEntity.RemoveEntityMp((short)skillInfo.ManaCost, skill);
            }

            (int firstDataNegative, int _) = session.PlayerEntity.BCardComponent.GetAllBCardsInformation(BCardType.HealingBurningAndCasting,
                (byte)AdditionalTypes.HealingBurningAndCasting.HPDecreasedByConsumingMP, session.PlayerEntity.Level);

            (int firstDataPositive, int _) = session.PlayerEntity.BCardComponent.GetAllBCardsInformation(BCardType.HealingBurningAndCasting,
                (byte)AdditionalTypes.HealingBurningAndCasting.HPIncreasedByConsumingMP, session.PlayerEntity.Level);

            int hpRemoved = (int)(firstDataPositive / 100.0 * skillInfo.ManaCost - firstDataNegative / 100.0 * skillInfo.ManaCost);

            if (hpRemoved > 0)
            {
                await session.PlayerEntity.EmitEventAsync(new BattleEntityHealEvent
                {
                    Entity = session.PlayerEntity,
                    HpHeal = hpRemoved
                });
            }
            else
            {
                if (session.PlayerEntity.Hp - hpRemoved <= 0)
                {
                    session.PlayerEntity.BroadcastDamage(session.PlayerEntity.Hp - 1);
                    session.PlayerEntity.Hp = 1;
                }
                else
                {
                    session.PlayerEntity.BroadcastDamage(hpRemoved);
                    session.PlayerEntity.Hp -= hpRemoved;
                }
            }

            session.RefreshStat();
        }

        session.SendDebugMessage($"Hit {skill.HitType} / Target {skill.TargetType} / Attack Type {skill.AttackType} / Affected entities {skill.TargetAffectedEntities}");
        session.SendCancelPacket(CancelType.NotInCombatMode);

        session.PlayerEntity.CleanComboState();
        session.SendMsCPacket(0);
        session.RefreshQuicklist();
        session.RefreshStat();
        session.PlayerEntity.ClearFoodBuffer();

        if (session.PlayerEntity.RainbowBattleComponent.IsInRainbowBattle)
        {
            session.PlayerEntity.RainbowBattleComponent.ActivityPoints += _rainbowBattleConfiguration.UsingSkillActivityPoints;
        }

        character.WeaponLoaded(characterSkill, _gameLanguage, true);
        character.LastSkillUse = DateTime.UtcNow;
        DateTime castTime = character.GenerateSkillCastTime(skillInfo);
        character.SkillComponent.CanBeInterrupted = character.CanBeInterrupted(skillInfo);

        await character.EmitEventAsync(new BattleExecuteSkillEvent(character, null, skillInfo, castTime, position));
    }
}