using TeleWave.Application.Programming.Templates.Transfer;
using TeleWave.Domain.Programming;
using Xunit;

namespace TeleWave.Application.Tests.Programming;

/// <summary>
/// Валидаторы обмена конфигурацией. Файл приходит извне — от другой установки или от языковой
/// модели, — поэтому проверка формата и объёма здесь единственная преграда между чужим JSON
/// и сеткой канала.
/// </summary>
public class TransferValidatorTests
{
    private static GridConfigSlot Slot() =>
        new("Вечерний блок", new TimeOnly(20, 0), 120, Daypart.Prime, Group: "Сериалы");

    private static GridConfig Config(params GridConfigLayer[] layers) => new(layers);

    private static GridConfig Valid() =>
        Config(new GridConfigLayer("Основная сетка", 10, [Slot()]));

    [Fact]
    public void Import_AcceptsNormalFile()
    {
        var command = new ImportGridCommand(Guid.NewGuid(), Valid(), Replace: true);

        Assert.True(new ImportGridCommandValidator().Validate(command).IsValid);
    }

    [Fact]
    public void Import_RejectsForeignFormat()
    {
        // Чужой формат с теми же полями разобрался бы во что-то бессмысленное — лучше отказать.
        var config = Valid() with
        {
            Format = "some.other.format",
        };

        Assert.False(Validate(config));
    }

    [Fact]
    public void Import_RejectsFutureVersion()
    {
        var config = Valid() with { Version = GridConfig.CurrentVersion + 1 };

        Assert.False(Validate(config));
    }

    [Fact]
    public void Import_RejectsEmptyAndNamelessLayers()
    {
        Assert.False(Validate(Config()));
        Assert.False(Validate(Config(new GridConfigLayer(" ", 10, [Slot()]))));
    }

    [Fact]
    public void Import_RejectsOversizedFile()
    {
        // Потолки — про объём: недельная сетка это десятки слотов, а не десятки тысяч.
        var layers = Enumerable
            .Range(0, 65)
            .Select(i => new GridConfigLayer($"Слой {i}", 10, [Slot()]))
            .ToArray();

        Assert.False(Validate(Config(layers)));
    }

    [Fact]
    public void Import_RejectsTooManyGroupsAndCollections()
    {
        var groups = Enumerable
            .Range(0, 257)
            .Select(i => new GridConfigGroup($"Группа {i}"))
            .ToList();
        var collections = Enumerable
            .Range(0, 257)
            .Select(i => new GridConfigCollection($"Цикл {i}", ["Шоу"]))
            .ToList();

        Assert.False(Validate(Valid() with { Groups = groups }));
        Assert.False(Validate(Valid() with { Collections = collections }));
    }

    [Fact]
    public void Prompt_AcceptsReferencesAndNotes()
    {
        var query = new BuildGridPromptQuery(Guid.NewGuid(), ["2×2"], "утром детское");

        Assert.True(new BuildGridPromptQueryValidator().Validate(query).IsValid);
    }

    [Fact]
    public void Prompt_RejectsTooManyReferencesAndEmptyOnes()
    {
        var validator = new BuildGridPromptQueryValidator();
        var many = Enumerable.Range(0, 17).Select(i => $"Канал {i}").ToList();

        Assert.False(
            validator.Validate(new BuildGridPromptQuery(Guid.NewGuid(), many, null)).IsValid
        );
        Assert.False(
            validator.Validate(new BuildGridPromptQuery(Guid.NewGuid(), [" "], null)).IsValid
        );
    }

    [Fact]
    public void Prompt_RejectsHugeNotes()
    {
        var notes = new string('x', 4001);

        Assert.False(
            new BuildGridPromptQueryValidator()
                .Validate(new BuildGridPromptQuery(Guid.NewGuid(), [], notes))
                .IsValid
        );
    }

    private static bool Validate(GridConfig config) =>
        new ImportGridCommandValidator()
            .Validate(new ImportGridCommand(Guid.NewGuid(), config, Replace: false))
            .IsValid;
}
