using ClawdDotNet.Tools.WebFetch;
using Shouldly;

namespace ClawdDotNet.Tools.Tests.WebFetch;

/// <summary>
/// S5 aus der Bestandsaufnahme: Die Whitelist wurde nur auf die Ausgangs-URL angewendet,
/// HttpClient folgte Weiterleitungen aber selbst. Eine erlaubte Domain konnte damit ins
/// lokale Netz weiterleiten — Router, NAS, der eigene Git-Server, Cloud-Metadatendienste.
/// </summary>
public sealed class UrlGuardTests
{
    private static readonly string[] Allowed = ["example.com", "reuters.com"];

    private static bool Validate(string url, out string? error)
        => UrlGuard.TryValidate(url, Allowed, out _, out error);

    // ═══════════════════════════════════════════════════════════
    //  Netzsperren
    // ═══════════════════════════════════════════════════════════

    [Theory]
    [InlineData("http://169.254.169.254/latest/meta-data/")]   // Cloud-Metadaten
    [InlineData("http://127.0.0.1:8080/")]
    [InlineData("http://localhost:8418/")]
    [InlineData("http://192.168.178.10:8418/Richard/ClawdDotNet.git")]
    [InlineData("http://10.0.0.5/admin")]
    [InlineData("http://172.16.4.2/")]
    [InlineData("http://172.31.255.255/")]
    [InlineData("http://0.0.0.0/")]
    [InlineData("http://100.64.1.1/")]
    [InlineData("http://[::1]/")]
    [InlineData("http://[fd00::1]/")]
    [InlineData("http://nas.local/")]
    [InlineData("http://dienst.internal/")]
    public void Adressen_im_lokalen_oder_privaten_Netz_werden_gesperrt(string url)
    {
        Validate(url, out var error).ShouldBeFalse($"'{url}' muss gesperrt sein");
        error.ShouldNotBeNull();
    }

    [Theory]
    [InlineData("172.15.0.1")]   // knapp unterhalb von 172.16/12
    [InlineData("172.32.0.1")]   // knapp oberhalb
    [InlineData("11.0.0.1")]
    [InlineData("8.8.8.8")]
    public void Oeffentliche_Adressen_gelten_nicht_als_privat(string host)
    {
        UrlGuard.IsPrivateOrLocal(host).ShouldBeFalse();
    }

    // ═══════════════════════════════════════════════════════════
    //  Schema
    // ═══════════════════════════════════════════════════════════

    [Theory]
    [InlineData("file:///C:/Windows/win.ini")]
    [InlineData("ftp://example.com/datei")]
    [InlineData("gopher://example.com/")]
    public void Nur_http_und_https_sind_erlaubt(string url)
    {
        Validate(url, out _).ShouldBeFalse();
    }

    // ═══════════════════════════════════════════════════════════
    //  Whitelist
    // ═══════════════════════════════════════════════════════════

    [Theory]
    [InlineData("https://example.com/artikel")]
    [InlineData("https://www.example.com/artikel")]
    [InlineData("https://news.example.com/artikel")]
    [InlineData("https://a.b.example.com/x")]
    [InlineData("https://reuters.com/")]
    public void Erlaubte_Domains_und_ihre_Subdomains_gehen_durch(string url)
    {
        Validate(url, out var error).ShouldBeTrue(error);
    }

    [Theory]
    [InlineData("https://example.com.attacker.net/x")]
    [InlineData("https://notexample.com/x")]
    [InlineData("https://evil.com/x")]
    [InlineData("https://exampleXcom/x")]
    public void Aehnlich_aussehende_Domains_werden_abgelehnt(string url)
    {
        Validate(url, out _).ShouldBeFalse();
    }

    [Fact]
    public void Ohne_Whitelist_wird_nichts_durchgelassen()
    {
        UrlGuard.TryValidate("https://example.com", [], out _, out _).ShouldBeFalse();
    }

    /// <summary>
    /// Die alte Fassung entfernte "www." per Ersetzen über die ganze Zeichenkette:
    /// aus "mywww.example.com" wurde "myexample.com".
    /// </summary>
    [Fact]
    public void Die_www_Behandlung_verstuemmelt_keine_anderen_Hostnamen()
    {
        UrlGuard.IsDomainAllowed("mywww.example.com", Allowed).ShouldBeTrue();
        UrlGuard.IsDomainAllowed("wwwxexample.com", Allowed).ShouldBeFalse();
    }

    [Fact]
    public void Ein_abschliessender_Punkt_im_Host_umgeht_die_Whitelist_nicht()
    {
        // "example.com." ist DNS-technisch derselbe Host.
        UrlGuard.IsDomainAllowed("evil.com.", Allowed).ShouldBeFalse();
        UrlGuard.IsDomainAllowed("example.com.", Allowed).ShouldBeTrue();
    }

    [Fact]
    public void Grossschreibung_im_Host_spielt_keine_Rolle()
    {
        Validate("https://EXAMPLE.COM/x", out var error).ShouldBeTrue(error);
    }

    [Theory]
    [InlineData(null)]
    [InlineData("")]
    [InlineData("kein-url")]
    [InlineData("/nur/ein/pfad")]
    public void Ungueltige_Eingaben_werden_abgelehnt(string? url)
    {
        UrlGuard.TryValidate(url, Allowed, out _, out _).ShouldBeFalse();
    }
}
