using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;

namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Drivers;

/// <summary>
///     Closes <c>deferment.md</c> G-4. A guard already existed for <c>DriverTypeNames</c> ↔ the <c>/raw</c>
///     driver PICKER, but not for the two dispatch maps downstream of it, so a driver could be registered,
///     offered in the picker, and then have no config form — which is exactly what happened to
///     <c>Calculation</c> (G-1) and to Sql/MTConnect/Calculation on the device modal (G-2). Both survived
///     review because nothing could see them.
///     <para>These are ordinary map lookups rather than reflection over a private field: the two modals were
///     converted from a Razor <c>@switch</c> — unreachable from a test, since it compiles into
///     <c>BuildRenderTree</c>'s IL — to <c>DriverConfigFormMap</c> / <c>DeviceFormMap</c>. There is no bUnit
///     in this project, so a map is the only thing a test can hold on to.</para>
/// </summary>
[Trait("Category", "Unit")]
public sealed class DriverFormMapParityTests
{
    /// <summary>Every declared driver type must have a typed config form. There is no exemption: a driver
    /// with no configurable surface still needs a form to say so, or the operator meets a bare warning.</summary>
    [Fact]
    public void Every_declared_driver_type_has_a_typed_config_form()
    {
        var missing = DriverTypeNames.All
            .Where(t => DriverConfigFormMap.Resolve(t) is null)
            .OrderBy(t => t, StringComparer.Ordinal)
            .ToList();

        missing.ShouldBeEmpty(
            $"these driver types are registered and offered in the /raw picker but DriverConfigModal has no "
            + $"form for them, so their config is unauthorable through the UI: {string.Join(", ", missing)}");
    }

    /// <summary>The reverse direction — a mapped type that is no longer a declared driver type is a stale
    /// entry, and would keep a deleted driver's form reachable.</summary>
    [Fact]
    public void Every_config_form_maps_to_a_declared_driver_type()
    {
        var declared = DriverTypeNames.All.ToHashSet(StringComparer.OrdinalIgnoreCase);
        var stale = DriverConfigFormMap.MappedDriverTypes
            .Where(t => !declared.Contains(t))
            .OrderBy(t => t, StringComparer.Ordinal)
            .ToList();

        stale.ShouldBeEmpty($"DriverConfigFormMap has entries for undeclared driver types: {string.Join(", ", stale)}");
    }

    /// <summary>
    ///     Every declared driver type must EITHER have a typed device form OR be declared single-connection.
    ///     <para>The either/or matters: demanding a device form from every driver would be wrong (Sql has one
    ///     connection string, MTConnect one Agent URI, Calculation no connection at all), and a test that has
    ///     to be suppressed for five drivers stops being read. Forcing the choice to be explicit is what makes
    ///     this a guard — a new driver cannot silently fall through.</para>
    /// </summary>
    [Fact]
    public void Every_declared_driver_type_has_a_device_form_or_is_declared_single_connection()
    {
        var unaccounted = DriverTypeNames.All
            .Where(t => DeviceFormMap.Resolve(t) is null && !DeviceFormMap.IsSingleConnection(t))
            .OrderBy(t => t, StringComparer.Ordinal)
            .ToList();

        unaccounted.ShouldBeEmpty(
            $"these driver types have neither a typed device form nor a place in "
            + $"DeviceFormMap.SingleConnectionDriverTypes, so DeviceModal falls through to its "
            + $"\"no typed device form\" warning: {string.Join(", ", unaccounted)}");
    }

    /// <summary>The reverse direction for the device map.</summary>
    [Fact]
    public void Every_device_form_maps_to_a_declared_driver_type()
    {
        var declared = DriverTypeNames.All.ToHashSet(StringComparer.OrdinalIgnoreCase);
        var stale = DeviceFormMap.MappedDriverTypes
            .Concat(DeviceFormMap.SingleConnectionDriverTypes)
            .Where(t => !declared.Contains(t))
            .Distinct(StringComparer.OrdinalIgnoreCase)
            .OrderBy(t => t, StringComparer.Ordinal)
            .ToList();

        stale.ShouldBeEmpty($"DeviceFormMap references undeclared driver types: {string.Join(", ", stale)}");
    }

    /// <summary>
    ///     A driver that authors its connection on the DRIVER form must have a driver form to author it on.
    ///     Without this, declaring a type single-connection would be a way to opt out of both maps at once
    ///     and leave the connection unauthorable anywhere.
    /// </summary>
    [Fact]
    public void Every_single_connection_driver_has_a_driver_config_form()
    {
        var missing = DeviceFormMap.SingleConnectionDriverTypes
            .Where(t => DriverConfigFormMap.Resolve(t) is null)
            .OrderBy(t => t, StringComparer.Ordinal)
            .ToList();

        missing.ShouldBeEmpty(
            $"these types are declared single-connection but have no driver config form, so their connection "
            + $"cannot be authored anywhere: {string.Join(", ", missing)}");
    }
}
