using System.ComponentModel.DataAnnotations;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;

namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Config;

/// <summary>
///     Driver-instance options for the in-process .NET 10 Galaxy driver. Maps to the
///     <c>DriverConfig</c> JSON column on the central config DB. Decomposed into nested
///     records so the JSON structure mirrors the runtime shape and operators can target
///     individual sections (gateway endpoint, mxaccess client identity, reconnect policy)
///     without touching the rest.
/// </summary>
/// <param name="Gateway">Connection details for the MxAccess gateway (mxaccessgw repo).</param>
/// <param name="MxAccess">MXAccess-specific knobs surfaced through gw — client name, publishing interval, write-user.</param>
/// <param name="Repository">Galaxy Repository browse options consumed by the discoverer.</param>
/// <param name="Reconnect">Backoff knobs for the in-driver reconnect supervisor (PR 4.5).</param>
public sealed record GalaxyDriverOptions(
    GalaxyGatewayOptions Gateway,
    GalaxyMxAccessOptions MxAccess,
    GalaxyRepositoryOptions Repository,
    GalaxyReconnectOptions Reconnect)
{
    /// <summary>
    ///     Timeout for the AdminUI Test Connect probe, in seconds. The AdminUI clamps to a
    ///     60s server-side maximum; this default is what the form pre-fills for new instances.
    /// </summary>
    [Display(Name = "Probe timeout (seconds)", Description = "Connection test timeout. Default 30s.", GroupName = "Diagnostics")]
    [Range(1, 60)]
    public int ProbeTimeoutSeconds { get; init; } = 30;

    /// <summary>
    ///     v3 authored raw tags this driver serves. The deploy artifact hands each authored raw
    ///     <c>Tag</c> as a <see cref="RawTagEntry"/> — RawPath identity (the driver wire reference)
    ///     + driver <c>TagConfig</c> blob (carrying the Galaxy dialled address under the camelCase
    ///     key <c>attributeRef</c> = <c>tag_name.AttributeName</c>) + the WriteIdempotent flag. The
    ///     driver builds a <c>RawPath → attributeRef</c> table from these: reads/writes/subscribes
    ///     arrive keyed by RawPath and the driver dials the mapped attributeRef to MXAccess. Empty
    ///     when no tags are authored — the driver then treats every reference as its own dialled
    ///     address (identity), preserving pre-v3 behaviour for the live-browse discovery path.
    /// </summary>
    public IReadOnlyList<RawTagEntry> RawTags { get; init; } = [];
}

/// <summary>
///     Connection details for the MxAccess gateway. <see cref="ApiKeySecretRef"/> is
///     resolved by <see cref="GalaxySecretRef.ResolveApiKeyAsync"/> at InitializeAsync time. Five forms
///     supported, in priority order:
///     <list type="bullet">
///       <item><c>env:NAME</c> — read from an environment variable (recommended for
///         production; the central config DB holds only the indirection, not the key).</item>
///       <item><c>file:PATH</c> — read from an ACL'd file outside the repo.</item>
///       <item><c>secret:NAME</c> — resolved through the shared <c>ZB.MOM.WW.Secrets</c>
///         encrypted store; fail-closed if absent (the production path).</item>
///       <item><c>dev:KEY</c> — explicit cleartext opt-in for dev rigs / parity tests;
///         no startup warning.</item>
///       <item>Anything else — treated as a literal cleartext API key for back-compat.
///         The resolver emits a <c>Warning</c> at startup so an operator who accidentally
///         committed a cleartext key sees it; production should migrate to <c>env:</c>
///         or <c>file:</c>.</item>
///     </list>
/// </summary>
// PR 6.5 tuning notes:
//   ConnectTimeoutSeconds = 10  — cold-start network path comfort margin; soak runs
//     never saw a successful connect take >2s, so 10s is generous without being lax.
//   DefaultCallTimeoutSeconds = 30 — bumped from 5s because a 50k-tag SubscribeBulk
//     can exceed 5s under MxAccess COM contention (the worker walks the gw item list
//     serially under the apartment lock). 30s leaves comfortable headroom for the
//     legitimate worst case while still failing fast on a wedged worker.
//   StreamTimeoutSeconds = 0 — unlimited; the StreamEvents RPC must run for the
//     lifetime of the driver. Set a finite value only for diagnostic runs.
public sealed record GalaxyGatewayOptions(
    string Endpoint,
    string ApiKeySecretRef,
    bool UseTls = true,
    string? CaCertificatePath = null,
    int ConnectTimeoutSeconds = 10,
    int DefaultCallTimeoutSeconds = 30,
    int StreamTimeoutSeconds = 0);

/// <summary>
///     MXAccess-specific knobs the gateway forwards to the worker process.
/// </summary>
/// <param name="ClientName">
///     Wonderware client identity. MUST be unique per OtOpcUa instance — when two instances
///     share a name, the older session loses subscription state. Redundancy pairs
///     enforce uniqueness via install scripts.
/// </param>
/// <param name="PublishingIntervalMs">
///     Hint forwarded as <c>buffered_update_interval_ms</c> on subscribe; lets the worker
///     coalesce updates at the OPC UA publishing cadence rather than every COM tick.
/// </param>
/// <param name="WriteUserId">
///     Reserved for ArchestrA secured-write user mapping; PR 4.3 wires <c>WriteSecured</c>
///     routing against this id. 0 = anonymous.
/// </param>
/// <param name="EventPumpChannelCapacity">
///     Bounded-channel size between the EventPump's network-read loop and its listener
///     fan-out loop (PR 6.2). Default 50_000 = one second of headroom at 50k tags / 1Hz;
///     raise it when <c>galaxy.events.dropped</c> shows up under transient consumer
///     slowness, lower it on a memory-tight host where the headroom isn't needed.
///     Must be ≥ 1 — <c>EventPump</c> enforces this at construction and throws
///     <see cref="ArgumentOutOfRangeException"/> if the value is zero or negative.
/// </param>
public sealed record GalaxyMxAccessOptions(
    string ClientName,
    int PublishingIntervalMs = 1000,
    int WriteUserId = 0,
    [Range(1, int.MaxValue)] int EventPumpChannelCapacity = 50_000);

/// <summary>
///     Galaxy Repository browse-side knobs consumed by PR 4.1's <c>GalaxyDiscoverer</c>.
/// </summary>
public sealed record GalaxyRepositoryOptions(
    int DiscoverPageSize = 5000,
    bool WatchDeployEvents = true);

/// <summary>
///     Backoff knobs for the in-driver reconnect supervisor (PR 4.5). Replay-on-session-lost
///     calls the gw's <c>ReplaySubscriptions</c> RPC after reconnect rather than re-issuing
///     subscribe-bulk for every tag.
/// </summary>
public sealed record GalaxyReconnectOptions(
    int InitialBackoffMs = 500,
    int MaxBackoffMs = 30_000,
    bool ReplayOnSessionLost = true);
