namespace ZB.MOM.NatsNet.Server.Internal;

/// <summary>
/// A pointer that can be toggled between weak and strong references, allowing
/// the garbage collector to reclaim the target when weakened.
/// Mirrors the Go <c>elastic.Pointer[T]</c> type.
/// </summary>
/// <typeparam name="T">The type of the referenced object. Must be a reference type.</typeparam>
public sealed class ElasticPointer<T> where T : class
{
    private WeakReference<T>? _weak;
    private T? _strong;

    /// <summary>
    /// Creates a new <see cref="ElasticPointer{T}"/> holding a weak reference to <paramref name="value"/>.
    /// </summary>
    public static ElasticPointer<T> Make(T value)
    {
        return new ElasticPointer<T> { _weak = new WeakReference<T>(value) };
    }

    /// <summary>
    /// Updates the target. If the pointer is currently strengthened, the strong reference is updated too.
    /// </summary>
    public void Set(T value)
    {
        _weak = new WeakReference<T>(value);
        if (_strong != null)
            _strong = value;
    }

    /// <summary>
    /// Promotes to a strong reference, preventing the GC from collecting the target.
    /// No-op if already strengthened or if the weak target has been collected.
    /// </summary>
    public void Strengthen()
    {
        if (_strong != null)
            return;
        if (_weak != null && _weak.TryGetTarget(out var target))
            _strong = target;
    }

    /// <summary>
    /// Reverts to a weak reference, allowing the GC to reclaim the target.
    /// No-op if already weakened.
    /// </summary>
    public void Weaken()
    {
        _strong = null;
    }

    /// <summary>
    /// Returns the target value, or <see langword="null"/> if the weak reference has been collected.
    /// </summary>
    public T? Value()
    {
        if (_strong != null)
            return _strong;
        if (_weak != null && _weak.TryGetTarget(out var target))
            return target;
        return null;
    }
}
