using ErsatzTV.Core.Domain;
using ErsatzTV.FFmpeg.State;
using ErsatzTV.Infrastructure.Data;
using ErsatzTV.Tests.Support;
using NUnit.Framework;
using Shouldly;

namespace ErsatzTV.Tests.Infrastructure;

[TestFixture]
public class DbInitializerChannelBugWatermarkTests
{
    private InMemoryTvContext _db = null!;

    [SetUp]
    public async Task SetUp() => _db = await InMemoryTvContext.CreateAsync();

    [TearDown]
    public async Task TearDown() => await _db.DisposeAsync();

    [Test]
    public async Task Initialize_Should_Seed_Channel_Bug_Watermark()
    {
        await using TvContext context = _db.CreateContext();

        await DbInitializer.Initialize(context, CancellationToken.None);

        ChannelWatermark watermark = context.ChannelWatermarks.Single(w => w.Name == "Channel Bug");
        watermark.Mode.ShouldBe(ChannelWatermarkMode.Permanent);
        watermark.ImageSource.ShouldBe(ChannelWatermarkImageSource.ChannelLogo);
        watermark.Image.ShouldBeNull();
        watermark.Location.ShouldBe(WatermarkLocation.TopLeft);
        watermark.Size.ShouldBe(WatermarkSize.Scaled);
        watermark.WidthPercent.ShouldBe(5.0);
        watermark.HorizontalMarginPercent.ShouldBe(1.0);
        watermark.VerticalMarginPercent.ShouldBe(1.0);
        watermark.Opacity.ShouldBe(80);
        watermark.ZIndex.ShouldBe(0);
        watermark.PlaceWithinSourceContent.ShouldBeFalse();
    }

    // The production instance already has a hand-made "Channel Bug" row with tuned geometry.
    // Seeding must adopt it untouched, never overwrite it and never duplicate it.
    [Test]
    public async Task Initialize_Should_Adopt_Existing_Channel_Bug_Watermark_Untouched()
    {
        await using TvContext context = _db.CreateContext();
        await context.ChannelWatermarks.AddAsync(
            new ChannelWatermark
            {
                Name = "Channel Bug",
                Mode = ChannelWatermarkMode.Intermittent,
                ImageSource = ChannelWatermarkImageSource.ChannelLogo,
                Location = WatermarkLocation.BottomRight,
                Size = WatermarkSize.Scaled,
                WidthPercent = 12,
                HorizontalMarginPercent = 3,
                VerticalMarginPercent = 4,
                Opacity = 55,
                FrequencyMinutes = 10,
                DurationSeconds = 20
            });
        await context.SaveChangesAsync();

        await DbInitializer.Initialize(context, CancellationToken.None);

        ChannelWatermark watermark = context.ChannelWatermarks.Single(w => w.Name == "Channel Bug");
        watermark.Mode.ShouldBe(ChannelWatermarkMode.Intermittent);
        watermark.Location.ShouldBe(WatermarkLocation.BottomRight);
        watermark.WidthPercent.ShouldBe(12);
        watermark.Opacity.ShouldBe(55);
    }

    [Test]
    public async Task Initialize_Should_Be_Idempotent()
    {
        await using TvContext context = _db.CreateContext();

        await DbInitializer.Initialize(context, CancellationToken.None);
        await DbInitializer.Initialize(context, CancellationToken.None);

        context.ChannelWatermarks.Count(w => w.Name == "Channel Bug").ShouldBe(1);
    }

    // No IsSystem flag exists on ChannelWatermark, and Initialize runs on every startup, so without
    // a seed marker a deliberate delete would be undone forever.
    [Test]
    public async Task Initialize_Should_Not_Resurrect_A_Deleted_Preset()
    {
        await using TvContext context = _db.CreateContext();
        await DbInitializer.Initialize(context, CancellationToken.None);

        ChannelWatermark seeded = context.ChannelWatermarks.Single(w => w.Name == "Channel Bug");
        context.ChannelWatermarks.Remove(seeded);
        await context.SaveChangesAsync();

        await DbInitializer.Initialize(context, CancellationToken.None);

        context.ChannelWatermarks.Any(w => w.Name == "Channel Bug").ShouldBeFalse();
    }

    // The production sequence is ADOPT (an existing hand-made row) and then, possibly, delete —
    // not seed-then-delete. The marker is written on the adopt path too, so this must not resurrect.
    [Test]
    public async Task Initialize_Should_Not_Resurrect_An_Adopted_Preset_After_Deletion()
    {
        await using TvContext context = _db.CreateContext();
        await context.ChannelWatermarks.AddAsync(
            new ChannelWatermark
            {
                Name = "Channel Bug",
                Mode = ChannelWatermarkMode.Permanent,
                ImageSource = ChannelWatermarkImageSource.ChannelLogo,
                Location = WatermarkLocation.BottomRight,
                Size = WatermarkSize.Scaled,
                WidthPercent = 12,
                Opacity = 55
            });
        await context.SaveChangesAsync();

        // First run adopts the existing row and records the marker.
        await DbInitializer.Initialize(context, CancellationToken.None);

        ChannelWatermark adopted = context.ChannelWatermarks.Single(w => w.Name == "Channel Bug");
        context.ChannelWatermarks.Remove(adopted);
        await context.SaveChangesAsync();

        await DbInitializer.Initialize(context, CancellationToken.None);

        context.ChannelWatermarks.Any(w => w.Name == "Channel Bug").ShouldBeFalse();
    }

    [Test]
    public async Task Initialize_Should_Stamp_The_Preset_On_Freshly_Seeded_Templates()
    {
        await using TvContext context = _db.CreateContext();

        await DbInitializer.Initialize(context, CancellationToken.None);

        ChannelWatermark watermark = context.ChannelWatermarks.Single(w => w.Name == "Channel Bug");
        List<ChannelTemplate> templates = context.ChannelTemplates.Where(t => t.IsSystem).ToList();

        // Assert the collection is non-empty FIRST: a bare foreach over zero rows passes vacuously,
        // so this test would stay green if template seeding silently bailed out.
        templates.Count.ShouldBe(2);
        foreach (ChannelTemplate template in templates)
        {
            template.WatermarkId.ShouldBe(watermark.Id);
        }
    }
}
