using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Driver.Sql.Contracts;

namespace ZB.MOM.WW.OtOpcUa.Driver.Sql;

/// <summary>
///     The typed form of a <c>Sql</c> driver instance's configuration — what
///     <see cref="SqlDriverConfigDto"/> becomes once the factory has applied its defaults (design §5.1).
///     Mirrors <c>ModbusDriverOptions</c>: the driver instance is constructed with these, and its
///     <see cref="SqlDriver.InitializeAsync"/> serves them rather than re-parsing the config JSON.
///     <para><b>No connection string here.</b> The resolved connection string is a separate constructor
///     argument, because it is a secret and these options are safe to log, diff and hold in a snapshot.</para>
/// </summary>
public sealed class SqlDriverOptions
{
    /// <summary>
    ///     The authored raw tags this driver serves. The deploy artifact hands each authored raw <c>Tag</c>
    ///     as a <see cref="RawTagEntry"/> (RawPath identity + driver <c>TagConfig</c> blob); the driver maps
    ///     each through <see cref="SqlEquipmentTagParser.TryParse"/> into its RawPath → definition table.
    ///     A SQL source has no tag-discovery protocol — the driver serves exactly these.
    /// </summary>
    public IReadOnlyList<RawTagEntry> RawTags { get; init; } = [];

    /// <summary>
    ///     Poll interval used when a subscriber does not ask for one (design §4: <c>defaultPollInterval</c>,
    ///     default 5 s). A subscriber that names an interval gets that interval, floored by
    ///     <see cref="PollGroupEngine.DefaultMinInterval"/>.
    /// </summary>
    public TimeSpan DefaultPollInterval { get; init; } = TimeSpan.FromSeconds(5);

    /// <summary>
    ///     The client-side wall-clock ceiling on one group query (design §8.3, default 15 s). A breach
    ///     publishes <see cref="SqlStatusCodes.BadTimeout"/> for that group's tags.
    /// </summary>
    public TimeSpan OperationTimeout { get; init; } = TimeSpan.FromSeconds(15);

    /// <summary>
    ///     The ADO.NET <c>CommandTimeout</c> server-side backstop (default 10 s), also the bound on the
    ///     Initialize-time liveness check.
    ///     <para>Authoring must keep <see cref="OperationTimeout"/> strictly greater than this (design §8.3);
    ///     inverted, the client-side abort always fires first and masks the backstop. <b>That rule is
    ///     enforced by config validation in the factory, not here</b> — the reader deliberately stays usable
    ///     with the order inverted so the frozen-database tests can prove the client-side bound fires.</para>
    /// </summary>
    public TimeSpan CommandTimeout { get; init; } = TimeSpan.FromSeconds(10);

    /// <summary>Cap on concurrently executing group queries — and therefore on concurrently open connections.</summary>
    public int MaxConcurrentGroups { get; init; } = 4;

    /// <summary>
    ///     When <see langword="true"/>, a present row whose value cell is NULL publishes
    ///     <see cref="SqlStatusCodes.Bad"/> instead of the default <see cref="SqlStatusCodes.Uncertain"/>.
    ///     It never governs an absent row, which is always <see cref="SqlStatusCodes.BadNoData"/>.
    /// </summary>
    public bool NullIsBad { get; init; }
}
