namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions;

/// <summary>
///     Process-singleton registry of driver types known to this OtOpcUa instance.
///     Per-driver assemblies register their type metadata at startup; the Core uses
///     the registry to validate <c>DriverInstance.DriverType</c> values from the central config DB.
/// </summary>
/// <remarks>
///     Per <c>docs/v2/plan.md</c> decisions #91 (JSON content validation in Admin app, not SQL CLR)
///     and #111 (driver type → namespace kind mapping enforced by sp_ValidateDraft).
///     The registry is the source of truth for both checks.
///
///     Thread-safety: registration happens at startup (single thread); lookups happen on every
///     config-apply (multi-threaded). The internal dictionary is replaced atomically via
///     <see cref="System.Threading.Interlocked"/> on register; readers see a stable snapshot.
/// </remarks>
public sealed class DriverTypeRegistry
{
    private IReadOnlyDictionary<string, DriverTypeMetadata> _types =
        new Dictionary<string, DriverTypeMetadata>(StringComparer.OrdinalIgnoreCase);

    /// <summary>Register a driver type. Throws if the type name is already registered.</summary>
    public void Register(DriverTypeMetadata metadata)
    {
        ArgumentNullException.ThrowIfNull(metadata);

        var snapshot = _types;
        if (snapshot.ContainsKey(metadata.TypeName))
        {
            throw new InvalidOperationException(
                $"Driver type '{metadata.TypeName}' is already registered. " +
                $"Each driver type may be registered only once per process.");
        }

        var next = new Dictionary<string, DriverTypeMetadata>(snapshot, StringComparer.OrdinalIgnoreCase)
        {
            [metadata.TypeName] = metadata,
        };
        Interlocked.Exchange(ref _types, next);
    }

    /// <summary>Look up a driver type by name. Throws if unknown.</summary>
    public DriverTypeMetadata Get(string driverType)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(driverType);

        if (_types.TryGetValue(driverType, out var metadata))
            return metadata;

        throw new KeyNotFoundException(
            $"Driver type '{driverType}' is not registered. " +
            $"Known types: {string.Join(", ", _types.Keys)}.");
    }

    /// <summary>Try to look up a driver type by name. Returns null if unknown (no exception).</summary>
    public DriverTypeMetadata? TryGet(string driverType)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(driverType);
        return _types.GetValueOrDefault(driverType);
    }

    /// <summary>Snapshot of all registered driver types.</summary>
    public IReadOnlyCollection<DriverTypeMetadata> All() => _types.Values.ToList();
}

/// <summary>Per-driver-type metadata used by the Core, validator, and Admin UI.</summary>
/// <param name="TypeName">Driver type name (matches <c>DriverInstance.DriverType</c> column values).</param>
/// <param name="AllowedNamespaceKinds">Which namespace kinds this driver type may be bound to.</param>
/// <param name="DriverConfigJsonSchema">JSON Schema (Draft 2020-12) the driver's <c>DriverConfig</c> column must validate against.</param>
/// <param name="DeviceConfigJsonSchema">JSON Schema for <c>DeviceConfig</c> (multi-device drivers); null if the driver has no device layer.</param>
/// <param name="TagConfigJsonSchema">JSON Schema for <c>TagConfig</c>; required for every driver since every driver has tags.</param>
/// <param name="Tier">
///     Stability tier per <c>docs/v2/driver-stability.md</c> §2-4 and <c>docs/v2/plan.md</c>
///     decisions #63-74. Drives the shared resilience pipeline defaults
///     (<see cref="Tier"/> × capability → <c>CapabilityPolicy</c>), the <c>MemoryTracking</c>
///     hybrid-formula constants, and whether process-level <c>MemoryRecycle</c> / scheduled-
///     recycle protections apply (Tier C only). Every registered driver type must declare one.
/// </param>
public sealed record DriverTypeMetadata(
    string TypeName,
    NamespaceKindCompatibility AllowedNamespaceKinds,
    string DriverConfigJsonSchema,
    string? DeviceConfigJsonSchema,
    string TagConfigJsonSchema,
    DriverTier Tier);

/// <summary>Bitmask of namespace kinds a driver type may populate. Per decision #111.</summary>
[Flags]
public enum NamespaceKindCompatibility
{
    /// <summary>Driver does not populate any namespace (invalid; should never appear in registry).</summary>
    None = 0,

    /// <summary>Driver may populate Equipment-kind namespaces (UNS path, Equipment rows).</summary>
    Equipment = 1,

    /// <summary>Driver may populate SystemPlatform-kind namespaces (Galaxy hierarchy, FolderPath).</summary>
    SystemPlatform = 2,

    /// <summary>Driver may populate the future Simulated namespace (replay driver — not in v2.0).</summary>
    Simulated = 4,
}
