using TeleWave.Domain.Library;
using TeleWave.Domain.Programming;

namespace TeleWave.Application.Programming.Templates.Generate;

/// <summary>
/// Каталог профилей сетки. Списаны с реальных каналов: у каждого свой ритм — длина блока, что
/// стоит в прайме, чем закрыта ночь. Это данные, а не алгоритм: чтобы добавить профиль, достаточно
/// описать полосы суток.
///
/// Полосы каждого профиля обязаны покрывать сутки целиком (06:00 → 06:00 следующих) — генератор
/// раскладывает по ним свободное время, и незакрытая полосой минута останется дырой в сетке.
/// </summary>
public static class GridProfiles
{
    // Праздничная сетка одна и та же у всех профилей, поэтому она навешивается здесь, а не
    // повторяется шесть раз в описаниях: профиль отличают будни, а не Новый год.
    public static IReadOnlyList<GridProfile> All { get; } =
    [.. Profiles().Select(profile => profile with { Season = Holidays() })];

    private static IEnumerable<GridProfile> Profiles()
    {
        yield return Mixed();
        yield return Animation();
        yield return Sitcom();
        yield return Music();
        yield return Movies();
        yield return Kids();
    }

    public static GridProfile Get(GridProfileKind kind) =>
        All.FirstOrDefault(p => p.Kind == kind) ?? All[0];

    // Названия полос, встречающиеся у нескольких профилей. Полосы — данные, и повторяющийся
    // заголовок должен быть один: он попадает в название слота и виден админу в сетке.
    private const string Morning = "Утро";
    private const string Day = "День";
    private const string DayBlock = "Дневной блок";
    private const string DayMarathon = "Дневной марафон";
    private const string Prime = "Прайм";
    private const string Night = "Ночь";
    private const string NightAir = "Ночной эфир";
    private const string MorningMarathon = "Утренний марафон";
    private const string GoodNight = "Спокойной ночи";
    private const string SignOffTitle = "Конец вещания";
    private const string PrimeMovie = "Кино в прайм";

    private static TimeOnly At(int hour, int minute = 0) => new(hour, minute);

    /// <summary>Ночной взрослый блок — общая концовка суток почти у всех профилей.</summary>
    private static GridBand AdultNight(TimeOnly from, TimeOnly to) =>
        new(
            from,
            to,
            Daypart.Night,
            "Ночной блок 18+",
            MinAudience: ShowAudience.R,
            BlockMinutes: 180,
            Strategy: SlotStrategyType.RandomWithCooldown,
            CooldownDays: 7
        );

    /// <summary>
    /// Праздничная сетка. Одна на все профили намеренно: в новогодние дни каналы сходятся к одному
    /// и тому же — днём марафон, вечером кино, — и различает их библиотека, а не ритм суток.
    ///
    /// Период пересекает Новый год, поэтому он ежегодный, а не диапазон дат: заводить слой заново
    /// каждый декабрь админ не должен.
    /// </summary>
    private static GridSeason Holidays() =>
        new(
            "Праздничная неделя",
            new AnnualRange(12, 25, 1, 8),
            [
                new(
                    At(6),
                    At(10),
                    Daypart.Morning,
                    "Праздничное утро",
                    ShowKind.Series,
                    ShowAudience.Pg,
                    BlockMinutes: 120,
                    UnitsPerBlock: 4
                ),
                new(
                    At(10),
                    At(16),
                    Daypart.Day,
                    "Праздничный марафон",
                    ShowKind.Series,
                    ShowAudience.Pg13,
                    BlockMinutes: 180
                ),
                new(
                    At(16),
                    At(19),
                    Daypart.Day,
                    "Семейное кино",
                    ShowKind.Single,
                    ShowAudience.Pg,
                    BlockMinutes: 150,
                    UnitsPerBlock: 1,
                    Strategy: SlotStrategyType.RandomWithCooldown,
                    CooldownDays: 21
                ),
                new(
                    At(19),
                    At(23),
                    Daypart.Prime,
                    "Праздничный прайм",
                    ShowKind.Single,
                    ShowAudience.Pg13,
                    BlockMinutes: 150,
                    UnitsPerBlock: 1,
                    Strategy: SlotStrategyType.RandomWithCooldown,
                    CooldownDays: 21,
                    Rotate: true
                ),
                AdultNight(At(23), At(2)),
                new(At(2), At(6), Daypart.Night, NightAir, BlockMinutes: 240),
            ]
        );

    private static GridProfile Mixed() =>
        new(
            GridProfileKind.Mixed,
            "Универсальная сетка",
            "как у обычного эфирного канала",
            "Сериалы днём, полнометражка в прайм, взрослое после полуночи. Подходит, если "
                + "библиотека смешанная и канал ни на чём не специализируется.",
            [
                new(
                    At(6),
                    At(10),
                    Daypart.Morning,
                    Morning,
                    ShowKind.Series,
                    ShowAudience.Pg13,
                    BlockMinutes: 120,
                    UnitsPerBlock: 3
                ),
                new(
                    At(10),
                    At(14),
                    Daypart.Day,
                    Day,
                    ShowKind.Series,
                    ShowAudience.Pg13,
                    BlockMinutes: 120,
                    UnitsPerBlock: 3
                ),
                new(
                    At(14),
                    At(18),
                    Daypart.Day,
                    DayBlock,
                    ShowKind.Series,
                    ShowAudience.Pg13,
                    BlockMinutes: 120,
                    UnitsPerBlock: 3,
                    Rotate: true
                ),
                new(
                    At(18),
                    At(20),
                    Daypart.Prime,
                    "Ранний прайм",
                    ShowKind.Series,
                    ShowAudience.Pg13,
                    BlockMinutes: 120,
                    UnitsPerBlock: 2
                ),
                new(
                    At(20),
                    At(23),
                    Daypart.Prime,
                    Prime,
                    ShowKind.Single,
                    BlockMinutes: 150,
                    UnitsPerBlock: 1,
                    Strategy: SlotStrategyType.RandomWithCooldown,
                    CooldownDays: 14,
                    Rotate: true
                ),
                AdultNight(At(23), At(2)),
                new(At(2), At(6), Daypart.Night, NightAir, BlockMinutes: 240),
            ],
            [
                new(
                    At(6),
                    At(10),
                    Daypart.Morning,
                    "Утро выходного",
                    ShowKind.Series,
                    ShowAudience.Pg,
                    BlockMinutes: 120,
                    UnitsPerBlock: 4
                ),
                new(
                    At(10),
                    At(14),
                    Daypart.Day,
                    DayMarathon,
                    ShowKind.Series,
                    ShowAudience.Pg13,
                    BlockMinutes: 240
                ),
                new(
                    At(14),
                    At(18),
                    Daypart.Day,
                    "Дневное кино",
                    ShowKind.Single,
                    ShowAudience.Pg13,
                    BlockMinutes: 120,
                    UnitsPerBlock: 1,
                    Strategy: SlotStrategyType.RandomWithCooldown,
                    CooldownDays: 14
                ),
                new(
                    At(18),
                    At(23),
                    Daypart.Prime,
                    PrimeMovie,
                    ShowKind.Single,
                    BlockMinutes: 150,
                    UnitsPerBlock: 1,
                    Strategy: SlotStrategyType.RandomWithCooldown,
                    CooldownDays: 21,
                    Rotate: true
                ),
                AdultNight(At(23), At(2)),
                new(At(2), At(6), Daypart.Night, NightAir, BlockMinutes: 240),
            ]
        );

    private static GridProfile Animation() =>
        new(
            GridProfileKind.Animation,
            "Мультсериалы и аниме",
            "как у 2×2",
            "Длинные блоки по 4–5 серий подряд, утром — повтор вчерашнего вечера, после 23:00 "
                + "взрослый блок. Нужен запас серий: сетка ест их быстро.",
            [
                new(
                    At(6),
                    At(10),
                    Daypart.Morning,
                    "Утренний повтор",
                    SlotKind: SlotKind.Repeat,
                    BlockMinutes: 0,
                    RepeatDaysAgo: 1,
                    RepeatFrom: At(19)
                ),
                new(
                    At(10),
                    At(14),
                    Daypart.Day,
                    DayBlock,
                    ShowKind.Series,
                    ShowAudience.Pg13,
                    BlockMinutes: 120,
                    UnitsPerBlock: 4
                ),
                new(
                    At(14),
                    At(19),
                    Daypart.Day,
                    DayMarathon,
                    ShowKind.Series,
                    ShowAudience.Pg13,
                    BlockMinutes: 150,
                    UnitsPerBlock: 5,
                    Rotate: true
                ),
                new(
                    At(19),
                    At(23),
                    Daypart.Prime,
                    Prime,
                    ShowKind.Series,
                    ShowAudience.Pg13,
                    BlockMinutes: 120,
                    UnitsPerBlock: 4
                ),
                AdultNight(At(23), At(2)),
                new(At(2), At(6), Daypart.Night, Night, ShowKind.Series, BlockMinutes: 240),
            ],
            [
                new(
                    At(6),
                    At(12),
                    Daypart.Morning,
                    MorningMarathon,
                    ShowKind.Series,
                    ShowAudience.Pg13,
                    BlockMinutes: 180,
                    UnitsPerBlock: 6
                ),
                new(
                    At(12),
                    At(19),
                    Daypart.Day,
                    DayMarathon,
                    ShowKind.Series,
                    ShowAudience.Pg13,
                    BlockMinutes: 210
                ),
                new(
                    At(19),
                    At(23),
                    Daypart.Prime,
                    Prime,
                    ShowKind.Series,
                    ShowAudience.Pg13,
                    BlockMinutes: 120,
                    UnitsPerBlock: 4
                ),
                AdultNight(At(23), At(3)),
                new(At(3), At(6), Daypart.Night, Night, ShowKind.Series, BlockMinutes: 180),
            ]
        );

    private static GridProfile Sitcom() =>
        new(
            GridProfileKind.Sitcom,
            "Ситкомы",
            "как у Paramount Comedy",
            "Получасовые серии парами весь день — между парами рекламный шов, как в настоящем "
                + "ситкомовском эфире. Вечером комедийная полнометражка, ночью взрослый юмор.",
            [
                new(
                    At(6),
                    At(12),
                    Daypart.Morning,
                    "Утренние ситкомы",
                    ShowKind.Series,
                    ShowAudience.Pg13,
                    BlockMinutes: 60,
                    UnitsPerBlock: 2
                ),
                new(
                    At(12),
                    At(18),
                    Daypart.Day,
                    "Дневные ситкомы",
                    ShowKind.Series,
                    ShowAudience.Pg13,
                    BlockMinutes: 60,
                    UnitsPerBlock: 2,
                    Rotate: true
                ),
                new(
                    At(18),
                    At(22),
                    Daypart.Prime,
                    "Вечерние ситкомы",
                    ShowKind.Series,
                    ShowAudience.Pg13,
                    BlockMinutes: 60,
                    UnitsPerBlock: 2
                ),
                new(
                    At(22),
                    At(0),
                    Daypart.Prime,
                    "Комедия в прайм",
                    ShowKind.Single,
                    BlockMinutes: 120,
                    UnitsPerBlock: 1,
                    Strategy: SlotStrategyType.RandomWithCooldown,
                    CooldownDays: 14,
                    Rotate: true
                ),
                AdultNight(At(0), At(6)),
            ],
            [
                new(
                    At(6),
                    At(14),
                    Daypart.Morning,
                    "Марафон ситкома",
                    ShowKind.Series,
                    ShowAudience.Pg13,
                    BlockMinutes: 240
                ),
                new(
                    At(14),
                    At(20),
                    Daypart.Day,
                    "Дневные ситкомы",
                    ShowKind.Series,
                    ShowAudience.Pg13,
                    BlockMinutes: 60,
                    UnitsPerBlock: 2
                ),
                new(
                    At(20),
                    At(0),
                    Daypart.Prime,
                    "Комедийное кино",
                    ShowKind.Single,
                    BlockMinutes: 120,
                    UnitsPerBlock: 1,
                    Strategy: SlotStrategyType.RandomWithCooldown,
                    CooldownDays: 21
                ),
                AdultNight(At(0), At(6)),
            ]
        );

    private static GridProfile Music() =>
        new(
            GridProfileKind.Music,
            "Ротация",
            "как у MTV",
            "Длинные полосы без жёсткой структуры: случайный выбор с остыванием, блоки по 3–4 часа. "
                + "Для клипов, коротких роликов и реалити.",
            [
                new(
                    At(6),
                    At(10),
                    Daypart.Morning,
                    "Утренняя ротация",
                    BlockMinutes: 240,
                    Strategy: SlotStrategyType.RandomWithCooldown,
                    CooldownDays: 2
                ),
                new(
                    At(10),
                    At(14),
                    Daypart.Day,
                    "Дневная ротация",
                    BlockMinutes: 240,
                    Strategy: SlotStrategyType.RandomWithCooldown,
                    CooldownDays: 2
                ),
                new(
                    At(14),
                    At(18),
                    Daypart.Day,
                    DayBlock,
                    BlockMinutes: 240,
                    Strategy: SlotStrategyType.RandomWithCooldown,
                    CooldownDays: 2,
                    Rotate: true
                ),
                new(
                    At(18),
                    At(22),
                    Daypart.Prime,
                    "Вечерний блок",
                    BlockMinutes: 240,
                    Strategy: SlotStrategyType.RandomWithCooldown,
                    CooldownDays: 3
                ),
                new(
                    At(22),
                    At(2),
                    Daypart.Night,
                    "Ночная ротация",
                    BlockMinutes: 240,
                    Strategy: SlotStrategyType.RandomWithCooldown,
                    CooldownDays: 1
                ),
                new(
                    At(2),
                    At(6),
                    Daypart.Night,
                    Night,
                    BlockMinutes: 240,
                    Strategy: SlotStrategyType.RandomWithCooldown,
                    CooldownDays: 1
                ),
            ],
            []
        );

    private static GridProfile Movies() =>
        new(
            GridProfileKind.Movies,
            "Кинопоказ",
            "как у киноканалов",
            "Двухчасовые слоты под полнометражки круглые сутки, в прайме — длиннее, ночью строже "
                + "по рейтингу. Сериалы такой сеткой не показать.",
            [
                new(
                    At(6),
                    At(12),
                    Daypart.Morning,
                    "Утреннее кино",
                    ShowKind.Single,
                    ShowAudience.Pg13,
                    BlockMinutes: 120,
                    UnitsPerBlock: 1,
                    Strategy: SlotStrategyType.RandomWithCooldown,
                    CooldownDays: 21
                ),
                new(
                    At(12),
                    At(18),
                    Daypart.Day,
                    "Дневное кино",
                    ShowKind.Single,
                    ShowAudience.Pg13,
                    BlockMinutes: 120,
                    UnitsPerBlock: 1,
                    Strategy: SlotStrategyType.RandomWithCooldown,
                    CooldownDays: 21
                ),
                new(
                    At(18),
                    At(23),
                    Daypart.Prime,
                    PrimeMovie,
                    ShowKind.Single,
                    BlockMinutes: 150,
                    UnitsPerBlock: 1,
                    Strategy: SlotStrategyType.RandomWithCooldown,
                    CooldownDays: 30,
                    Rotate: true
                ),
                new(
                    At(23),
                    At(2),
                    Daypart.Night,
                    "Ночное кино 18+",
                    ShowKind.Single,
                    MinAudience: ShowAudience.R,
                    BlockMinutes: 150,
                    UnitsPerBlock: 1,
                    Strategy: SlotStrategyType.RandomWithCooldown,
                    CooldownDays: 14
                ),
                new(
                    At(2),
                    At(6),
                    Daypart.Night,
                    NightAir,
                    ShowKind.Single,
                    BlockMinutes: 120,
                    UnitsPerBlock: 1,
                    Strategy: SlotStrategyType.RandomWithCooldown,
                    CooldownDays: 7
                ),
            ],
            []
        );

    private static GridProfile Kids() =>
        new(
            GridProfileKind.Kids,
            "Детский",
            "как у детских каналов",
            "Только мягкий рейтинг, короткие блоки по 3 серии, вечером «спокойной ночи», "
                + "с 22:00 до утра — конец вещания вместо ночного эфира.",
            [
                new(
                    At(6),
                    At(10),
                    Daypart.Morning,
                    Morning,
                    ShowKind.Series,
                    ShowAudience.Pg,
                    BlockMinutes: 90,
                    UnitsPerBlock: 3
                ),
                new(
                    At(10),
                    At(14),
                    Daypart.Day,
                    Day,
                    ShowKind.Series,
                    ShowAudience.Pg,
                    BlockMinutes: 90,
                    UnitsPerBlock: 3
                ),
                new(
                    At(14),
                    At(18),
                    Daypart.Day,
                    DayBlock,
                    ShowKind.Series,
                    ShowAudience.Pg,
                    BlockMinutes: 90,
                    UnitsPerBlock: 3,
                    Rotate: true
                ),
                new(
                    At(18),
                    At(21),
                    Daypart.Prime,
                    "Вечерний блок",
                    ShowKind.Series,
                    ShowAudience.Pg,
                    BlockMinutes: 90,
                    UnitsPerBlock: 3
                ),
                new(
                    At(21),
                    At(22),
                    Daypart.Prime,
                    GoodNight,
                    ShowKind.Series,
                    ShowAudience.Pg,
                    BlockMinutes: 60,
                    UnitsPerBlock: 2
                ),
                new(
                    At(22),
                    At(6),
                    Daypart.Night,
                    SignOffTitle,
                    SlotKind: SlotKind.SignOff,
                    BlockMinutes: 0
                ),
            ],
            [
                new(
                    At(6),
                    At(10),
                    Daypart.Morning,
                    MorningMarathon,
                    ShowKind.Series,
                    ShowAudience.Pg,
                    BlockMinutes: 240
                ),
                new(
                    At(10),
                    At(14),
                    Daypart.Day,
                    Day,
                    ShowKind.Series,
                    ShowAudience.Pg,
                    BlockMinutes: 120,
                    UnitsPerBlock: 4
                ),
                new(
                    At(14),
                    At(18),
                    Daypart.Day,
                    DayBlock,
                    ShowKind.Series,
                    ShowAudience.Pg,
                    BlockMinutes: 120,
                    UnitsPerBlock: 4
                ),
                new(
                    At(18),
                    At(21),
                    Daypart.Prime,
                    "Семейное кино",
                    ShowKind.Single,
                    ShowAudience.Pg,
                    BlockMinutes: 180,
                    UnitsPerBlock: 1,
                    Strategy: SlotStrategyType.RandomWithCooldown,
                    CooldownDays: 21
                ),
                new(
                    At(21),
                    At(22),
                    Daypart.Prime,
                    GoodNight,
                    ShowKind.Series,
                    ShowAudience.Pg,
                    BlockMinutes: 60,
                    UnitsPerBlock: 2
                ),
                new(
                    At(22),
                    At(6),
                    Daypart.Night,
                    SignOffTitle,
                    SlotKind: SlotKind.SignOff,
                    BlockMinutes: 0
                ),
            ]
        );
}
