using System;
using Server.Mobiles;

namespace Server.Engines.Virtues;

public class HonorContext
{
    private readonly Point3D _initialLocation;
    private readonly Map _initialMap;

    private readonly InternalTimer _timer;
    private FirstHit _firstHit;
    private double _honorDamage;
    private bool _poisoned;
    private int _totalDamage;

    public HonorContext(PlayerMobile source, Mobile target)
    {
        Source = source;
        Target = target;

        _firstHit = FirstHit.NotDelivered;
        _poisoned = false;

        _initialLocation = source.Location;
        _initialMap = source.Map;

        source.SentHonorContext = this;
        ((IHonorTarget)target).ReceivedHonorContext = this;

        _timer = new InternalTimer(this);
        _timer.Start();
        source._honorTime = Core.Now + TimeSpan.FromMinutes(40);

        Timer.DelayCall(
            TimeSpan.FromMinutes(40),
            (m, c) =>
            {
                if (m._honorTime < Core.Now && m.SentHonorContext != null)
                {
                    c.Cancel();
                }
            },
            source,
            this
        );
    }

    public PlayerMobile Source { get; }

    public Mobile Target { get; }

    public int PerfectionDamageBonus { get; private set; }

    public int PerfectionLuckBonus => PerfectionDamageBonus * PerfectionDamageBonus / 10;

    public void OnSourceDamaged(Mobile from, int amount)
    {
        if (from != Target)
        {
            return;
        }

        if (_firstHit == FirstHit.NotDelivered)
        {
            _firstHit = FirstHit.Granted;
        }
    }

    public void OnTargetPoisoned()
    {
        _poisoned = true; // Set this flag for OnTargetDamaged which will be called next
    }

    public void OnTargetDamaged(Mobile from, int amount)
    {
        if (_firstHit == FirstHit.NotDelivered)
        {
            _firstHit = FirstHit.Delivered;
        }

        if (_poisoned)
        {
            _honorDamage += amount * 0.8;
            _poisoned = false; // Reset the flag

            return;
        }

        _totalDamage += amount;

        if (from == Source)
        {
            if (Target.CanSee(Source) && Target.InLOS(Source) &&
                (Source.InRange(Target, 1) || Source.Location == _initialLocation && Source.Map == _initialMap))
            {
                _honorDamage += amount;
            }
            else
            {
                _honorDamage += amount * 0.8;
            }
        }
        else if (from is BaseCreature creature && creature.GetMaster() == Source)
        {
            _honorDamage += amount * 0.8;
        }
    }

    public void OnTargetHit(Mobile from)
    {
        if (from != Source || PerfectionDamageBonus == 100)
        {
            return;
        }

        var bushido = (int)from.Skills.Bushido.Value;
        if (bushido < 50)
        {
            return;
        }

        PerfectionDamageBonus += bushido / 10;

        if (PerfectionDamageBonus >= 100)
        {
            PerfectionDamageBonus = 100;
            Source.SendLocalizedMessage(1063254); // You have Achieved Perfection in inflicting damage to this opponent!
        }
        else
        {
            Source.SendLocalizedMessage(1063255); // You gain in Perfection as you precisely strike your opponent.
        }
    }

    public void OnTargetMissed(Mobile from)
    {
        if (from != Source || PerfectionDamageBonus == 0)
        {
            return;
        }

        PerfectionDamageBonus -= 25;

        if (PerfectionDamageBonus <= 0)
        {
            PerfectionDamageBonus = 0;
            Source.SendLocalizedMessage(1063256); // You have lost all Perfection in fighting this opponent.
        }
        else
        {
            Source.SendLocalizedMessage(1063257); // You have lost some Perfection in fighting this opponent.
        }
    }

    public void OnSourceBeneficialAction(Mobile to)
    {
        if (to != Target)
        {
            return;
        }

        if (PerfectionDamageBonus >= 0)
        {
            PerfectionDamageBonus = 0;
            Source.SendLocalizedMessage(1063256); // You have lost all Perfection in fighting this opponent.
        }
    }

    public void OnSourceKilled()
    {
    }

    public void OnTargetKilled()
    {
        Cancel();

        var targetFame = Target.Fame;

        if (PerfectionDamageBonus > 0)
        {
            var restore = Math.Min(PerfectionDamageBonus * (targetFame + 5000) / 25000, 10);

            Source.Hits += restore;
            Source.Stam += restore;
            Source.Mana += restore;
        }

        if (VirtueSystem.GetVirtues(Source)?.Honor > targetFame)
        {
            return;
        }

        // Initial honor gain is 100th of the monsters honor
        var dGain = targetFame / 100.0 * (_honorDamage / _totalDamage);

        if (Math.Abs(_honorDamage - _totalDamage) < 0.01 && _firstHit == FirstHit.Granted)
        {
            dGain *= 1.5; // honor gain is increased a lot more if the combat was fully honorable
        }
        else
        {
            dGain *= 0.9;
        }

        // Minimum gain of 1 honor when the honor is under the monsters fame
        var gain = Math.Clamp((int)dGain, 1, 200);

        if (VirtueSystem.IsHighestPath(Source, VirtueName.Honor))
        {
            Source.SendLocalizedMessage(1063228); // You cannot gain more Honor.
            return;
        }

        var gainedPath = false;
        if (VirtueSystem.Award(Source, VirtueName.Honor, gain, ref gainedPath))
        {
            if (gainedPath)
            {
                Source.SendLocalizedMessage(1063226); // You have gained a path in Honor!
            }
            else
            {
                Source.SendLocalizedMessage(1063225); // You have gained in Honor.
            }
        }
    }

    public bool CheckDistance() => Utility.InRange(Source.Location, Target.Location, 18);

    public void Cancel()
    {
        Source.SentHonorContext = null;
        ((IHonorTarget)Target).ReceivedHonorContext = null;

        _timer.Stop();
    }

    private enum FirstHit
    {
        NotDelivered,
        Delivered,
        Granted
    }

    private class InternalTimer : Timer
    {
        private readonly HonorContext _context;

        public InternalTimer(HonorContext context) : base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0)) =>
            _context = context;

        protected override void OnTick()
        {
            if (!_context.CheckDistance())
            {
                _context.Cancel();
            }
        }
    }
}
