using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Driver.S7.Cli.Commands;

namespace ZB.MOM.WW.OtOpcUa.Driver.S7.Cli.Tests;

/// <summary>
///     Covers <see cref="WriteCommand.ParseValue"/> across every S7 atomic type.
/// </summary>
[Trait("Category", "Unit")]
public sealed class WriteCommandParseValueTests
{
    [Theory]
    [InlineData("true", true)]
    [InlineData("0", false)]
    [InlineData("yes", true)]
    [InlineData("OFF", false)]
    public void ParseValue_Bool_accepts_common_aliases(string raw, bool expected)
    {
        WriteCommand.ParseValue(raw, S7DataType.Bool).ShouldBe(expected);
    }

    [Fact]
    public void ParseValue_Bool_rejects_garbage()
    {
        Should.Throw<CliFx.Exceptions.CommandException>(
            () => WriteCommand.ParseValue("maybe", S7DataType.Bool));
    }

    [Fact]
    public void ParseValue_Byte_ranges()
    {
        WriteCommand.ParseValue("0", S7DataType.Byte).ShouldBe((byte)0);
        WriteCommand.ParseValue("255", S7DataType.Byte).ShouldBe((byte)255);
    }

    [Fact]
    public void ParseValue_Int16_signed_range()
    {
        WriteCommand.ParseValue("-32768", S7DataType.Int16).ShouldBe((short)-32768);
    }

    [Fact]
    public void ParseValue_UInt16_unsigned_max()
    {
        WriteCommand.ParseValue("65535", S7DataType.UInt16).ShouldBe((ushort)65535);
    }

    [Fact]
    public void ParseValue_Int32_parses_negative()
    {
        WriteCommand.ParseValue("-2147483648", S7DataType.Int32).ShouldBe(int.MinValue);
    }

    [Fact]
    public void ParseValue_UInt32_parses_max()
    {
        WriteCommand.ParseValue("4294967295", S7DataType.UInt32).ShouldBe(uint.MaxValue);
    }

    [Fact]
    public void ParseValue_Int64_parses_min()
    {
        WriteCommand.ParseValue("-9223372036854775808", S7DataType.Int64).ShouldBe(long.MinValue);
    }

    [Fact]
    public void ParseValue_UInt64_parses_max()
    {
        WriteCommand.ParseValue("18446744073709551615", S7DataType.UInt64).ShouldBe(ulong.MaxValue);
    }

    [Fact]
    public void ParseValue_Float32_invariant_culture()
    {
        WriteCommand.ParseValue("3.14", S7DataType.Float32).ShouldBe(3.14f);
    }

    [Fact]
    public void ParseValue_Float64_higher_precision()
    {
        WriteCommand.ParseValue("2.718281828", S7DataType.Float64).ShouldBeOfType<double>();
    }

    [Fact]
    public void ParseValue_String_passthrough()
    {
        WriteCommand.ParseValue("hallo siemens", S7DataType.String).ShouldBe("hallo siemens");
    }

    [Fact]
    public void ParseValue_DateTime_parses_roundtrip_form()
    {
        var result = WriteCommand.ParseValue("2026-04-21T12:34:56Z", S7DataType.DateTime);
        result.ShouldBeOfType<DateTime>();
        ((DateTime)result).Year.ShouldBe(2026);
    }

    [Fact]
    public void ParseValue_non_numeric_for_numeric_types_throws()
    {
        Should.Throw<FormatException>(
            () => WriteCommand.ParseValue("xyz", S7DataType.Int16));
    }

    [Theory]
    [InlineData("DB1.DBW0", S7DataType.Int16, "DB1.DBW0:Int16")]
    [InlineData("M0.0", S7DataType.Bool, "M0.0:Bool")]
    [InlineData("IW4", S7DataType.UInt16, "IW4:UInt16")]
    [InlineData("QD8", S7DataType.UInt32, "QD8:UInt32")]
    [InlineData("DB10.STRING[0]", S7DataType.String, "DB10.STRING[0]:String")]
    public void SynthesiseTagName_preserves_S7_address_verbatim(
        string address, S7DataType type, string expected)
    {
        ReadCommand.SynthesiseTagName(address, type).ShouldBe(expected);
    }
}
