using TeleWave.Domain.Programming;
using Xunit;

namespace TeleWave.Domain.Tests.Programming;

public class ScheduleTemplateTests
{
    private static ScheduleTemplate NewTemplate() =>
        ScheduleTemplate.Create(Guid.NewGuid(), "Основная");

    [Fact]
    public void Create_AddsBackgroundLayer()
    {
        var template = NewTemplate();

        var background = Assert.Single(template.Layers);
        Assert.True(background.IsBackground);
        Assert.Equal(GridLayer.BackgroundPriority, background.Priority);
    }

    [Fact]
    public void RemoveLayer_RefusesBackground()
    {
        var template = NewTemplate();
        var background = template.Background!;

        Assert.False(template.RemoveLayer(background.Id));
        Assert.Single(template.Layers);
    }

    [Fact]
    public void AddLayer_KeepsPriorityAboveBackground()
    {
        var template = NewTemplate();

        var layer = template.AddLayer("Выходные", 0);

        // Ноль занят фоном: слой с таким же приоритетом сделал бы выбор неоднозначным.
        Assert.True(layer.Priority > GridLayer.BackgroundPriority);
    }

    [Fact]
    public void BackgroundLayer_KeepsPriority_WhenUpdated()
    {
        var template = NewTemplate();
        var background = template.Background!;

        background.Update("Фон", 99, null, isEnabled: true);

        Assert.Equal(GridLayer.BackgroundPriority, background.Priority);
    }

    [Fact]
    public void MarkChanged_MakesPendingUntilApplied()
    {
        var template = NewTemplate();
        Assert.False(template.HasPendingChanges);

        template.MarkChanged();
        Assert.True(template.HasPendingChanges);

        template.MarkApplied();
        Assert.False(template.HasPendingChanges);
    }
}

public class GridLayerOverlapTests
{
    private static GridLayer NewLayer() =>
        ScheduleTemplate.Create(Guid.NewGuid(), "T").AddLayer("Базовая", 10);

    [Fact]
    public void HasOverlap_DetectsIntersectionOnSameDay()
    {
        var layer = NewLayer();
        layer.AddSlot("Вечернее кино", new TimeOnly(20, 0), 90, weekday: 5);

        Assert.True(layer.HasOverlap(5, new TimeOnly(21, 0), 30, null));
        Assert.False(layer.HasOverlap(5, new TimeOnly(21, 30), 30, null));
    }

    [Fact]
    public void HasOverlap_IgnoresOtherWeekdays()
    {
        var layer = NewLayer();
        layer.AddSlot("Вечернее кино", new TimeOnly(20, 0), 90, weekday: 5);

        Assert.False(layer.HasOverlap(3, new TimeOnly(20, 30), 30, null));
    }

    [Fact]
    public void HasOverlap_EverydaySlotConflictsWithAnyDay()
    {
        var layer = NewLayer();
        layer.AddSlot("Новости", new TimeOnly(18, 0), 30);

        // Слот без дня недели идёт каждый день, поэтому конфликтует и с конкретным днём.
        Assert.True(layer.HasOverlap(2, new TimeOnly(18, 15), 30, null));
    }

    [Fact]
    public void HasOverlap_SkipsSlotBeingEdited()
    {
        var layer = NewLayer();
        var slot = layer.AddSlot("Вечернее кино", new TimeOnly(20, 0), 90, weekday: 5);

        Assert.False(layer.HasOverlap(5, new TimeOnly(20, 0), 120, slot.Id));
    }

    [Fact]
    public void UpdateContent_ClearsFieldsForeignToSlotKind()
    {
        var layer = NewLayer();
        var slot = layer.AddSlot("Кино", new TimeOnly(20, 0), 90);
        slot.UpdateContent(
            new SlotContent(
                "Кино",
                SlotKind.Content,
                Guid.NewGuid(),
                "{\"type\":\"sequential\"}",
                null,
                SlotBlockMode.Count,
                1,
                OverflowPolicy.ContinueNext
            )
        );

        slot.UpdateContent(
            new SlotContent(
                "Конец вещания",
                SlotKind.SignOff,
                Guid.NewGuid(),
                "{\"type\":\"sequential\"}",
                null,
                SlotBlockMode.FillSlot,
                1,
                OverflowPolicy.ContinueNext
            )
        );

        // Иначе генератор прочитал бы настройки, оставшиеся от прежнего типа слота.
        Assert.Null(slot.GroupId);
        Assert.Null(slot.StrategyJson);
    }
}
