using System.Runtime.CompilerServices;
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Iptv;
using NUnit.Framework;
using Shouldly;

namespace ErsatzTV.Core.Tests.Iptv;

// Golden-file tests that lock the M3U output shape Jellyfin/Kodi consume (ChannelPlaylist.ToM3U).
// Baselines capture TODAY's behaviour so a regression — e.g. while implementing the paused #1
// (tvg-logo base URL) — fails CI instead of only surfacing as Jellyfin misbehaving in prod.
//
// Goldens live next to this file under Goldens/ and are located via [CallerFilePath], so they
// resolve from the checked-out source in CI and locally without embedded-resource plumbing.
// To regenerate after an INTENTIONAL output change, run the (explicit) Regenerate_goldens test
// or set ETV_UPDATE_GOLDENS=1, then review and commit the diff.
[TestFixture]
public class ChannelPlaylistGoldenTests
{
    private const string Scheme = "https";
    private const string Host = "tv.example.com";
    private const string BaseUrl = "";

    [SetUp]
    public void GuardVolatileEnvironment() =>
        // tvg-id (ChannelIdentifier.FromNumber) appends SystemEnvironment.InstanceId when set;
        // the goldens assume it is unset (the CI/default case). Skip rather than falsely fail.
        Assume.That(
            string.IsNullOrEmpty(SystemEnvironment.InstanceId),
            "ETV_INSTANCE_ID is set; M3U goldens assume it is unset.");

    [Test]
    public void Simple_channel_generated_logo() =>
        Verify(
            "simple-generated-logo.m3u",
            new ChannelPlaylist(
                Scheme,
                Host,
                BaseUrl,
                [BuildChannel(1, "1", "News", StreamingMode.HttpLiveStreamingDirect)],
                userAgent: "VLC/3.0",
                accessToken: null));

    [Test]
    public void Channel_with_internal_logo() =>
        Verify(
            "internal-logo.m3u",
            new ChannelPlaylist(
                Scheme,
                Host,
                BaseUrl,
                [WithLogo(BuildChannel(2, "2", "Movies", StreamingMode.HttpLiveStreamingSegmenter), "abc123def")],
                userAgent: "VLC/3.0",
                accessToken: null));

    [Test]
    public void Channel_with_external_logo() =>
        Verify(
            "external-logo.m3u",
            new ChannelPlaylist(
                Scheme,
                Host,
                BaseUrl,
                [WithLogo(BuildChannel(3, "3", "Music", StreamingMode.TransportStream), "https://cdn.example.com/music.png")],
                userAgent: "VLC/3.0",
                accessToken: null));

    [Test]
    public void Multi_channel_sorted_by_number() =>
        Verify(
            "multi-channel.m3u",
            new ChannelPlaylist(
                Scheme,
                Host,
                BaseUrl,
                [
                    BuildChannel(10, "10", "Ten", StreamingMode.HttpLiveStreamingDirect),
                    BuildChannel(2, "2", "Two", StreamingMode.TransportStreamHybrid),
                    BuildChannel(1, "1.5", "OnePointFive", StreamingMode.HttpLiveStreamingSegmenter)
                ],
                userAgent: "VLC/3.0",
                accessToken: null));

    [Test]
    public void Kodi_user_agent_adds_kodiprops() =>
        Verify(
            "kodi-user-agent.m3u",
            new ChannelPlaylist(
                Scheme,
                Host,
                BaseUrl,
                [BuildChannel(4, "4", "Kids", StreamingMode.TransportStream)],
                userAgent: "Kodi/21.0",
                accessToken: null));

    [Test]
    public void With_access_token_and_base_url() =>
        Verify(
            "access-token-base-url.m3u",
            new ChannelPlaylist(
                Scheme,
                Host,
                baseUrl: "/etv",
                [WithLogo(BuildChannel(5, "5", "Docs", StreamingMode.HttpLiveStreamingDirect), "logo5")],
                userAgent: "VLC/3.0",
                accessToken: "SECRET-TOKEN"));

    // --- helpers ---

    private static Channel BuildChannel(int seed, string number, string name, StreamingMode mode) =>
        new(new Guid($"00000000-0000-0000-0000-{seed:D12}"))
        {
            Number = number,
            Name = name,
            Group = "ErsatzTV",
            StreamingMode = mode,
            Artwork = [],
            FFmpegProfile = new FFmpegProfile
            {
                VideoFormat = FFmpegProfileVideoFormat.H264,
                AudioFormat = FFmpegProfileAudioFormat.Aac
            }
        };

    private static Channel WithLogo(Channel channel, string path)
    {
        channel.Artwork = [new Artwork { ArtworkKind = ArtworkKind.Logo, Path = path }];
        return channel;
    }

    private static void Verify(string goldenName, ChannelPlaylist playlist)
    {
        string actual = playlist.ToM3U().ReplaceLineEndings("\n");
        string path = Path.Combine(GoldenDir(), goldenName);

        if (Environment.GetEnvironmentVariable("ETV_UPDATE_GOLDENS") == "1")
        {
            Directory.CreateDirectory(GoldenDir());
            File.WriteAllText(path, actual);
            Assert.Inconclusive($"Wrote golden '{goldenName}'. Review it and re-run to verify.");
            return;
        }

        // A missing golden is a hard failure (not a silent skip) so an un-committed baseline
        // can't pass CI. Create it with the Regenerate_goldens test or ETV_UPDATE_GOLDENS=1.
        File.Exists(path).ShouldBeTrue(
            $"Missing golden '{goldenName}'. Run Regenerate_goldens (or ETV_UPDATE_GOLDENS=1) and commit it.");

        string expected = File.ReadAllText(path).ReplaceLineEndings("\n");
        actual.ShouldBe(expected);
    }

    [Test]
    [Explicit("Regenerates all M3U goldens from current output; review the diff before committing.")]
    public void Regenerate_goldens()
    {
        Environment.SetEnvironmentVariable("ETV_UPDATE_GOLDENS", "1");
        try
        {
            Simple_channel_generated_logo();
            Channel_with_internal_logo();
            Channel_with_external_logo();
            Multi_channel_sorted_by_number();
            Kodi_user_agent_adds_kodiprops();
            With_access_token_and_base_url();
        }
        catch (InconclusiveException)
        {
            // expected — each Verify writes its golden then reports inconclusive
        }
        finally
        {
            Environment.SetEnvironmentVariable("ETV_UPDATE_GOLDENS", null);
        }
    }

    private static string GoldenDir([CallerFilePath] string thisFile = "") =>
        Path.Combine(Path.GetDirectoryName(thisFile) ?? ".", "Goldens");
}
