using System;
using ModernUO.Serialization;
using Server.Collections;
using Server.ContextMenus;
using Server.Engines.ConPVP;
using Server.Mobiles;
using Server.Regions;
using Server.Spells;
using Server.Spells.Ninjitsu;
using Server.Targeting;

namespace Server.Items;

[SerializationGenerator(2)]
public partial class BallOfSummoning : Item, TranslocationItem
{
    [SerializableField(3, setter: "private")]
    [SerializedCommandProperty(AccessLevel.GameMaster)]
    private string _petName;

    [Constructible]
    public BallOfSummoning() : base(0xE2E)
    {
        Light = LightType.Circle150;

        _charges = Utility.RandomMinMax(3, 9);
        _petName = null;
    }

    public override double DefaultWeight => 10.0;

    [SerializableProperty(0)]
    [CommandProperty(AccessLevel.GameMaster)]
    public int Recharges
    {
        get => _recharges;
        set
        {
            _recharges = Math.Clamp(value, 0, MaxRecharges);
            InvalidateProperties();
            this.MarkDirty();
        }
    }

    [SerializableProperty(1)]
    [CommandProperty(AccessLevel.GameMaster)]
    public int Charges
    {
        get => _charges;
        set
        {
            _charges = Math.Clamp(value, 0, MaxCharges);
            InvalidateProperties();
            this.MarkDirty();
        }
    }

    [SerializableProperty(2)]
    [CommandProperty(AccessLevel.GameMaster)]
    public BaseCreature Pet
    {
        get
        {
            if (_pet?.Deleted == true)
            {
                _pet = null;
                InternalUpdatePetName();
            }

            return _pet;
        }
        set
        {
            _pet = value;
            InternalUpdatePetName();
            this.MarkDirty();
        }
    }

    [CommandProperty(AccessLevel.GameMaster)]
    public int MaxCharges => 20;

    [CommandProperty(AccessLevel.GameMaster)]
    public int MaxRecharges => 255;

    private static TextDefinition _translocationItemName = 1054129; // crystal ball of pet summoning
    public TextDefinition TranslocationItemName => _translocationItemName;

    public override void AddNameProperty(IPropertyList list)
    {
        // a crystal ball of pet summoning: [charges: ~1_charges~] : [linked pet: ~2_petName~]
        list.Add(1054131, $"{_charges}\t{_petName.DefaultIfNullOrEmpty(" ")}");
    }

    public override void OnSingleClick(Mobile from)
    {
        // a crystal ball of pet summoning: [charges: ~1_charges~] : [linked pet: ~2_petName~]
        LabelTo(from, 1054131, $"{_charges}\t{_petName.DefaultIfNullOrEmpty(" ")}");
    }

    public override void GetContextMenuEntries(Mobile from, ref PooledRefList<ContextMenuEntry> list)
    {
        base.GetContextMenuEntries(from, ref list);

        if (!from.Alive || RootParent != from)
        {
            return;
        }

        if (Pet == null)
        {
            list.Add(new BallEntry(LinkPet, 6180));
        }
        else
        {
            list.Add(new BallEntry(CastSummonPet, 6181));
            list.Add(new BallEntry(UpdatePetName, 6183));
            list.Add(new BallEntry(UnlinkPet, 6182));
        }
    }

    public override void OnDoubleClick(Mobile from)
    {
        // TODO: Previous implementation allowed use on ground, without house protection checks. What is the correct behavior?
        if (RootParent != from)
        {
            // That must be in your pack for you to use it.
            from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1042001);
            return;
        }

        var animalContext = AnimalForm.GetContext(from);

        if (Core.ML && animalContext != null)
        {
            // You cannot use a Crystal Ball of Pet Summoning while in animal form.
            from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1080073);
            return;
        }

        if (Pet == null)
        {
            LinkPet(from);
        }
        else
        {
            CastSummonPet(from);
        }
    }

    public void LinkPet(Mobile from)
    {
        var pet = Pet;

        if (Deleted || pet != null || RootParent != from)
        {
            return;
        }

        // Target your pet that you wish to link to this Crystal Ball of Pet Summoning.
        from.SendLocalizedMessage(1054114);
        from.Target = new PetLinkTarget(this);
    }

    public void CastSummonPet(Mobile from)
    {
        var pet = Pet;

        if (Deleted || pet == null || RootParent != from || from is not PlayerMobile pm)
        {
            return;
        }

        if (Charges == 0)
        {
            // The Crystal Ball darkens. It must be charged before it can be used again.
            SendLocalizedMessageTo(from, 1054122);
        }
        else if (pet is BaseMount mount && mount.Rider == from)
        {
            // The Crystal Ball fills with a yellow mist. Why would you summon your pet while riding it?
            this.SendLocalizedMessageTo(from, 1054124, 0x36);
        }
        else if (pet.Map == Map.Internal && (!pet.IsStabled || from.Followers + pet.ControlSlots > from.FollowersMax))
        {
            // The Crystal Ball fills with a blue mist. Your pet is not responding to the summons.
            this.SendLocalizedMessageTo(from, 1054125, 0x5);
        }
        else if ((!pet.Controlled || pet.ControlMaster != from) && pm.Stabled?.Contains(pet) != true)
        {
            // The Crystal Ball fills with a grey mist. You are not the owner of the pet you are attempting to summon.
            this.SendLocalizedMessageTo(from, 1054126, 0x8FD);
        }
        else if (!pet.IsBonded)
        {
            // The Crystal Ball fills with a red mist. You appear to have let your bond to your pet deteriorate.
            this.SendLocalizedMessageTo(from, 1054127, 0x22);
        }
        else if (from.Map == Map.Ilshenar || from.Region.IsPartOf<DungeonRegion>() ||
                 from.Region.IsPartOf<JailRegion>() || from.Region.IsPartOf<SafeZone>())
        {
            // You cannot summon your pet to this location.
            this.SendLocalizedMessageTo(from, 1080049, 0x22);
        }
        else if (Core.ML && from is PlayerMobile mobile && Core.Now < mobile.LastPetBallTime.AddSeconds(15.0))
        {
            // You must wait a few seconds before you can summon your pet.
            this.SendLocalizedMessageTo(mobile, 1080072, 0x22);
        }
        else if (Core.ML)
        {
            new PetSummoningSpell(this, from).Cast();
        }
        else
        {
            SummonPet(from);
        }
    }

    public void SummonPet(Mobile from)
    {
        var pet = Pet;

        if (pet == null || from is not PlayerMobile pm)
        {
            return;
        }

        Charges--;

        if (pet.IsStabled)
        {
            pet.SetControlMaster(from);

            if (pet.Summoned)
            {
                pet.SummonMaster = from;
            }

            pet.ControlTarget = from;
            pet.ControlOrder = OrderType.Follow;

            pet.IsStabled = false;
            pet.StabledBy = null;
            pm.RemoveStabled(pet);
            pm.AutoStabled?.Remove(pet);
        }

        pet.MoveToWorld(from.Location, from.Map);

        // The Crystal Ball fills with a green mist. Your pet has been summoned.
        this.SendLocalizedMessageTo(from, 1054128, 0x43);

        if (from is PlayerMobile playerMobile)
        {
            playerMobile.LastPetBallTime = Core.Now;
        }
    }

    public void UnlinkPet(Mobile from)
    {
        if (!Deleted && Pet != null && RootParent == from)
        {
            Pet = null;

            SendLocalizedMessageTo(from, 1054120); // This crystal ball is no longer linked to a pet.
        }
    }

    public void UpdatePetName(Mobile from)
    {
        InternalUpdatePetName();
    }

    private void InternalUpdatePetName()
    {
        PetName = Pet?.Name ?? "";
        InvalidateProperties();
    }

    private void Deserialize(IGenericReader reader, int version)
    {
        _recharges = reader.ReadEncodedInt();
        _charges = reader.ReadEncodedInt();
        _pet = (BaseCreature)reader.ReadEntity<Mobile>();
        _petName = reader.ReadString();
    }

    [AfterDeserialization(false)]
    private void AfterDeserialization()
    {
        InternalUpdatePetName();
    }

    private delegate void BallCallback(Mobile from);

    private class BallEntry : ContextMenuEntry
    {
        private readonly BallCallback _callback;

        public BallEntry(BallCallback callback, int number) : base(number, 2) => _callback = callback;

        public override void OnClick(Mobile from, IEntity target)
        {
            if (from.CheckAlive())
            {
                _callback(from);
            }
        }
    }

    private class PetLinkTarget : Target
    {
        private readonly BallOfSummoning _ball;

        public PetLinkTarget(BallOfSummoning ball) : base(-1, false, TargetFlags.None) => _ball = ball;

        protected override void OnTarget(Mobile from, object targeted)
        {
            if (_ball.Deleted || _ball.Pet != null)
            {
                return;
            }

            if (_ball.RootParent != from)
            {
                // That must be in your pack for you to use it.
                from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1042001);
                return;
            }

            if (targeted is BaseCreature creature)
            {
                if (!creature.Controlled || creature.ControlMaster != from)
                {
                    // You may only link your own pets to a Crystal Ball of Pet Summoning.
                    _ball.SendLocalizedMessageTo(from, 1054117, 0x59);
                }
                else if (!creature.IsBonded)
                {
                    // You must bond with your pet before it can be linked to a Crystal Ball of Pet Summoning.
                    _ball.SendLocalizedMessageTo(from, 1054118, 0x59);
                }
                else
                {
                    // Your pet is now linked to this Crystal Ball of Pet Summoning.
                    _ball.SendLocalizedMessageTo(from, 1054119, 0x59);
                    _ball.Pet = creature;
                }
            }
            else if (targeted == _ball)
            {
                // The Crystal Ball of Pet Summoning cannot summon itself.
                _ball.SendLocalizedMessageTo(from, 1054115, 0x59);
            }
            else
            {
                // Only pets can be linked to this Crystal Ball of Pet Summoning.
                _ball.SendLocalizedMessageTo(from, 1054116, 0x59);
            }
        }
    }

    private class PetSummoningSpell : Spell
    {
        private static readonly SpellInfo _info = new("Ball Of Summoning", "", 230);

        private readonly BallOfSummoning _ball;
        private readonly Mobile _caster;

        private bool _stop;

        public PetSummoningSpell(BallOfSummoning ball, Mobile caster) : base(caster, null, _info)
        {
            _caster = caster;
            _ball = ball;
        }

        public override bool ClearHandsOnCast => false;
        public override bool RevealOnCast => true;

        public override double CastDelayFastScalar => 0;

        public override TimeSpan CastDelayBase => TimeSpan.FromSeconds(2.0);

        public override TimeSpan GetCastRecovery() => TimeSpan.Zero;

        public override int GetMana() => 0;

        public override bool ConsumeReagents() => true;

        public override bool CheckFizzle() => true;

        public void Stop()
        {
            _stop = true;
            Disturb(DisturbType.Hurt, false);
        }

        public override bool CheckDisturb(DisturbType type, bool checkFirst, bool resistable) =>
            type != DisturbType.EquipRequest && type != DisturbType.UseRequest;

        public override void DoHurtFizzle()
        {
            if (!_stop)
            {
                base.DoHurtFizzle();
            }
        }

        public override void DoFizzle()
        {
            if (!_stop)
            {
                base.DoFizzle();
            }
        }

        public override void OnDisturb(DisturbType type, bool message)
        {
            if (message && !_stop)
            {
                Caster.SendLocalizedMessage(1080074); // You have been disrupted while attempting to summon your pet!
            }
        }

        public override void OnCast()
        {
            _ball.SummonPet(_caster);

            FinishSequence();
        }
    }
}
