namespace TeleWave.Domain.Programming;

/// <summary>Что за врезка стоит в стыке.</summary>
public enum JunctionElementKind
{
    /// <summary>Реклама — ролики или готовые рекламные блоки из группы.</summary>
    Ad = 0,

    /// <summary>Анонс: «смотрите в четверг».</summary>
    Promo = 1,

    /// <summary>ТВ-заставка «Сейчас — Далее»: ассет рендерится под конкретную пару шоу.</summary>
    Bumper = 2,

    /// <summary>Заполнитель: закрывает остаток, когда врезки не добрали до цели.</summary>
    Filler = 3,
}

/// <summary>Чем меряется врезка.</summary>
public enum JunctionAmountMode
{
    /// <summary>Ровно N единиц. Предсказуемо по числу, но не по времени.</summary>
    Count = 0,

    /// <summary>
    /// Пока не наберётся M минут. Нужен для смешанных групп, где рядом лежат и целые рекламные
    /// блоки, и отдельные ролики: «одна единица» там означает то ли 20 секунд, то ли три минуты.
    /// </summary>
    Duration = 1,
}

/// <summary>
/// Полный набор настроек врезки. Собран в запись, потому что по отдельности эти девять значений
/// ехали бы через команду, хендлер и домен плоским списком, в котором ничего не читается.
/// </summary>
public sealed record JunctionElementSettings(
    JunctionElementKind Kind,
    Guid? GroupId,
    Guid? BumperTemplateId,
    Guid? BumperVariantId,
    JunctionAmountMode AmountMode,
    int AmountValue,
    bool IsRequired,
    string? ChoiceKey,
    int ChoiceWeight,
    string? ConditionsJson
);

/// <summary>
/// Врезка в шаблоне стыка. Условия показа хранятся структурно (<see cref="ConditionsJson"/>),
/// а не выражением: парсер, его валидация и отдельный UI обошлись бы дорого, а покрывают ровно
/// те же три-четыре реальных случая.
/// </summary>
public class JunctionElement
{
    public Guid Id { get; private set; }
    public Guid JunctionTemplateId { get; private set; }

    /// <summary>Порядок показа в эфире — и только он: обязательность на порядок не влияет.</summary>
    public int Position { get; private set; }

    public JunctionElementKind Kind { get; private set; }

    /// <summary>Откуда брать единицы — для <see cref="JunctionElementKind.Ad"/>, <see cref="JunctionElementKind.Promo"/>, <see cref="JunctionElementKind.Filler"/>.</summary>
    public Guid? GroupId { get; private set; }

    /// <summary>Какой блок заставки рендерить — для <see cref="JunctionElementKind.Bumper"/>.</summary>
    public Guid? BumperTemplateId { get; private set; }

    /// <summary>Конкретный подблок заставки; null — выбрать по триггеру перехода и весам.</summary>
    public Guid? BumperVariantId { get; private set; }

    public JunctionAmountMode AmountMode { get; private set; }

    /// <summary>Единиц либо минут.</summary>
    public int AmountValue { get; private set; }

    /// <summary>Обязательную врезку нельзя выбросить при нехватке времени.</summary>
    public bool IsRequired { get; private set; }

    /// <summary>
    /// Метка развилки: из врезок с одной меткой в эфир идёт одна, выбранная по весам. Врезки одной
    /// развилки обязаны занимать непрерывный отрезок позиций — иначе неясно, куда встаёт выбранная.
    /// </summary>
    public string? ChoiceKey { get; private set; }

    /// <summary>Вес внутри развилки (0 — не выбирается). Вне развилки не используется.</summary>
    public int ChoiceWeight { get; private set; }

    /// <summary>Условия показа (JSON). Домен их не интерпретирует — схема живёт в Application.</summary>
    public string? ConditionsJson { get; private set; }

    public const int DefaultChoiceWeight = 1;

    private JunctionElement() { }

    internal static JunctionElement Create(
        Guid junctionTemplateId,
        int position,
        JunctionElementKind kind
    ) =>
        new()
        {
            Id = Guid.NewGuid(),
            JunctionTemplateId = junctionTemplateId,
            Position = position,
            Kind = kind,
            AmountMode = JunctionAmountMode.Count,
            AmountValue = 1,
            IsRequired = false,
            ChoiceWeight = DefaultChoiceWeight,
        };

    public void Update(JunctionElementSettings settings)
    {
        Kind = settings.Kind;
        AmountMode = settings.AmountMode;
        AmountValue = Math.Max(1, settings.AmountValue);
        IsRequired = settings.IsRequired;
        ChoiceKey = string.IsNullOrWhiteSpace(settings.ChoiceKey)
            ? null
            : settings.ChoiceKey.Trim();
        ChoiceWeight = Math.Max(0, settings.ChoiceWeight);
        ConditionsJson = string.IsNullOrWhiteSpace(settings.ConditionsJson)
            ? null
            : settings.ConditionsJson;

        // Источник зависит от типа врезки: у заставки нет группы, у остальных нет блока заставки.
        // Оставленная от прежнего типа ссылка потом читалась бы генератором как настройка.
        var isBumper = settings.Kind == JunctionElementKind.Bumper;
        GroupId = isBumper ? null : settings.GroupId;
        BumperTemplateId = isBumper ? settings.BumperTemplateId : null;
        BumperVariantId = isBumper ? settings.BumperVariantId : null;
    }

    /// <summary>Перевесить врезку в другую развилку (или вынести из неё) — это делает перетаскивание.</summary>
    public void SetChoice(string? choiceKey, int weight)
    {
        ChoiceKey = string.IsNullOrWhiteSpace(choiceKey) ? null : choiceKey.Trim();
        ChoiceWeight = Math.Max(0, weight);
    }

    internal void SetPosition(int position) => Position = position;
}
