using ClawdDotNet.Tools.SocialMediaManager;
using Shouldly;

namespace ClawdDotNet.Tools.Tests.SocialMedia;

/// <summary>
/// S2 aus der Bestandsaufnahme: Die Kanal-/Video-Angabe wurde ungeprüft in eine
/// Argument-Zeichenkette für yt-dlp interpoliert. UseShellExecute=false verhindert
/// Shell-Metazeichen, nicht aber Options-Injection — yt-dlp kennt --exec, das beliebige
/// Befehle ausführt.
///
/// Die Angriffsfälle bleiben hier dauerhaft als Testfälle dokumentiert.
/// </summary>
public sealed class YouTubeUrlTests
{
    // ═══════════════════════════════════════════════════════════
    //  Angriffe
    // ═══════════════════════════════════════════════════════════

    [Theory]
    [InlineData("--exec cmd.exe https://www.youtube.com/@kanal")]
    [InlineData("--exec=calc.exe")]
    [InlineData("-o /tmp/evil")]
    [InlineData("--config-location /tmp/evil.conf")]
    [InlineData("--paths /windows/system32")]
    [InlineData("-")]
    public void Optionsartige_Eingaben_werden_abgelehnt(string input)
    {
        YouTubeUrl.TryResolveChannelUrl(input, out _, out var error).ShouldBeFalse();
        error.ShouldNotBeNull();
    }

    [Theory]
    [InlineData("https://evil.com/video")]
    [InlineData("https://youtube.com.attacker.net/@kanal")]
    [InlineData("https://notyoutube.com/@kanal")]
    [InlineData("http://192.168.178.10:8418/Richard/ClawdDotNet.git")]
    [InlineData("file:///C:/Windows/win.ini")]
    [InlineData("ftp://example.com/datei")]
    public void Fremde_Hosts_und_Schemata_werden_abgelehnt(string url)
    {
        YouTubeUrl.TryResolveChannelUrl(url, out _, out var error).ShouldBeFalse();
        error.ShouldNotBeNull();
    }

    [Theory]
    [InlineData("kanal name")]           // Leerzeichen
    [InlineData("kanal\"name")]          // Anführungszeichen
    [InlineData("kanal\nname")]          // Zeilenumbruch
    [InlineData("kanal;name")]
    [InlineData("kanal&name")]
    [InlineData("../../etc/passwd")]
    [InlineData("@kanal/../../evil")]
    public void Unsaubere_Handles_werden_abgelehnt(string input)
    {
        YouTubeUrl.TryResolveChannelUrl(input, out _, out var error).ShouldBeFalse();
        error.ShouldNotBeNull();
    }

    [Theory]
    [InlineData(null)]
    [InlineData("")]
    [InlineData("   ")]
    public void Leere_Eingaben_werden_abgelehnt(string? input)
    {
        YouTubeUrl.TryResolveChannelUrl(input, out _, out _).ShouldBeFalse();
    }

    // ═══════════════════════════════════════════════════════════
    //  Gegenproben — legitime Eingaben müssen weiter funktionieren
    // ═══════════════════════════════════════════════════════════

    [Theory]
    [InlineData("@Computerphile", "https://www.youtube.com/@Computerphile/videos")]
    [InlineData("Computerphile", "https://www.youtube.com/@Computerphile/videos")]
    [InlineData("kanal_mit-punkt.name", "https://www.youtube.com/@kanal_mit-punkt.name/videos")]
    public void Handles_werden_zu_Kanal_URLs(string input, string expected)
    {
        YouTubeUrl.TryResolveChannelUrl(input, out var url, out var error).ShouldBeTrue(error);
        url.ShouldBe(expected);
    }

    [Theory]
    [InlineData("https://www.youtube.com/watch?v=dQw4w9WgXcQ")]
    [InlineData("https://youtube.com/@kanal/videos")]
    [InlineData("https://m.youtube.com/watch?v=abc")]
    [InlineData("https://music.youtube.com/watch?v=abc")]
    [InlineData("https://youtu.be/dQw4w9WgXcQ")]
    public void Echte_YouTube_URLs_werden_durchgelassen(string url)
    {
        YouTubeUrl.TryResolveChannelUrl(url, out var resolved, out var error).ShouldBeTrue(error);
        resolved.ShouldBe(url);
    }

    // ═══════════════════════════════════════════════════════════
    //  Argumentbildung
    // ═══════════════════════════════════════════════════════════

    [Fact]
    public void Die_URL_steht_hinter_dem_Optionsende_Trenner()
    {
        // "--" beendet die Optionsliste. Selbst wenn ein Wert wie eine Option aussähe,
        // würde yt-dlp ihn danach als Adresse behandeln.
        var args = YouTubeUrl.BuildLatestVideoIdArgs("https://www.youtube.com/@kanal/videos");

        var separator = args.IndexOf("--");
        separator.ShouldBeGreaterThanOrEqualTo(0);
        args[^1].ShouldBe("https://www.youtube.com/@kanal/videos");
        args.IndexOf(args[^1]).ShouldBeGreaterThan(separator);
    }

    [Fact]
    public void Jedes_Argument_ist_ein_eigener_Eintrag()
    {
        // Entscheidend: Kein Eintrag darf mehrere Argumente enthalten — sonst könnten
        // sie beim Start wieder aufgetrennt werden.
        var args = YouTubeUrl.BuildAudioDownloadArgs(
            @"C:\Pfad mit Leerzeichen\datei.mp3",
            "https://www.youtube.com/watch?v=abc",
            @"C:\ffmpeg\bin",
            convertToMp3: true);

        args.ShouldContain(@"C:\Pfad mit Leerzeichen\datei.mp3");
        args.ShouldContain(@"C:\ffmpeg\bin");
        args.ShouldContain("--audio-format");
        args.ShouldContain("mp3");
        args[^1].ShouldBe("https://www.youtube.com/watch?v=abc");
    }

    [Fact]
    public void Ohne_ffmpeg_entfaellt_die_Konvertierung()
    {
        var args = YouTubeUrl.BuildAudioDownloadArgs(
            "/tmp/x.%(ext)s", "https://youtu.be/abc", null, convertToMp3: false);

        args.ShouldNotContain("--audio-format");
        args.ShouldNotContain("--ffmpeg-location");
    }
}
