using ErsatzTV.Core.Iptv;
using NUnit.Framework;
using Shouldly;

namespace ErsatzTV.Core.Tests.Iptv;

[TestFixture]
public class AdvertisedBaseUrlTests
{
    private const string RequestScheme = "http";
    private const string RequestHost = "ersatztv:8409";
    private const string RequestBaseUrl = "";

    [Test]
    public void Resolve_Uses_Configured_Value_When_Set()
    {
        (string scheme, string host, string baseUrl) = AdvertisedBaseUrl.Resolve(
            "https://tv.example.com",
            RequestScheme,
            RequestHost,
            RequestBaseUrl);

        scheme.ShouldBe("https");
        host.ShouldBe("tv.example.com");
        baseUrl.ShouldBe("");
    }

    [TestCase(null)]
    [TestCase("")]
    [TestCase("   ")]
    public void Resolve_Falls_Back_To_Request_When_Blank(string configured)
    {
        (string scheme, string host, string baseUrl) = AdvertisedBaseUrl.Resolve(
            configured,
            RequestScheme,
            RequestHost,
            RequestBaseUrl);

        scheme.ShouldBe(RequestScheme);
        host.ShouldBe(RequestHost);
        baseUrl.ShouldBe(RequestBaseUrl);
    }

    [Test]
    public void Resolve_Falls_Back_To_Request_When_Invalid()
    {
        (string scheme, string host, string baseUrl) = AdvertisedBaseUrl.Resolve(
            "not a url",
            RequestScheme,
            RequestHost,
            RequestBaseUrl);

        scheme.ShouldBe(RequestScheme);
        host.ShouldBe(RequestHost);
        baseUrl.ShouldBe(RequestBaseUrl);
    }

    [Test]
    public void TryParse_Preserves_Non_Default_Port()
    {
        (string scheme, string host, string baseUrl) = AdvertisedBaseUrl.TryParse("http://192.168.1.99:8409").IfNone(("", "", ""));

        scheme.ShouldBe("http");
        host.ShouldBe("192.168.1.99:8409");
        baseUrl.ShouldBe("");
    }

    [TestCase("http://tv.example.com:80", "http", "tv.example.com")]
    [TestCase("https://tv.example.com:443", "https", "tv.example.com")]
    public void TryParse_Drops_Redundant_Default_Port(string configured, string expectedScheme, string expectedHost)
    {
        (string scheme, string host, string baseUrl) = AdvertisedBaseUrl.TryParse(configured).IfNone(("", "", ""));

        scheme.ShouldBe(expectedScheme);
        host.ShouldBe(expectedHost);
        baseUrl.ShouldBe("");
    }

    [Test]
    public void TryParse_Preserves_Path_Prefix()
    {
        (string _, string _, string baseUrl) = AdvertisedBaseUrl.TryParse("https://tv.example.com/etv").IfNone(("", "", ""));
        baseUrl.ShouldBe("/etv");
    }

    [TestCase("https://tv.example.com/", "")]
    [TestCase("https://tv.example.com/etv/", "/etv")]
    [TestCase("https://tv.example.com/etv//", "/etv")]
    public void TryParse_Normalizes_Trailing_Slash(string configured, string expectedBaseUrl)
    {
        (string _, string _, string baseUrl) = AdvertisedBaseUrl.TryParse(configured).IfNone(("", "", ""));
        baseUrl.ShouldBe(expectedBaseUrl);
    }

    [Test]
    public void TryParse_Trims_Whitespace()
    {
        Option<(string Scheme, string Host, string BaseUrl)> result =
            AdvertisedBaseUrl.TryParse("  https://tv.example.com/etv  ");

        result.IsSome.ShouldBeTrue();
        result.IfNone(("", "", "")).BaseUrl.ShouldBe("/etv");
    }

    [Test]
    public void TryParse_Handles_IPv6_Host()
    {
        (string _, string host, string _) = AdvertisedBaseUrl.TryParse("http://[2001:db8::1]:8409").IfNone(("", "", ""));
        host.ShouldBe("[2001:db8::1]:8409");
    }

    [TestCase(null)]
    [TestCase("")]
    [TestCase("   ")]
    [TestCase("not a url")]
    [TestCase("tv.example.com")] // no scheme (relative)
    [TestCase("//tv.example.com")] // protocol-relative (not absolute)
    [TestCase("ftp://tv.example.com")] // non-http(s) scheme
    [TestCase("ws://tv.example.com")] // non-http(s) scheme
    [TestCase("http://user:pass@tv.example.com")] // credentials
    [TestCase("http://tv.example.com?foo=bar")] // query
    [TestCase("http://tv.example.com/etv?foo=bar")] // query on a path
    [TestCase("http://tv.example.com#frag")] // fragment
    [TestCase("http://")] // no host
    public void TryParse_Rejects_Invalid_Input(string configured) =>
        AdvertisedBaseUrl.TryParse(configured).IsNone.ShouldBeTrue();
}
