using System.Collections.Concurrent;
using Microsoft.Extensions.Options;
using NeonSprawl.Server.Game.PositionState;

namespace NeonSprawl.Server.Game.AbilityInput;

/// <summary>Thread-safe in-memory hotbar loadout store; seeds configured dev player with an empty loadout.</summary>
public sealed class InMemoryPlayerHotbarLoadoutStore(IOptions<GamePositionOptions> options) : IPlayerHotbarLoadoutStore
{
    private readonly ConcurrentDictionary<string, ConcurrentDictionary<int, string>> byPlayer =
        CreateInitialMap(options.Value);

    private static ConcurrentDictionary<string, ConcurrentDictionary<int, string>> CreateInitialMap(GamePositionOptions o)
    {
        var id = NormalizePlayerId(o.DevPlayerId);
        var map = new ConcurrentDictionary<string, ConcurrentDictionary<int, string>>(StringComparer.OrdinalIgnoreCase);
        map[id] = new ConcurrentDictionary<int, string>();
        return map;
    }

    public bool TryGetBindings(string playerId, out IReadOnlyDictionary<int, string> bindings)
    {
        var key = NormalizePlayerId(playerId);
        if (key.Length == 0 || !byPlayer.TryGetValue(key, out var map))
        {
            bindings = EmptyBindings();
            return false;
        }

        bindings = Clone(map);
        return true;
    }

    public bool TryUpsertBindings(string playerId, IReadOnlyList<HotbarSlotBinding> updates, out IReadOnlyDictionary<int, string> bindings)
    {
        var key = NormalizePlayerId(playerId);
        if (key.Length == 0 || !byPlayer.TryGetValue(key, out var map))
        {
            bindings = EmptyBindings();
            return false;
        }

        foreach (var update in updates)
        {
            map[update.SlotIndex] = update.AbilityId;
        }

        bindings = Clone(map);
        return true;
    }

    private static IReadOnlyDictionary<int, string> Clone(ConcurrentDictionary<int, string> source)
    {
        var copy = new Dictionary<int, string>(source.Count);
        foreach (var kv in source)
        {
            copy[kv.Key] = kv.Value;
        }

        return copy;
    }

    private static IReadOnlyDictionary<int, string> EmptyBindings() => new Dictionary<int, string>();

    private static string NormalizePlayerId(string? playerId)
    {
        var t = playerId?.Trim();
        if (string.IsNullOrEmpty(t))
        {
            return string.Empty;
        }

        return t.ToLowerInvariant();
    }
}
