using Microsoft.Extensions.Options;

namespace SpaceGame.Api.Universe.Simulation;

public sealed class BalanceService : IBalanceService
{
    private readonly Lock _sync = new();
    private BalanceOptions _current;

    public BalanceService(IOptions<BalanceOptions> defaults)
    {
        _current = Sanitize(defaults.Value);
    }

    public float SimulationSpeedMultiplier
    {
        get
        {
            lock (_sync)
            {
                return _current.SimulationSpeedMultiplier;
            }
        }
    }

    public float YPlane
    {
        get
        {
            lock (_sync)
            {
                return _current.YPlane;
            }
        }
    }

    public float ArrivalThreshold
    {
        get
        {
            lock (_sync)
            {
                return _current.ArrivalThreshold;
            }
        }
    }

    public float MiningRate
    {
        get
        {
            lock (_sync)
            {
                return _current.MiningRate;
            }
        }
    }

    public float MiningCycleSeconds
    {
        get
        {
            lock (_sync)
            {
                return _current.MiningCycleSeconds;
            }
        }
    }

    public float TransferRate
    {
        get
        {
            lock (_sync)
            {
                return _current.TransferRate;
            }
        }
    }

    public float DockingDuration
    {
        get
        {
            lock (_sync)
            {
                return _current.DockingDuration;
            }
        }
    }

    public float UndockingDuration
    {
        get
        {
            lock (_sync)
            {
                return _current.UndockingDuration;
            }
        }
    }

    public float UndockDistance
    {
        get
        {
            lock (_sync)
            {
                return _current.UndockDistance;
            }
        }
    }

    public BalanceOptions GetCurrent()
    {
        lock (_sync)
        {
            return Clone(_current);
        }
    }

    public BalanceOptions Update(BalanceOptions candidate)
    {
        lock (_sync)
        {
            _current = Sanitize(candidate);
            return Clone(_current);
        }
    }

    private static BalanceOptions Clone(BalanceOptions value)
    {
        return new BalanceOptions
        {
            SimulationSpeedMultiplier = value.SimulationSpeedMultiplier,
            YPlane = value.YPlane,
            ArrivalThreshold = value.ArrivalThreshold,
            MiningRate = value.MiningRate,
            MiningCycleSeconds = value.MiningCycleSeconds,
            TransferRate = value.TransferRate,
            DockingDuration = value.DockingDuration,
            UndockingDuration = value.UndockingDuration,
            UndockDistance = value.UndockDistance,
        };
    }

    private static BalanceOptions Sanitize(BalanceOptions candidate)
    {
        static float finiteOr(float value, float fallback) =>
          float.IsFinite(value) ? value : fallback;

        return new BalanceOptions
        {
            SimulationSpeedMultiplier = MathF.Max(0.01f, finiteOr(candidate.SimulationSpeedMultiplier, 1f)),
            YPlane = MathF.Max(0f, finiteOr(candidate.YPlane, 0f)),
            ArrivalThreshold = MathF.Max(0.1f, finiteOr(candidate.ArrivalThreshold, 16f)),
            MiningRate = MathF.Max(0f, finiteOr(candidate.MiningRate, 10f)),
            MiningCycleSeconds = MathF.Max(0.1f, finiteOr(candidate.MiningCycleSeconds, 10f)),
            TransferRate = MathF.Max(0f, finiteOr(candidate.TransferRate, 56f)),
            DockingDuration = MathF.Max(0.1f, finiteOr(candidate.DockingDuration, 1.2f)),
            UndockingDuration = MathF.Max(0.1f, finiteOr(candidate.UndockingDuration, 1.2f)),
            UndockDistance = MathF.Max(0f, finiteOr(candidate.UndockDistance, 42f)),
        };
    }
}
