using Shouldly;
using Xunit;

namespace ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Tests;

/// <summary>
///     Task 2 coverage for <see cref="MTConnectDriverOptions"/> / <see cref="MTConnectTagDefinition"/>:
///     defaults are sane (non-zero timers, probe on) and the required-field shape (AgentUri) binds.
/// </summary>
[Trait("Category", "Unit")]
public sealed class MTConnectDriverOptionsTests
{
    /// <summary>Default sample/heartbeat timers are non-zero and the probe is enabled by default.</summary>
    [Fact]
    public void Options_default_sample_and_heartbeat_are_sane()
    {
        var o = new MTConnectDriverOptions { AgentUri = "http://agent:5000" };
        o.SampleIntervalMs.ShouldBeGreaterThan(0);
        o.HeartbeatMs.ShouldBeGreaterThan(0);
        o.Probe.Enabled.ShouldBeTrue();
    }

    /// <summary>AgentUri is the only required field; DeviceName scope defaults to unset (agent-wide).</summary>
    [Fact]
    public void Options_agent_uri_is_required_device_name_defaults_to_null()
    {
        var o = new MTConnectDriverOptions { AgentUri = "http://agent:5000" };
        o.AgentUri.ShouldBe("http://agent:5000");
        o.DeviceName.ShouldBeNull();
    }

    /// <summary>RequestTimeoutMs and SampleCount default to sane, non-zero, non-wedging values.</summary>
    [Fact]
    public void Options_request_timeout_and_sample_count_are_non_zero()
    {
        var o = new MTConnectDriverOptions { AgentUri = "http://agent:5000" };
        o.RequestTimeoutMs.ShouldBeGreaterThan(0);
        o.SampleCount.ShouldBeGreaterThan(0);
    }

    /// <summary>Probe sub-options mirror ModbusProbeOptions' shape: Enabled/Interval/Timeout, both non-zero.</summary>
    [Fact]
    public void Probe_defaults_have_non_zero_interval_and_timeout()
    {
        var probe = new MTConnectDriverOptions { AgentUri = "http://agent:5000" }.Probe;
        probe.Interval.ShouldBeGreaterThan(TimeSpan.Zero);
        probe.Timeout.ShouldBeGreaterThan(TimeSpan.Zero);
    }

    /// <summary>Reconnect sub-options default to a bounded, non-degenerate geometric backoff.</summary>
    [Fact]
    public void Reconnect_defaults_bound_backoff_growth()
    {
        var reconnect = new MTConnectDriverOptions { AgentUri = "http://agent:5000" }.Reconnect;
        reconnect.MaxBackoffMs.ShouldBeGreaterThan(reconnect.MinBackoffMs);
        reconnect.BackoffMultiplier.ShouldBeGreaterThan(1.0);
    }

    /// <summary>
    ///     <see cref="MTConnectDriverOptions.Tags"/> defaults to an <i>empty</i> collection, never
    ///     <c>null</c> — a null collection here is an operator-authorable NullReferenceException at
    ///     deploy, since a driver instance authored with no tags binds this default.
    /// </summary>
    [Fact]
    public void Options_tags_default_to_empty_and_are_never_null()
    {
        var o = new MTConnectDriverOptions { AgentUri = "http://agent:5000" };

        o.Tags.ShouldNotBeNull();
        o.Tags.ShouldBeEmpty();
    }

    /// <summary>
    ///     A populated options object carries the authored tag definitions through unchanged —
    ///     the observation index (Task 8) coerces each present observation to <i>its tag's</i>
    ///     DriverDataType, so the definitions must reach the driver from the factory config.
    /// </summary>
    [Fact]
    public void Options_round_trip_the_authored_tag_definitions()
    {
        var pos = new MTConnectTagDefinition(
            FullName: "dev1_pos",
            DriverDataType: ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Float64,
            MtCategory: "SAMPLE",
            MtType: "POSITION",
            Units: "MILLIMETER");
        var partCount = new MTConnectTagDefinition(
            FullName: "dev1_partcount",
            DriverDataType: ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Int64,
            MtCategory: "EVENT",
            MtType: "PART_COUNT");

        var o = new MTConnectDriverOptions { AgentUri = "http://agent:5000", Tags = [pos, partCount] };

        o.Tags.Count.ShouldBe(2);
        o.Tags[0].ShouldBe(pos);
        o.Tags[1].ShouldBe(partCount);
    }

    /// <summary>MTConnectTagDefinition round-trips its positional fields, including MTConnect metadata.</summary>
    [Fact]
    public void TagDefinition_carries_dataItemId_and_mtconnect_metadata()
    {
        var tag = new MTConnectTagDefinition(
            FullName: "dtop_2",
            DriverDataType: ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Float64,
            MtCategory: "SAMPLE",
            MtType: "POSITION",
            MtSubType: "ACTUAL",
            Units: "MILLIMETER");

        tag.FullName.ShouldBe("dtop_2");
        tag.DriverDataType.ShouldBe(ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Float64);
        tag.IsArray.ShouldBeFalse();
        tag.ArrayDim.ShouldBe(0);
        tag.MtCategory.ShouldBe("SAMPLE");
        tag.MtType.ShouldBe("POSITION");
        tag.MtSubType.ShouldBe("ACTUAL");
        tag.Units.ShouldBe("MILLIMETER");
    }
}
