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

namespace FrameProcessor.Tests;

public class FrameNameTests
{
    [Theory]
    [InlineData("living-room")]
    [InlineData("kitchen")]
    [InlineData("Bedroom_2")]
    [InlineData("frame.1")]
    [InlineData("a~b")]
    [InlineData("ABC")]
    [InlineData("0")]
    [InlineData("A-Za-z0-9-._~")]
    public void TryParse_AcceptsUrlSafeNames(string input)
    {
        Assert.True(FrameName.TryParse(input, out var name));
        Assert.Equal(input, name.Value);
        Assert.Equal(input, name.ToString());
    }

    [Theory]
    [InlineData("")]
    [InlineData(" ")]
    [InlineData("living room")]            // space
    [InlineData("living/room")]            // reserved
    [InlineData("living?room")]            // reserved
    [InlineData("living#room")]            // reserved
    [InlineData("living%20room")]          // percent
    [InlineData("café")]                   // non-ASCII
    [InlineData("\tname")]                 // whitespace
    [InlineData("name\n")]                 // newline
    public void TryParse_RejectsInvalidNames(string input)
    {
        Assert.False(FrameName.TryParse(input, out _));
    }

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

    [Fact]
    public void Parse_Throws_OnBadInput()
    {
        var ex = Assert.Throws<FormatException>(() => FrameName.Parse("bad name"));
        Assert.Contains("bad name", ex.Message);
        Assert.Contains("A-Z", ex.Message);
    }

    [Fact]
    public void Equality_IsValueWise()
    {
        Assert.True(FrameName.TryParse("living-room", out var a));
        Assert.True(FrameName.TryParse("living-room", out var b));
        Assert.True(FrameName.TryParse("kitchen", out var c));

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

    [Fact]
    public void JsonRoundTrip_PreservesValue()
    {
        var json = "\"living-room\"";
        var name = JsonSerializer.Deserialize<FrameName>(json);
        var written = JsonSerializer.Serialize(name);

        Assert.Equal("living-room", name.Value);
        Assert.Equal(json, written);
    }

    [Fact]
    public void JsonDeserialize_BadInput_Throws()
    {
        Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<FrameName>("\"bad name\""));
    }

    [Fact]
    public void IParsable_Hookup_Works()
    {
        var name = FrameName.Parse("living-room", provider: null);
        Assert.Equal("living-room", name.Value);

        Assert.True(FrameName.TryParse("living-room", provider: null, out var name2));
        Assert.Equal(name, name2);
    }

    [Fact]
    public void Default_HasEmptyValue()
    {
        FrameName name = default;
        Assert.Equal(string.Empty, name.Value);
        Assert.Equal(string.Empty, name.ToString());
    }
}
