using System.Collections.Concurrent;

namespace NeonSprawl.Server.Game.AbilityInput;

/// <summary>Thread-safe in-memory cooldown map (NEO-32); not persisted across process restarts.</summary>
public sealed class InMemoryPlayerAbilityCooldownStore : IPlayerAbilityCooldownStore
{
    private readonly ConcurrentDictionary<string, ConcurrentDictionary<int, DateTimeOffset>> ends = new(StringComparer.Ordinal);

    public bool IsOnCooldown(string playerId, int slotIndex, DateTimeOffset now)
    {
        return TryGetActiveCooldownEnd(playerId, slotIndex, now, out _);
    }

    public void StartCooldown(string playerId, int slotIndex, DateTimeOffset now, TimeSpan duration)
    {
        var perSlot = ends.GetOrAdd(playerId, _ => new ConcurrentDictionary<int, DateTimeOffset>());
        perSlot[slotIndex] = now + duration;
    }

    public bool TryGetCooldownEndUtc(string playerId, int slotIndex, DateTimeOffset now, out DateTimeOffset endUtc)
    {
        return TryGetActiveCooldownEnd(playerId, slotIndex, now, out endUtc);
    }

    /// <summary>Shared read path: returns true with <paramref name="endUtc"/> when cooldown is active; removes slot entry when <paramref name="now"/> &gt;= stored end.</summary>
    private static bool TryGetActiveCooldownEnd(
        ConcurrentDictionary<string, ConcurrentDictionary<int, DateTimeOffset>> ends,
        string playerId,
        int slotIndex,
        DateTimeOffset now,
        out DateTimeOffset endUtc)
    {
        endUtc = default;
        if (!ends.TryGetValue(playerId, out var perSlot))
        {
            return false;
        }

        if (!perSlot.TryGetValue(slotIndex, out var end))
        {
            return false;
        }

        if (now >= end)
        {
            perSlot.TryRemove(slotIndex, out _);
            return false;
        }

        endUtc = end;
        return true;
    }

    private bool TryGetActiveCooldownEnd(string playerId, int slotIndex, DateTimeOffset now, out DateTimeOffset endUtc)
    {
        return TryGetActiveCooldownEnd(ends, playerId, slotIndex, now, out endUtc);
    }
}
