using CliFx.Attributes;
using CliFx.Infrastructure;
using ZB.MOM.WW.OtOpcUa.Driver.Cli.Common;

namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Cli.Commands;

/// <summary>
///     Read one PCCC address (N7:0, F8:0, B3:0/3, L19:0, ST17:0, T4:0.ACC, etc.).
/// </summary>
[Command("read", Description = "Read a single PCCC file address.")]
public sealed class ReadCommand : AbLegacyCommandBase
{
    /// <summary>Gets the PCCC file address to read.</summary>
    [CommandOption("address", 'a', Description =
        "PCCC file address. File letter implies storage; bit-within-word via slash " +
        "(B3:0/3 or N7:0/5). Sub-element access for timers/counters/controls uses " +
        "dot notation (T4:0.ACC, C5:0.PRE, R6:0.LEN).",
        IsRequired = true)]
    public string Address { get; init; } = default!;

    /// <summary>Gets the data type of the address.</summary>
    [CommandOption("type", 't', Description =
        "Bit / Int / Long / Float / AnalogInt / String / TimerElement / CounterElement / " +
        "ControlElement (default Int).")]
    public AbLegacyDataType DataType { get; init; } = AbLegacyDataType.Int;

    /// <inheritdoc />
    public override async ValueTask ExecuteAsync(IConsole console)
    {
        ConfigureLogging();
        var ct = console.RegisterCancellationHandler();

        var tagName = SynthesiseTagName(Address, DataType);
        var tag = new AbLegacyTagDefinition(
            Name: tagName,
            DeviceHostAddress: Gateway,
            Address: Address,
            DataType: DataType,
            Writable: false);
        var options = BuildOptions([tag]);

        // Plain `var driver`: explicit ShutdownAsync(CancellationToken.None) in the
        // finally is the deliberate teardown path; combining it with `await using`
        // (which itself calls ShutdownAsync) would tear the driver down twice.
        var driver = new AbLegacyDriver(options, DriverInstanceId);
        try
        {
            await driver.InitializeAsync("{}", ct);
            var snapshot = await driver.ReadAsync([tagName], ct);
            await console.Output.WriteLineAsync(SnapshotFormatter.Format(Address, snapshot[0]));
        }
        finally
        {
            await driver.ShutdownAsync(CancellationToken.None);
            // Flush Serilog before process exit so buffered
            // log output emitted during driver shutdown is not lost.
            FlushLogging();
        }
    }

    /// <summary>Tag-name key the driver uses internally. Address+type is already unique.</summary>
    /// <param name="address">The PCCC file address.</param>
    /// <param name="type">The data type of the address.</param>
    /// <returns>The synthesised tag-name key combining <paramref name="address"/> and <paramref name="type"/>.</returns>
    internal static string SynthesiseTagName(string address, AbLegacyDataType type)
        => $"{address}:{type}";
}
