using CliFx.Attributes;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;

namespace ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli;

/// <summary>
///     Intermediate base for the four TwinCAT CLI commands that build a single-device /
///     single-tag <see cref="TwinCATDriverOptions"/> — <c>probe</c>, <c>read</c>, <c>write</c>,
///     <c>subscribe</c>. Adds the <c>--poll-only</c> flag (relevant only when the driver is
///     about to register native ADS notifications, which is why it does NOT live on the
///     <c>browse</c> command) and the <c>BuildOptions</c> helper that
///     assembles the driver-side options record.
/// </summary>
public abstract class TwinCATTagCommandBase : TwinCATCommandBase
{
    /// <summary>Gets or sets a value indicating whether to use polling instead of native ADS notifications.</summary>
    [CommandOption("poll-only", Description =
        "Disable native ADS notifications and fall through to the shared PollGroupEngine " +
        "(same as setting UseNativeNotifications=false in a real driver config).")]
    public bool PollOnly { get; init; }

    /// <summary>
    ///     Build a <see cref="TwinCATDriverOptions"/> with the AMS target this base collected +
    ///     the tag list a subclass supplies. The CLI holds typed definitions (synthesised from
    ///     operator flags), so each is serialised back to a <see cref="RawTagEntry"/> — the v3
    ///     delivery unit — via <see cref="TwinCATTagDefinitionFactory.ToTagConfig"/>, keyed by the
    ///     def's synthesised name as its RawPath, with the def's <c>DeviceHostAddress</c> carried as
    ///     the entry's <see cref="RawTagEntry.DeviceName"/> so the driver routes it to the sole
    ///     device (matched by host). Probe disabled, controller-browse disabled, native
    ///     notifications toggled by <see cref="PollOnly"/>.
    /// </summary>
    /// <param name="tags">Tag definitions for the driver.</param>
    /// <returns>The built <see cref="TwinCATDriverOptions"/>.</returns>
    protected TwinCATDriverOptions BuildOptions(IReadOnlyList<TwinCATTagDefinition> tags) => new()
    {
        Devices = [new TwinCATDeviceOptions(
            HostAddress: Gateway,
            DeviceName: $"cli-{AmsNetId}:{AmsPort}")],
        RawTags = [.. tags.Select(t => new RawTagEntry(
            t.Name, TwinCATTagDefinitionFactory.ToTagConfig(t), t.WriteIdempotent, t.DeviceHostAddress))],
        Timeout = Timeout,
        Probe = new TwinCATProbeOptions { Enabled = false },
        UseNativeNotifications = !PollOnly,
        EnableControllerBrowse = false,
    };

    // ---- Test hook ----
    /// <summary>Test hook that exposes BuildOptions for unit testing.</summary>
    /// <param name="tags">Tag definitions for the driver.</param>
    /// <returns>The built <see cref="TwinCATDriverOptions"/>.</returns>
    internal TwinCATDriverOptions BuildOptionsForTest(IReadOnlyList<TwinCATTagDefinition> tags)
        => BuildOptions(tags);
}
