using System.Text.Json;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;

namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions.Tests;

/// <summary>
///     Pins the shared strict-capable TagConfig field readers (R2-11, 05/CONV-2): the
///     <see cref="TagConfigJson.TryReadEnum{TEnum}"/> Absent/Valid/Invalid matrix (case-insensitive),
///     <see cref="TagConfigJson.ReadEnumOrDefault{TEnum}"/> lenient equivalence to the retired copies,
///     <see cref="TagConfigJson.TryReadEnumStrict{TEnum}"/> reject-only-invalid semantics (R2-11 Phase C),
///     <see cref="TagConfigJson.ReadWritable"/> explicit-false-only semantics, and
///     <see cref="TagConfigJson.DescribeInvalidEnum{TEnum}"/> messages.
/// </summary>
public sealed class TagConfigJsonTests
{
    /// <summary>Sample enum for the reader matrix (public so it can be an <c>[InlineData]</c> arg).</summary>
    public enum Sample { Alpha, Beta, Gamma }

    private static JsonElement Root(string json) => JsonDocument.Parse(json).RootElement;

    [Theory]
    [InlineData("{\"k\":\"Beta\"}", JsonEnumRead.Valid, Sample.Beta)]
    [InlineData("{\"k\":\"beta\"}", JsonEnumRead.Valid, Sample.Beta)]     // case-insensitive
    [InlineData("{\"k\":\"GAMMA\"}", JsonEnumRead.Valid, Sample.Gamma)]
    [InlineData("{\"k\":\"Deltaa\"}", JsonEnumRead.Invalid, default(Sample))]  // typo → Invalid
    [InlineData("{}", JsonEnumRead.Absent, default(Sample))]              // absent
    [InlineData("{\"k\":123}", JsonEnumRead.Absent, default(Sample))]    // non-string → Absent (lenient)
    [InlineData("{\"k\":null}", JsonEnumRead.Absent, default(Sample))]
    public void TryReadEnum_matrix(string json, JsonEnumRead expected, Sample expectedValue)
    {
        var outcome = TagConfigJson.TryReadEnum<Sample>(Root(json), "k", out var v);
        outcome.ShouldBe(expected);
        v.ShouldBe(expectedValue);
    }

    [Theory]
    [InlineData("{\"k\":\"Gamma\"}", Sample.Gamma)]     // valid → parsed
    [InlineData("{\"k\":\"typo\"}", Sample.Alpha)]      // invalid → fallback
    [InlineData("{}", Sample.Alpha)]                    // absent → fallback
    [InlineData("{\"k\":42}", Sample.Alpha)]            // non-string → fallback
    public void ReadEnumOrDefault_is_lenient(string json, Sample expected)
    {
        TagConfigJson.ReadEnumOrDefault(Root(json), "k", Sample.Alpha).ShouldBe(expected);
    }

    [Theory]
    [InlineData("{\"k\":\"Gamma\"}", true, Sample.Gamma)]   // valid → parsed, ok
    [InlineData("{}", true, Sample.Alpha)]                  // absent → fallback, ok
    [InlineData("{\"k\":42}", true, Sample.Alpha)]          // non-string → Absent → fallback, ok
    [InlineData("{\"k\":\"typo\"}", false, default(Sample))] // invalid (typo) → REJECT (false)
    public void TryReadEnumStrict_rejects_only_invalid(string json, bool expectedOk, Sample expectedValue)
    {
        var ok = TagConfigJson.TryReadEnumStrict(Root(json), "k", Sample.Alpha, out var v);
        ok.ShouldBe(expectedOk);
        v.ShouldBe(expectedValue);
    }

    [Theory]
    [InlineData("{\"writable\":false}", true, false)]   // explicit false → false
    [InlineData("{\"writable\":true}", true, true)]     // explicit true → default
    [InlineData("{}", true, true)]                       // absent → default
    [InlineData("{\"writable\":\"no\"}", true, true)]  // non-bool → default
    [InlineData("{}", false, false)]                     // absent, default false
    [InlineData("{\"writable\":false}", false, false)]  // explicit false, default false
    public void ReadWritable_explicit_false_only(string json, bool dflt, bool expected)
    {
        TagConfigJson.ReadWritable(Root(json), dflt).ShouldBe(expected);
    }

    [Fact]
    public void DescribeInvalidEnum_names_field_value_and_valid_members()
    {
        var msg = TagConfigJson.DescribeInvalidEnum<Sample>(Root("{\"dataType\":\"Betaa\"}"), "dataType");
        msg.ShouldNotBeNull();
        msg.ShouldContain("Betaa");
        msg.ShouldContain("dataType");
        msg.ShouldContain("Alpha");
        msg.ShouldContain("Beta");
        msg.ShouldContain("Gamma");
    }

    [Theory]
    [InlineData("{\"dataType\":\"Beta\"}")]   // valid → null
    [InlineData("{}")]                          // absent → null
    [InlineData("{\"dataType\":5}")]           // non-string → null
    public void DescribeInvalidEnum_null_when_absent_or_valid(string json)
    {
        TagConfigJson.DescribeInvalidEnum<Sample>(Root(json), "dataType").ShouldBeNull();
    }
}
