namespace NeonSprawl.Server.Game.Targeting;

/// <summary>Thread-safe prototype lock store; player keys are trimmed with ordinal case-insensitive lookup (same spirit as position APIs).</summary>
public sealed class InMemoryPlayerTargetLockStore : IPlayerTargetLockStore
{
    private readonly object mutex = new();
    private readonly Dictionary<string, PlayerLockRow> rows = new(StringComparer.OrdinalIgnoreCase);

    private readonly record struct PlayerLockRow(string? LockIdLowercase, int Sequence);

    public (string? LockIdLowercase, int Sequence) GetLockState(string playerId)
    {
        var key = playerId.Trim();
        if (key.Length == 0)
        {
            return (null, 0);
        }

        lock (mutex)
        {
            return rows.TryGetValue(key, out var row) ? (row.LockIdLowercase, row.Sequence) : (null, 0);
        }
    }

    public (string? LockIdLowercase, int Sequence) ApplyClear(string playerId)
    {
        var key = playerId.Trim();
        if (key.Length == 0)
        {
            return (null, 0);
        }

        lock (mutex)
        {
            if (!rows.TryGetValue(key, out var prev))
            {
                return (null, 0);
            }

            if (prev.LockIdLowercase is null)
            {
                return (null, prev.Sequence);
            }

            var next = new PlayerLockRow(null, prev.Sequence + 1);
            rows[key] = next;
            // NEO-27 telemetry hook site: `target_changed` fires when lock id transitions.
            // TODO(E9.M1): emit cataloged telemetry event (player id, previous/next lock, cause=clear).
            return (null, next.Sequence);
        }
    }

    public (string? LockIdLowercase, int Sequence) ApplySet(string playerId, string targetIdLowercase)
    {
        var key = playerId.Trim();
        if (key.Length == 0)
        {
            return (null, 0);
        }

        lock (mutex)
        {
            var prev = rows.TryGetValue(key, out var r) ? r : new PlayerLockRow(null, 0);

            if (string.Equals(prev.LockIdLowercase, targetIdLowercase, StringComparison.Ordinal))
            {
                return (prev.LockIdLowercase, prev.Sequence);
            }

            var next = new PlayerLockRow(targetIdLowercase, prev.Sequence + 1);
            rows[key] = next;
            // NEO-27 telemetry hook site: `target_changed` fires when lock id transitions.
            // TODO(E9.M1): emit cataloged telemetry event (player id, previous/next lock, cause=tab/select).
            return (next.LockIdLowercase, next.Sequence);
        }
    }
}
