using TeleWave.Application.Broadcast.Bumpers;
using TeleWave.Application.Programming.Templates;
using TeleWave.Application.Programming.Templates.Junctions;
using TeleWave.Domain.Broadcast;
using TeleWave.Domain.Programming;
using Xunit;

namespace TeleWave.Application.Tests.Programming;

/// <summary>
/// Валидаторы стыков и заставок. Проверяют границы, за которыми настройка перестаёт что-либо
/// значить в эфире: врезка длиннее суток, допуск от часа шире самого часа, семь строк в кадре.
/// </summary>
public class JunctionBumperValidatorTests
{
    private static JunctionElementInput Element(
        int amount = 1,
        int choiceWeight = 1,
        string? choiceKey = null,
        JunctionConditions? conditions = null
    ) =>
        new(
            JunctionElementKind.Ad,
            Guid.NewGuid(),
            null,
            null,
            JunctionAmountMode.Count,
            amount,
            IsRequired: false,
            choiceKey,
            choiceWeight,
            conditions
        );

    private static bool Valid(JunctionElementInput input) =>
        new UpdateJunctionElementCommandValidator()
            .Validate(new UpdateJunctionElementCommand(Guid.NewGuid(), Guid.NewGuid(), input))
            .IsValid;

    [Fact]
    public void Element_AcceptsUsualSettings()
    {
        var conditions = new JunctionConditions(
            MinMinutesBetween: 30,
            Chance: 40,
            NearHourMinutes: 2
        );

        Assert.True(
            Valid(Element(amount: 3, choiceWeight: 5, choiceKey: "fork", conditions: conditions))
        );
        // Условий может не быть вовсе — тогда проверять нечего.
        Assert.True(Valid(Element()));
    }

    [Theory]
    [InlineData(0)] // ноль врезок — это отсутствие врезки, а не настройка
    [InlineData(24 * 60 + 1)] // длиннее вещательных суток
    public void Element_RejectsAmountOutOfRange(int amount)
    {
        Assert.False(Valid(Element(amount)));
    }

    [Theory]
    [InlineData(-1)]
    [InlineData(1001)]
    public void Element_RejectsWeightOutOfRange(int weight)
    {
        Assert.False(Valid(Element(choiceWeight: weight)));
    }

    [Theory]
    [InlineData(101)] // вероятность выше ста
    [InlineData(-1)]
    public void Element_RejectsChanceOutOfRange(int chance)
    {
        Assert.False(Valid(Element(conditions: new JunctionConditions(Chance: chance))));
    }

    [Fact]
    public void Element_RejectsNearHourWiderThanHalfHour()
    {
        // Допуск в 31 минуту покрывает час целиком — это уже не привязка к :00.
        Assert.False(Valid(Element(conditions: new JunctionConditions(NearHourMinutes: 31))));
        Assert.False(Valid(Element(conditions: new JunctionConditions(NearHourMinutes: -1))));
    }

    [Fact]
    public void Junction_ChecksNameAndCap()
    {
        var validator = new UpdateJunctionCommandValidator();
        var id = Guid.NewGuid();

        Assert.True(
            validator.Validate(new UpdateJunctionCommand(id, "Рекламный шов", 120)).IsValid
        );
        // Потолок можно не задавать — тогда стык ограничен только якорем.
        Assert.True(validator.Validate(new UpdateJunctionCommand(id, "Шов", null)).IsValid);
        Assert.False(validator.Validate(new UpdateJunctionCommand(id, " ", 120)).IsValid);
        Assert.False(validator.Validate(new UpdateJunctionCommand(id, "Шов", 0)).IsValid);
        Assert.False(
            validator.Validate(new UpdateJunctionCommand(id, "Шов", 24 * 60 * 60 + 1)).IsValid
        );
        Assert.False(
            new CreateJunctionCommandValidator().Validate(new CreateJunctionCommand("")).IsValid
        );
    }

    private static bool ValidVariant(int weight, params string[] lines) =>
        new UpdateBumperVariantCommandValidator()
            .Validate(
                new UpdateBumperVariantCommand(
                    Guid.NewGuid(),
                    Guid.NewGuid(),
                    new BumperVariantInput(
                        "Подблок",
                        BumperTrigger.Both,
                        BumperBackground.Template,
                        weight,
                        [
                            .. lines.Select(text => new BumperLineDto(
                                BumperLineStyle.Title,
                                BumperLineColor.Text,
                                text
                            )),
                        ]
                    )
                )
            )
            .IsValid;

    [Fact]
    public void Variant_AcceptsUpToSixLines()
    {
        Assert.True(ValidVariant(1, "СЕЙЧАС", "{now.title}"));
        Assert.True(ValidVariant(0, "1", "2", "3", "4", "5", "6"));
    }

    [Fact]
    public void Variant_RejectsSeventhLine()
    {
        // Больше шести строк в кадр не помещается ни при каком размере шрифта.
        Assert.False(ValidVariant(1, "1", "2", "3", "4", "5", "6", "7"));
    }

    [Fact]
    public void Variant_RejectsEmptyLineAndBadWeight()
    {
        Assert.False(ValidVariant(1, " "));
        Assert.False(ValidVariant(1001, "СЕЙЧАС"));
        Assert.False(ValidVariant(-1, "СЕЙЧАС"));
    }
}
