using System;
using System.Threading.Tasks;
using WingsEmu.DTOs.Maps;
using WingsEmu.Game._i18n;
using WingsEmu.Game.Characters;
using WingsEmu.Game.Configurations;
using WingsEmu.Game.Helpers.Damages;
using WingsEmu.Game.Items;
using WingsEmu.Game.Maps;
using WingsEmu.Game.Mates;
using WingsEmu.Game.Mates.Events;
using WingsEmu.Game.Networking;
using WingsEmu.Packets.Enums;
using WingsEmu.Packets.Enums.Battle;

namespace WingsEmu.Game.Extensions.Mates;

public static class MateExtensions
{
    public static int GetModifier(this IMateEntity mate)
    {
        return mate.AttackType switch
        {
            AttackType.Melee => mate.MeleeHpFactor,
            AttackType.Ranged => mate.RangeDodgeFactor,
            AttackType.Magical => mate.MagicMpFactor,
            _ => 0
        };
    }


    public static void TeleportNearCharacter(this IMateEntity mateEntity)
    {
        IClientSession session = mateEntity.Owner?.Session;
        if (session?.CurrentMapInstance == null)
        {
            return;
        }

        mateEntity.ChangePosition(new Position((short)(session.PlayerEntity.PositionX + (mateEntity.MateType == MateType.Partner ? -1 : 1)), (short)(session.PlayerEntity.PositionY + 1)));

        if (mateEntity.MapInstance.HasMapFlag(MapFlags.IS_BASE_MAP))
        {
            mateEntity.MapX = mateEntity.Position.X;
            mateEntity.MapX = mateEntity.Position.Y;
        }

        if (mateEntity.MapInstance.MapInstanceType == MapInstanceType.Miniland && mateEntity.MapInstance.Id == mateEntity.Owner.Miniland.Id)
        {
            mateEntity.MinilandX = mateEntity.Position.X;
            mateEntity.MinilandX = mateEntity.Position.Y;
        }

        bool isBlocked = session.PlayerEntity.MapInstance.IsBlockedZone(mateEntity.PositionX, mateEntity.PositionY);

        if (!isBlocked)
        {
            return;
        }

        mateEntity.ChangePosition(new Position(session.PlayerEntity.PositionX, session.PlayerEntity.PositionY));

        if (mateEntity.MapInstance.MapInstanceType == MapInstanceType.Miniland && mateEntity.MapInstance.Id == mateEntity.Owner.Miniland.Id)
        {
            mateEntity.MinilandX = mateEntity.Position.X;
            mateEntity.MinilandX = mateEntity.Position.Y;
        }

        if (!mateEntity.MapInstance.HasMapFlag(MapFlags.IS_BASE_MAP))
        {
            return;
        }

        mateEntity.MapX = mateEntity.Position.X;
        mateEntity.MapX = mateEntity.Position.Y;
    }

    public static void TeleportToCharacter(this IMateEntity mateEntity)
    {
        IClientSession session = mateEntity.Owner?.Session;
        if (session == null || session.PlayerEntity.IsOnVehicle)
        {
            return;
        }


        mateEntity.ChangePosition(new Position(session.PlayerEntity.PositionX, session.PlayerEntity.PositionY));
        session.BroadcastMateTeleport(mateEntity);
    }

    public static bool IsInCombat(this IMateEntity mateEntity, DateTime date) => mateEntity.LastDefence.AddSeconds(4) > date || mateEntity.LastSkillUse.AddSeconds(2) > date;

    public static bool CanAttack(this IMateEntity mateEntity) => !mateEntity.Owner.IsOnVehicle && mateEntity.Loyalty != 0 && mateEntity.CanPerformAttack();

    public static bool CanMove(this IMateEntity mateEntity)
    {
        if (mateEntity.Loyalty <= 0)
        {
            return false;
        }

        return mateEntity.CanPerformMove();
    }

    public static void RemoveLoyalty(this IMateEntity mateEntity, short loyalty, GameMinMaxConfiguration minMaxConfiguration, IGameLanguageService languageService)
    {
        if (mateEntity.Loyalty < minMaxConfiguration.MinMateLoyalty)
        {
            mateEntity.Loyalty = minMaxConfiguration.MinMateLoyalty;
            return;
        }

        mateEntity.Loyalty -= Math.Abs(loyalty);

        if (mateEntity.Loyalty < minMaxConfiguration.MinMateLoyalty)
        {
            mateEntity.Loyalty = minMaxConfiguration.MinMateLoyalty;
        }

        mateEntity.Owner.Session.SendPetInfo(mateEntity, languageService);
    }

    public static void AddLoyalty(this IMateEntity mateEntity, short loyalty, GameMinMaxConfiguration minMaxConfiguration, IGameLanguageService languageService)
    {
        if (mateEntity.Loyalty == minMaxConfiguration.MaxMateLoyalty)
        {
            return;
        }

        if (mateEntity.Loyalty > minMaxConfiguration.MaxMateLoyalty)
        {
            mateEntity.Loyalty = minMaxConfiguration.MaxMateLoyalty;
            return;
        }

        mateEntity.Loyalty += Math.Abs(loyalty);

        if (mateEntity.Loyalty > minMaxConfiguration.MaxMateLoyalty)
        {
            mateEntity.Loyalty = minMaxConfiguration.MaxMateLoyalty;
        }

        mateEntity.Owner.Session.SendPetInfo(mateEntity, languageService);
    }

    public static bool CanWearItem(this IMateEntity entity, IGameItem gameItem)
    {
        if (gameItem.EquipmentSlot != EquipmentType.Sp && gameItem.EquipmentSlot != EquipmentType.Armor && gameItem.EquipmentSlot != EquipmentType.MainWeapon)
        {
            return true;
        }

        if (gameItem.IsPartnerSpecialist)
        {
            AttackType mateSpecialist = gameItem.PartnerClass switch
            {
                0 => AttackType.Melee,
                1 => AttackType.Ranged,
                2 => AttackType.Magical,
                _ => AttackType.Other
            };

            return mateSpecialist == entity.AttackType;
        }

        AttackType mateClass = gameItem.Class switch
        {
            0 => AttackType.Melee,
            2 => AttackType.Melee,
            4 => AttackType.Ranged,
            8 => AttackType.Magical,
            _ => AttackType.Other
        };

        return mateClass == entity.AttackType;
    }

    public static bool CanWearSpecialist(this IMateEntity entity, IGameItem gameItem) => gameItem.IsPartnerSpecialist && entity.AttackType == (AttackType)gameItem.PartnerClass;

    public static int GetSpCooldown(this IMateEntity mate) => !mate.SpCooldownEnd.HasValue ? 0 : (int)(mate.SpCooldownEnd.Value - DateTime.UtcNow).TotalSeconds;

    public static bool IsSpCooldownElapsed(this IMateEntity mate)
    {
        if (!mate.SpCooldownEnd.HasValue)
        {
            return true;
        }

        return mate.SpCooldownEnd.Value < DateTime.UtcNow;
    }

    public static bool HavePartnerSkill(this IMateEntity mateEntity, byte slot)
    {
        bool skill = slot switch
        {
            0 => mateEntity.Specialist.PartnerSkill1,
            1 => mateEntity.Specialist.PartnerSkill2,
            2 => mateEntity.Specialist.PartnerSkill3,
            _ => false
        };

        return skill;
    }

    public static async Task RemovePartnerSp(this IMateEntity mateEntity)
    {
        if (!mateEntity.IsUsingSp)
        {
            return;
        }

        await mateEntity.Owner.Session.EmitEventAsync(new MateSpUntransformEvent
        {
            MateEntity = mateEntity
        });
    }

    public static void RefreshPartnerSkills(this IMateEntity mateEntity)
    {
        if (mateEntity.MateType != MateType.Partner)
        {
            return;
        }

        if (mateEntity.MonsterSkills?.Count == 0)
        {
            return;
        }

        mateEntity.Owner.Session.RefreshSkillList();
    }

    public static bool SkillRankS(this IMateEntity mateEntity, byte slot) => mateEntity.Specialist?.PartnerSkills?.Find(s => s.Rank == 7 && s.Slot == slot) != null;

    public static byte GetFreeMateSlot(this IPlayerEntity player, bool isPartner)
    {
        byte slot;
        byte maxCount = isPartner ? player.MaxPartnerCount : player.MaxPetCount;
        for (slot = 0; slot < maxCount; slot++)
        {
            IMateEntity getMate = isPartner
                ? player.MateComponent.GetMate(m => m.PetSlot == slot && m.MateType == MateType.Partner)
                : player.MateComponent.GetMate(m => m.PetSlot == slot && m.MateType == MateType.Pet);

            if (getMate != null)
            {
                continue;
            }

            break;
        }

        return slot;
    }

    public static void RefreshEquipmentValues(this IMateEntity mateEntity, GameItemInstance gameItemInstance, bool clearValues)
    {
        if (clearValues)
        {
            mateEntity.BCardComponent.ClearEquipmentBCards(gameItemInstance.GameItem.EquipmentSlot);
            return;
        }

        mateEntity.BCardComponent.AddEquipmentBCards(gameItemInstance.GameItem.EquipmentSlot, gameItemInstance.GameItem.BCards);
    }
}