using System.Collections.Concurrent;

namespace NeonSprawl.Server.Game.Gathering;

/// <summary>Thread-safe in-memory node instance capacity; empty at startup — lazy rows only (NEO-61).</summary>
public sealed class InMemoryResourceNodeInstanceStore : IResourceNodeInstanceStore
{
    private readonly ConcurrentDictionary<string, int> remainingById = new(StringComparer.Ordinal);

    private readonly ConcurrentDictionary<string, object> idLocks = new(StringComparer.Ordinal);

    /// <inheritdoc />
    public bool TryGetRemainingGathers(string interactableId, out ResourceNodeInstanceSnapshot snapshot)
    {
        var key = ResourceNodeInstanceIds.Normalize(interactableId);
        if (key.Length == 0)
        {
            snapshot = default;
            return false;
        }

        lock (idLocks.GetOrAdd(key, _ => new object()))
        {
            if (!remainingById.TryGetValue(key, out var remaining))
            {
                snapshot = default;
                return false;
            }

            snapshot = new ResourceNodeInstanceSnapshot(key, remaining);
            return true;
        }
    }

    /// <inheritdoc />
    public bool TryEnsureInitialized(string interactableId, int initialRemainingGathers)
    {
        var key = ResourceNodeInstanceIds.Normalize(interactableId);
        if (key.Length == 0 || initialRemainingGathers < 0)
        {
            return false;
        }

        lock (idLocks.GetOrAdd(key, _ => new object()))
        {
            if (remainingById.ContainsKey(key))
            {
                return true;
            }

            remainingById[key] = initialRemainingGathers;
            return true;
        }
    }

    /// <inheritdoc />
    public bool TryDecrementRemainingGathers(
        string interactableId,
        out int previousRemaining,
        out int remainingGathers)
    {
        previousRemaining = 0;
        remainingGathers = 0;
        var key = ResourceNodeInstanceIds.Normalize(interactableId);
        if (key.Length == 0)
        {
            return false;
        }

        lock (idLocks.GetOrAdd(key, _ => new object()))
        {
            if (!remainingById.TryGetValue(key, out var current) || current <= 0)
            {
                if (current == 0)
                {
                    remainingGathers = 0;
                }

                return false;
            }

            previousRemaining = current;
            remainingGathers = current - 1;
            remainingById[key] = remainingGathers;
            return true;
        }
    }
}
