using System.Text.Json.Nodes;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Core.Resilience;

public class ResilienceFormModelTests
{
    // ---- 04/C-7: non-lossy round-trip ----

    [Fact]
    public void Unknown_top_level_key_survives_round_trip()
    {
        // bulkheadMaxConcurrent doubles as the 01/U-6 continuity proof: the deleted knob's key survives.
        var json = """{"bulkheadMaxConcurrent":16,"capabilityPolicies":{"Read":{"timeoutSeconds":5}}}""";

        var back = ResilienceFormModel.FromJson(json).ToJson();

        back.ShouldNotBeNull();
        var obj = JsonNode.Parse(back)!.AsObject();
        obj["bulkheadMaxConcurrent"]!.GetValue<int>().ShouldBe(16);
        obj["capabilityPolicies"]!["Read"]!["timeoutSeconds"]!.GetValue<int>().ShouldBe(5);
    }

    [Fact]
    public void Unknown_capability_entry_survives_round_trip()
    {
        var json = """{"capabilityPolicies":{"Read":{"timeoutSeconds":5},"FutureCap":{"timeoutSeconds":9}}}""";

        var back = ResilienceFormModel.FromJson(json).ToJson();

        back.ShouldNotBeNull();
        var obj = JsonNode.Parse(back)!.AsObject();
        obj["capabilityPolicies"]!["FutureCap"]!["timeoutSeconds"]!.GetValue<int>().ShouldBe(9);
        obj["capabilityPolicies"]!["Read"]!["timeoutSeconds"]!.GetValue<int>().ShouldBe(5);
    }

    [Fact]
    public void Unknown_per_policy_field_survives_round_trip()
    {
        var json = """{"capabilityPolicies":{"Read":{"timeoutSeconds":5,"futureField":7}}}""";

        var back = ResilienceFormModel.FromJson(json).ToJson();

        back.ShouldNotBeNull();
        var read = JsonNode.Parse(back)!.AsObject()["capabilityPolicies"]!["Read"]!.AsObject();
        read["timeoutSeconds"]!.GetValue<int>().ShouldBe(5);
        read["futureField"]!.GetValue<int>().ShouldBe(7);
    }

    [Fact]
    public void Malformed_json_sets_ParseFailed_and_ToJson_returns_original_text()
    {
        const string json = "{ not json";

        var m = ResilienceFormModel.FromJson(json);

        m.ParseFailed.ShouldBeTrue();
        m.ToJson().ShouldBe(json);   // never overwrite what it couldn't read
    }

    [Fact]
    public void Blank_form_serializes_to_null()
        => new ResilienceFormModel().ToJson().ShouldBeNull();

    [Fact]
    public void Partial_override_round_trips()
    {
        var m = new ResilienceFormModel { RecycleIntervalSeconds = 3600 };
        m.Policies["Read"].TimeoutSeconds = 5;
        m.Policies["Read"].RetryCount = 5;

        var json = m.ToJson();
        json.ShouldNotBeNull();

        var back = ResilienceFormModel.FromJson(json);
        back.RecycleIntervalSeconds.ShouldBe(3600);
        back.Policies["Read"].TimeoutSeconds.ShouldBe(5);
        back.Policies["Write"].IsEmpty.ShouldBeTrue();
    }

    [Fact]
    public void Malformed_json_yields_empty_model()
    {
        var m = ResilienceFormModel.FromJson("{ not valid json");
        m.RecycleIntervalSeconds.ShouldBeNull();
        m.Policies["Read"].IsEmpty.ShouldBeTrue();
    }

    [Fact]
    public void Validate_flags_out_of_range_values_with_capability_and_range()
    {
        var m = new ResilienceFormModel();
        m.Policies["Read"].TimeoutSeconds = 0;      // < MinTimeoutSeconds
        m.Policies["Read"].RetryCount = -1;         // < MinRetryCount
        m.Policies["Read"].BreakerFailureThreshold = 1;  // below Polly MinimumThroughput floor

        var msgs = m.Validate();

        msgs.Count.ShouldBe(3);
        msgs.ShouldContain(s => s.Contains("Read") && s.Contains("timeout"));
        msgs.ShouldContain(s => s.Contains("Read") && s.Contains("retries"));
        msgs.ShouldContain(s => s.Contains("Read") && s.Contains("breaker"));
    }

    [Fact]
    public void Validate_passes_in_range_and_blank()
    {
        var blank = new ResilienceFormModel();
        blank.Validate().ShouldBeEmpty();

        var inRange = new ResilienceFormModel();
        inRange.Policies["Read"].TimeoutSeconds = 5;
        inRange.Policies["Read"].RetryCount = 3;
        inRange.Policies["Read"].BreakerFailureThreshold = 4;
        inRange.Policies["Write"].BreakerFailureThreshold = 0;   // 0 = breaker off, legal
        inRange.Validate().ShouldBeEmpty();
    }

    [Fact]
    public void Emitted_json_is_consumable_by_the_runtime_parser()
    {
        var m = new ResilienceFormModel();
        m.Policies["Read"].TimeoutSeconds = 7;

        var opts = DriverResilienceOptionsParser.ParseOrDefaults(DriverTier.B, m.ToJson(), out var diag);
        diag.ShouldBeNull();
        opts.Resolve(DriverCapability.Read).TimeoutSeconds.ShouldBe(7);
        opts.Resolve(DriverCapability.Write).RetryCount.ShouldBe(0);
    }
}
