using System.Collections.Generic;

namespace ZB.MOM.WW.OtOpcUa.Runtime.Historian;

/// <summary>
///     Binds the <c>ContinuousHistorization</c> configuration section that gates the continuous
///     historization of driver (non-Galaxy) tag values. When <see cref="Enabled"/> is <c>true</c>
///     <em>and</em> the <c>ServerHistorian</c> gateway is configured, the Host builds a durable,
///     crash-safe <c>FasterLogHistorizationOutbox</c> + a gateway-backed <c>IHistorianValueWriter</c>
///     and <c>WithOtOpcUaRuntimeActors</c> spawns the <see cref="ContinuousHistorizationRecorder"/>;
///     otherwise no recorder is spawned and driver tag values are not historized.
///     <para>
///         The recorder taps the per-node dependency-mux value fan-out, appends each numeric value to
///         the outbox (the crash boundary), and drains the outbox to the historian's SQL live-value
///         write path (<c>WriteLiveValues</c>) through the single <c>ServerHistorian</c> gateway. The
///         gateway connection (endpoint / key / TLS) is sourced from <c>ServerHistorianOptions</c> — this
///         section carries only the recorder + outbox knobs, never a gateway address or credential.
///     </para>
/// </summary>
public sealed class ContinuousHistorizationOptions
{
    /// <summary>The configuration section name this options class binds.</summary>
    public const string SectionName = "ContinuousHistorization";

    /// <summary>
    ///     When <c>true</c> (and the <c>ServerHistorian</c> gateway is configured), the
    ///     continuous-historization recorder + its durable outbox are wired and spawned; when
    ///     <c>false</c> (the default) no recorder is spawned and driver tag values are not historized.
    /// </summary>
    public bool Enabled { get; init; }

    /// <summary>
    ///     Directory holding the FasterLog outbox segment + commit files. Required when
    ///     <see cref="Enabled"/> is <c>true</c>. In production set an <b>absolute</b> path on durable
    ///     storage — a relative path resolves against the host's working directory, which may change
    ///     across deployments.
    /// </summary>
    public string OutboxPath { get; init; } = "";

    /// <summary>
    ///     Outbox commit cadence: <c>PerEntry</c> (the default) fsyncs the log before each append
    ///     returns (safest, no loss window); <c>Periodic</c> batches commits onto a background timer
    ///     every <see cref="CommitIntervalMs"/> ms (higher throughput, a bounded worst-case loss window).
    ///     Parsed case-insensitively against <c>HistorizationCommitMode</c>; an unrecognized value falls
    ///     back to <c>PerEntry</c>.
    /// </summary>
    public string CommitMode { get; init; } = "PerEntry";

    /// <summary>Periodic-mode commit cadence in milliseconds; must be positive when
    /// <see cref="CommitMode"/> is <c>Periodic</c>. Ignored under <c>PerEntry</c>.</summary>
    public int CommitIntervalMs { get; init; } = 100;

    /// <summary>Maximum outbox entries peeked + written per drain pass; clamped to a positive value by
    /// the recorder (a non-positive value falls back to 64).</summary>
    public int DrainBatchSize { get; init; } = 64;

    /// <summary>Steady drain cadence in seconds (also the post-success reschedule). Defaults to 2.</summary>
    public double DrainIntervalSeconds { get; init; } = 2;

    /// <summary>Maximum un-acked outbox entries before the drop-oldest capacity policy kicks in;
    /// <c>0</c> (the default) means unbounded.</summary>
    public int Capacity { get; init; }

    /// <summary>Initial retry backoff (seconds) after a failed drain pass. Defaults to 1.</summary>
    public double MinBackoffSeconds { get; init; } = 1;

    /// <summary>Cap (seconds) on the exponential retry backoff after repeated drain failures. Defaults to 30.</summary>
    public double MaxBackoffSeconds { get; init; } = 30;

    /// <summary>Returns operator-facing misconfiguration warnings for an <c>Enabled</c> recorder
    /// (empty when disabled or correctly configured). Pure — the registration logs each entry.</summary>
    /// <returns>Zero or more human-readable warning messages (never carrying secret values).</returns>
    public IReadOnlyList<string> Validate()
    {
        var warnings = new List<string>();
        if (!Enabled) return warnings;
        if (string.IsNullOrWhiteSpace(OutboxPath))
            warnings.Add("ContinuousHistorization:OutboxPath is empty while historization is enabled — the durable outbox has no directory to persist to; host startup will fail when the outbox is constructed. Set an absolute path on durable storage.");
        if (string.Equals(CommitMode, "Periodic", StringComparison.OrdinalIgnoreCase) && CommitIntervalMs <= 0)
            warnings.Add($"ContinuousHistorization:CommitIntervalMs is {CommitIntervalMs} — must be > 0 in Periodic commit mode; the periodic-commit loop cannot run.");
        return warnings;
    }
}
