using System.Text.Json;
using System.Text.Json.Serialization;
using TeleWave.Domain.Programming;

namespace TeleWave.Application.Programming.Templates.Transfer;

/// <summary>
/// Слот в формате обмена. Группы и стыки — **по именам**, а не по идентификаторам: файл переносится
/// между каналами и установками, где тех же GUID нет, а сетку для импорта пишет в том числе ИИ,
/// который идентификаторы может только выдумать.
/// </summary>
public sealed record GridConfigSlot(
    string Title,
    TimeOnly Start,
    int DurationMinutes,
    Daypart Daypart = Daypart.Day,
    SlotKind Kind = SlotKind.Content,
    /// <summary>0=Вс..6=Сб; null — слот идёт каждый день.</summary>
    int? Weekday = null,
    /// <summary>Имя группы — обязательно для слотов вида Content.</summary>
    string? Group = null,
    SlotStrategy? Strategy = null,
    RepeatSource? Repeat = null,
    SlotBlockMode BlockMode = SlotBlockMode.FillSlot,
    int BlockValue = 1,
    OverflowPolicy Overflow = OverflowPolicy.ContinueNext,
    bool IsAnchor = false,
    int MaxDriftMinutes = Slot.DefaultMaxDriftMinutes,
    int? SnapToMinutes = null,
    /// <summary>Имя стыка между единицами блока.</summary>
    string? JunctionBetween = null,
    /// <summary>Имя стыка в конце блока; null — стык шаблона по умолчанию.</summary>
    string? JunctionAfter = null
);

/// <summary>Слой сетки в формате обмена.</summary>
public sealed record GridConfigLayer(
    string Name,
    int Priority,
    IReadOnlyList<GridConfigSlot> Slots,
    bool IsEnabled = true,
    /// <summary>Фоновый слой один и создаётся вместе с шаблоном — при импорте в него только пишутся слоты.</summary>
    bool IsBackground = false,
    LayerApplicability? Applicability = null
);

/// <summary>
/// Коллекция в формате обмена: франшиза или цикл, собранный из шоу по названиям. Нужна там, где
/// в слот ставится не одно шоу, а серия фильмов целиком.
/// </summary>
public sealed record GridConfigCollection(
    string Name,
    IReadOnlyList<string> Shows,
    string? Description = null
);

/// <summary>
/// Группа в формате обмена: список шоу и коллекций по названиям. Слот ссылается на группу, поэтому
/// собрать сетку под библиотеку, где групп ещё нет, можно только вместе с ними — импорт заводит
/// недостающие сам.
/// </summary>
public sealed record GridConfigGroup(
    string Name,
    IReadOnlyList<string>? Shows = null,
    IReadOnlyList<string>? Collections = null,
    string? Description = null
);

/// <summary>Параметры канала — переносятся справочно: импорт их не применяет.</summary>
public sealed record GridConfigChannel(string Name, TimeOnly DayStart, int UtcOffsetMinutes);

/// <summary>
/// Конфигурация сетки канала одним файлом: слои, слоты, правила отбора и ссылки на группы и стыки
/// по именам. Формат один и тот же на выгрузку, загрузку и на то, что просят сгенерировать ИИ, —
/// иначе схема в запросе однажды разойдётся с тем, что читает импорт.
/// </summary>
public sealed record GridConfig(
    IReadOnlyList<GridConfigLayer> Layers,
    string Format = GridConfig.CurrentFormat,
    int Version = GridConfig.CurrentVersion,
    GridConfigChannel? Channel = null,
    PlanningRules? Rules = null,
    /// <summary>Имя аварийной группы — ею закрываются паузы между слотами.</summary>
    string? FallbackGroup = null,
    /// <summary>Имя стыка по умолчанию.</summary>
    string? DefaultJunction = null,
    /// <summary>Коллекции, которых может не быть в библиотеке: импорт заводит недостающие.</summary>
    IReadOnlyList<GridConfigCollection>? Collections = null,
    /// <summary>Группы, на которые ссылаются слоты. Существующие по имени не трогаются.</summary>
    IReadOnlyList<GridConfigGroup>? Groups = null
)
{
    public const string CurrentFormat = "telewave.grid";
    public const int CurrentVersion = 1;

    public static readonly JsonSerializerOptions Options = new()
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        PropertyNameCaseInsensitive = true,
        DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
        Converters = { new JsonStringEnumConverter() },
        WriteIndented = true,
    };

    public string ToJson() => JsonSerializer.Serialize(this, Options);
}
