using System.Collections.Concurrent;
using Microsoft.Extensions.Options;

namespace NeonSprawl.Server.Game.PositionState;

/// <summary>Thread-safe in-memory positions; seeds the configured dev player on construction.</summary>
public sealed class InMemoryPositionStateStore(IOptions<GamePositionOptions> options) : IPositionStateStore
{
    private readonly ConcurrentDictionary<string, PositionSnapshot> positions = CreateInitialMap(options.Value);

    private static ConcurrentDictionary<string, PositionSnapshot> CreateInitialMap(GamePositionOptions o)
    {
        var id = o.DevPlayerId.Trim();
        if (id.Length == 0)
        {
            throw new InvalidOperationException("Game:DevPlayerId must be non-empty.");
        }

        var map = new ConcurrentDictionary<string, PositionSnapshot>(StringComparer.OrdinalIgnoreCase);
        var p = o.DefaultPosition ?? new DefaultPositionOptions();
        map[id] = new PositionSnapshot(p.X, p.Y, p.Z, 0);
        return map;
    }

    public bool TryGetPosition(string playerId, out PositionSnapshot snapshot)
    {
        var key = playerId?.Trim();
        if (!string.IsNullOrEmpty(key))
        {
            return positions.TryGetValue(key, out snapshot);
        }

        snapshot = default;
        return false;
    }

    public bool TryApplyMoveTarget(string playerId, double x, double y, double z, out PositionSnapshot snapshot)
    {
        var key = playerId?.Trim();
        if (string.IsNullOrEmpty(key))
        {
            snapshot = default;
            return false;
        }

        while (true)
        {
            if (!positions.TryGetValue(key, out var previous))
            {
                snapshot = default;
                return false;
            }

            var next = new PositionSnapshot(x, y, z, previous.Sequence + 1);
            if (!positions.TryUpdate(key, next, previous))
            {
                continue;
            }

            snapshot = next;
            return true;
        }
    }
}
