using System.IO.Abstractions;
using Testably.Abstractions.Testing;
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Infrastructure.Data;
using ErsatzTV.Infrastructure.Streaming.Graphics;
using ErsatzTV.Tests.Support;
using Microsoft.EntityFrameworkCore;
using NUnit.Framework;
using Shouldly;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
using ErsatzTV.Core.Graphics;

namespace ErsatzTV.Tests.Infrastructure;

[TestFixture]
public class GraphicsElementSeederTests
{
    private InMemoryTvContext _db = null!;
    private string _seededPath = null!;

    [SetUp]
    public async Task SetUp()
    {
        _db = await InMemoryTvContext.CreateAsync();
        _seededPath = Path.Combine(FileSystemLayout.GraphicsElementsTextTemplatesFolder, "on-now-next.yml");
    }

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

    [Test]
    public async Task Seeds_File_And_Marker_When_Absent()
    {
        var fs = new MockFileSystem();
        await using TvContext context = _db.CreateContext();

        await GraphicsElementSeeder.SeedOnNowNext(context, fs, CancellationToken.None);

        fs.File.Exists(_seededPath).ShouldBeTrue();
        fs.File.ReadAllText(_seededPath).ShouldContain("epg_entries: 2");
        (await context.ConfigElements.AnyAsync(c => c.Key == ConfigElementKey.GraphicsOnNowNextSeeded.Key)).ShouldBeTrue();
    }

    [Test]
    public async Task Adopts_Existing_File_Untouched()
    {
        var fs = new MockFileSystem();
        fs.Directory.CreateDirectory(FileSystemLayout.GraphicsElementsTextTemplatesFolder);
        await fs.File.WriteAllTextAsync(_seededPath, "name: Operator Custom\nepg_entries: 2\n");
        await using TvContext context = _db.CreateContext();

        await GraphicsElementSeeder.SeedOnNowNext(context, fs, CancellationToken.None);

        fs.File.ReadAllText(_seededPath).ShouldContain("Operator Custom");
    }

    [Test]
    public async Task Does_Not_Resurrect_After_Delete()
    {
        var fs = new MockFileSystem();
        await using TvContext context = _db.CreateContext();

        await GraphicsElementSeeder.SeedOnNowNext(context, fs, CancellationToken.None);
        fs.File.Delete(_seededPath);
        await GraphicsElementSeeder.SeedOnNowNext(context, fs, CancellationToken.None);

        fs.File.Exists(_seededPath).ShouldBeFalse();
    }

    // Guards the #570 render bugs: the seeded YAML must deserialize into a TextGraphicsElement whose
    // base_style names a real style and whose every style sets a font_family — a null font_family
    // crashes CustomFontMapper before its default-font fallback, so the overlay silently never renders.
    [Test]
    public async Task Seeded_Element_Deserializes_With_Resolvable_BaseStyle_And_Font_Families()
    {
        var fs = new MockFileSystem();
        await using TvContext context = _db.CreateContext();
        await GraphicsElementSeeder.SeedOnNowNext(context, fs, CancellationToken.None);

        string yaml = await fs.File.ReadAllTextAsync(_seededPath);
        IDeserializer deserializer = new DeserializerBuilder()
            .WithNamingConvention(CamelCaseNamingConvention.Instance)
            .IgnoreUnmatchedProperties()
            .Build();

        var element = deserializer.Deserialize<TextGraphicsElement>(yaml);

        element.ShouldNotBeNull();
        element.Styles.ShouldNotBeEmpty();
        element.Styles.Select(s => s.Name).ShouldContain(element.BaseStyle);
        element.Styles.ShouldAllBe(s => !string.IsNullOrWhiteSpace(s.FontFamily));
    }
}
