using System.Text.Json;
using FrameProcessor.Domain;

namespace FrameProcessor.Tests;

public class MacAddressTests
{
    private static readonly byte[] ExpectedBytes = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF];
    private const string Canonical = "aabbccddeeff";

    [Theory]
    [InlineData("AA:BB:CC:DD:EE:FF")]
    [InlineData("aa:bb:cc:dd:ee:ff")]
    [InlineData("AA-BB-CC-DD-EE-FF")]
    [InlineData("aa-bb-cc-dd-ee-ff")]
    [InlineData("AABBCCDDEEFF")]
    [InlineData("aabbccddeeff")]
    [InlineData("  AA:BB:CC:DD:EE:FF  ")]
    [InlineData("\tAA-bb-CC-dd-EE-ff\n")]
    public void TryParse_AcceptedForms_ProduceCanonicalString(string input)
    {
        Assert.True(MacAddress.TryParse(input, out var mac));
        Assert.Equal(Canonical, mac.ToString());
    }

    [Fact]
    public void Equality_IsByteWise_AcrossInputForms()
    {
        Assert.True(MacAddress.TryParse("AA:BB:CC:DD:EE:FF", out var a));
        Assert.True(MacAddress.TryParse("aa-bb-cc-dd-ee-ff", out var b));
        Assert.True(MacAddress.TryParse("AABBCCDDEEFF", out var c));

        Assert.Equal(a, b);
        Assert.Equal(b, c);
        Assert.Equal(a.GetHashCode(), b.GetHashCode());
    }

    [Fact]
    public void Equality_DiffersOnAnySingleByte()
    {
        Assert.True(MacAddress.TryParse("aabbccddeeff", out var a));
        Assert.True(MacAddress.TryParse("aabbccddeefe", out var b));

        Assert.NotEqual(a, b);
    }

    [Theory]
    [InlineData("")]
    [InlineData("   ")]
    [InlineData("not-a-mac")]
    [InlineData("AA:BB:CC:DD:EE")]              // too short
    [InlineData("AA:BB:CC:DD:EE:FF:00")]        // too long
    [InlineData("ZZ:BB:CC:DD:EE:FF")]           // non-hex digit
    [InlineData("AA:BB:CC:DD:EE:F")]            // odd hex count
    [InlineData("AA BB CC DD EE FF")]           // unsupported space separator
    public void TryParse_RejectsBadInput(string input)
    {
        Assert.False(MacAddress.TryParse(input, out _));
    }

    [Fact]
    public void TryParse_Null_ReturnsFalse()
    {
        Assert.False(MacAddress.TryParse((string?)null, out _));
    }

    [Fact]
    public void Parse_Throws_OnBadInput()
    {
        Assert.Throws<FormatException>(() => MacAddress.Parse("nope"));
    }

    [Fact]
    public void Constructor_FromBytes_RoundTripsThroughToString()
    {
        var mac = new MacAddress(ExpectedBytes);
        Assert.Equal(Canonical, mac.ToString());
    }

    [Fact]
    public void Constructor_FromWrongLength_Throws()
    {
        Assert.Throws<ArgumentException>(() => new MacAddress(new byte[] { 0x00 }));
        Assert.Throws<ArgumentException>(() => new MacAddress(new byte[7]));
    }

    [Fact]
    public void JsonRoundTrip_NormalizesToCanonicalForm()
    {
        var json = "\"AA:BB:CC:DD:EE:FF\"";
        var mac = JsonSerializer.Deserialize<MacAddress>(json);
        var written = JsonSerializer.Serialize(mac);

        Assert.Equal($"\"{Canonical}\"", written);
    }

    [Fact]
    public void JsonDeserialize_BadInput_Throws()
    {
        Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<MacAddress>("\"not-a-mac\""));
    }

    [Fact]
    public void IParsable_Hookup_Works()
    {
        var mac = MacAddress.Parse("AA:BB:CC:DD:EE:FF", provider: null);
        Assert.Equal(Canonical, mac.ToString());

        Assert.True(MacAddress.TryParse("AA:BB:CC:DD:EE:FF", provider: null, out var mac2));
        Assert.Equal(mac, mac2);
    }
}
