using NSubstitute;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Programming.Groups;
using TeleWave.Application.Programming.Templates;
using TeleWave.Application.Programming.Templates.Generate;
using TeleWave.Application.Programming.Templates.Transfer;
using TeleWave.Application.Tests.Support;
using TeleWave.Domain.Broadcast;
using TeleWave.Domain.Programming;
using Xunit;

namespace TeleWave.Application.Tests.Programming;

/// <summary>
/// Обмен конфигурацией там, где обмениваться нечем: нет канала, нет шаблона, нет источника
/// метаданных. Всё это штатные исходы — админ открыл экран на канале, который ещё не настроен.
/// </summary>
public class TransferGuardsTests
{
    private static readonly DateTimeOffset T0 = new(2026, 1, 1, 0, 0, 0, TimeSpan.Zero);

    private static ExportGridQueryHandler Export(IAppDbContext db) =>
        new(db, GroupServices.Dynamic(db));

    [Fact]
    public async Task Export_FailsWithoutChannel()
    {
        var fixture = new TestDb();
        await using var db = fixture.New();

        var result = await Export(db)
            .Handle(new ExportGridQuery(Guid.NewGuid()), CancellationToken.None);

        Assert.False(result.IsSuccess);
    }

    [Fact]
    public async Task Export_FailsWithoutTemplate()
    {
        // Канал без сетки выгружать нечем — и это отдельная ошибка, а не пустой файл.
        var fixture = new TestDb();
        var channel = Channel.Create("Первый", "first", T0);

        await using (var seed = fixture.New())
        {
            seed.Channels.Add(channel);
            await seed.SaveChangesAsync(CancellationToken.None);
        }

        await using var db = fixture.New();
        var result = await Export(db)
            .Handle(new ExportGridQuery(channel.Id), CancellationToken.None);

        Assert.False(result.IsSuccess);
    }

    [Fact]
    public async Task Import_FailsWithoutTemplate()
    {
        var fixture = new TestDb();
        await using var db = fixture.New();

        var config = new GridConfig([new GridConfigLayer("Основная сетка", 10, [])]);
        var result = await new ImportGridCommandHandler(
            db,
            GroupServices.Slots(db),
            GroupServices.Stats(db),
            GroupServices.Interstitials(db)
        ).Handle(new ImportGridCommand(Guid.NewGuid(), config, false), CancellationToken.None);

        Assert.False(result.IsSuccess);
    }

    [Fact]
    public async Task Prompt_FailsWithoutChannel()
    {
        var fixture = new TestDb();
        await using var db = fixture.New();

        var result = await new BuildGridPromptQueryHandler(db, Catalog(db)).Handle(
            new BuildGridPromptQuery(Guid.NewGuid(), [], null),
            CancellationToken.None
        );

        Assert.False(result.IsSuccess);
    }

    [Fact]
    public async Task Prompt_SaysWhatIsMissing_OnEmptyLibrary()
    {
        // Пустая библиотека — не ошибка: запрос собирается, но честно говорит, что групп нет,
        // иначе модель придумает их сама и вернёт сетку под несуществующий контент.
        var fixture = new TestDb();
        var channel = Channel.Create("Первый", "first", T0);

        await using (var seed = fixture.New())
        {
            seed.Channels.Add(channel);
            await seed.SaveChangesAsync(CancellationToken.None);
        }

        await using var db = fixture.New();
        var result = await new BuildGridPromptQueryHandler(db, Catalog(db)).Handle(
            new BuildGridPromptQuery(channel.Id, [], null),
            CancellationToken.None
        );

        Assert.True(result.IsSuccess);
        Assert.Equal(0, result.Value.Groups);
        Assert.Equal(0, result.Value.Shows);
        Assert.Contains("Готовых групп нет", result.Value.Prompt, StringComparison.Ordinal);
        Assert.Contains("Библиотека пуста", result.Value.Prompt, StringComparison.Ordinal);
        // Стыков и заставок тоже нет — модель должна знать, что ссылаться не на что.
        Assert.Contains("стыков ещё нет", result.Value.Prompt, StringComparison.Ordinal);
    }

    private static GroupCatalog Catalog(IAppDbContext db) =>
        new(db, GroupServices.Dynamic(db), new GroupElementResolver(db));
}
