using ZB.MOM.WW.OtOpcUa.Core.OpcUa;

namespace ZB.MOM.WW.OtOpcUa.Server.OpcUa;

/// <summary>
///     Holds pre-loaded <see cref="EquipmentNamespaceContent"/> snapshots keyed by
///     <c>DriverInstanceId</c>. Populated once during <see cref="OpcUaServerService"/> startup
///     (after <see cref="NodeBootstrap"/> resolves the generation) so the synchronous lookup
///     delegate on <see cref="OpcUaApplicationHost"/> can serve the walker from memory without
///     blocking on async DB I/O mid-dispatch.
/// </summary>
/// <remarks>
///     <para>The registry is intentionally a shared mutable singleton with set-once-per-bootstrap
///     semantics rather than an immutable map passed by value — the composition in Program.cs
///     builds <see cref="OpcUaApplicationHost"/> before <see cref="NodeBootstrap"/> runs, so the
///     registry must exist at DI-compose time but be empty until the generation is known. A
///     driver registered after the initial populate pass simply returns null from
///     <see cref="Get"/> + the wire-in falls back to the "no UNS content, let DiscoverAsync own
///     it" path that PR #155 established.</para>
/// </remarks>
public sealed class DriverEquipmentContentRegistry
{
    private readonly Dictionary<string, EquipmentNamespaceContent> _content =
        new(StringComparer.OrdinalIgnoreCase);
    private readonly Lock _lock = new();

    public EquipmentNamespaceContent? Get(string driverInstanceId)
    {
        lock (_lock)
        {
            return _content.TryGetValue(driverInstanceId, out var c) ? c : null;
        }
    }

    public void Set(string driverInstanceId, EquipmentNamespaceContent content)
    {
        lock (_lock)
        {
            _content[driverInstanceId] = content;
        }
    }

    public int Count
    {
        get { lock (_lock) { return _content.Count; } }
    }
}
