using Shouldly;
using Xunit;

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

/// <summary>
///     Gitea #516 — <c>OpcUaClientDriver.InitializeAsync</c> used to serve the options its CONSTRUCTOR
///     captured. Its own comment claimed "resolving on every InitializeAsync … picks up rotations", which
///     was true of SECRET rotation only: the endpoint, the security policy and the tag set were all frozen
///     at construction, so an operator's edit was discarded while the deployment still sealed green.
/// </summary>
[Trait("Category", "Unit")]
public sealed class OpcUaClientReinitConfigAdoptionTests
{
    /// <summary>
    ///     <c>ValidateNamespaceKind</c> runs at the top of init and rejects <c>TargetNamespaceKind=Equipment</c>
    ///     with an empty UNS mapping table. Reinitializing INTO that shape must therefore throw — a driver
    ///     still serving the constructor's valid options would validate those and succeed, which is exactly
    ///     how the discarded edit stayed invisible.
    /// </summary>
    [Fact]
    public async Task Reinitialize_adopts_a_changed_config()
    {
        var driver = OpcUaClientDriverFactoryExtensions.CreateInstance(
            "opc1",
            """{"endpointUrl":"opc.tcp://10.0.0.1:4840","targetNamespaceKind":"Equipment","unsMappingTable":{"a":"b"}}""");

        await Should.ThrowAsync<InvalidOperationException>(
            () => driver.ReinitializeAsync(
                """{"endpointUrl":"opc.tcp://10.0.0.1:4840","targetNamespaceKind":"Equipment","unsMappingTable":{}}""",
                CancellationToken.None),
            "OpcUaClientDriver kept its constructor-supplied options after a reinitialize with a changed config (#516)");
    }

    /// <summary>An empty/placeholder document must still keep the constructor options.</summary>
    [Fact]
    public async Task Reinitialize_with_an_empty_document_keeps_the_constructor_options()
    {
        var driver = OpcUaClientDriverFactoryExtensions.CreateInstance(
            "opc1",
            """{"endpointUrl":"opc.tcp://10.0.0.1:4840","targetNamespaceKind":"Equipment","unsMappingTable":{"a":"b"}}""");

        // "{}" keeps the (valid) constructor options, so ValidateNamespaceKind passes and the driver gets as
        // far as the connect — which fails against an unreachable endpoint. Anything BUT the validation
        // throw proves the constructor options survived.
        var ex = await Record.ExceptionAsync(() => driver.ReinitializeAsync("{}", CancellationToken.None));
        (ex is InvalidOperationException ioe && ioe.Message.Contains("UnsMappingTable", StringComparison.OrdinalIgnoreCase))
            .ShouldBeFalse("an empty document must keep the constructor-supplied options, not re-validate an empty config");
    }
}
