namespace TeleWave.Domain.Broadcast;

/// <summary>
/// Канал линейного эфира. Что и когда идёт в эфире, определяет шаблон сетки (<see cref="TemplateId"/>,
/// см. <c>Domain/Programming</c>); канал хранит только собственные свойства: время, номер, аварийный
/// филлер и общие настройки заставок.
/// </summary>
public class Channel
{
    private readonly List<BumperTemplate> _bumperTemplates = [];

    public Guid Id { get; private set; }
    public string Name { get; private set; } = string.Empty;
    public string Slug { get; private set; } = string.Empty;
    public bool IsEnabled { get; private set; }

    /// <summary>Точка отсчёта эфирной ленты (UTC) — база для MEDIA-SEQUENCE на этапе раздачи.</summary>
    public DateTimeOffset EpochUtc { get; private set; }

    /// <summary>
    /// Номер канала — на телевизоре канал это номер, а не карточка в сетке. Переключение по номерам
    /// включается глобальным флагом настроек сайта; null — номер не задан.
    /// </summary>
    public int? Number { get; private set; }

    /// <summary>
    /// Смещение времени канала от UTC в минутах (по умолчанию 180 — московское). Фиксированный
    /// оффсет, а не IANA-зона: с переводом часов сутки становятся 23- или 25-часовыми, и сетке
    /// понадобилось бы отдельное правило подрезки. Появятся каналы в зонах с DST — перейдём на IANA.
    /// </summary>
    public int UtcOffsetMinutes { get; private set; }

    /// <summary>
    /// Начало вещательных суток в времени канала (по умолчанию 06:00). Ночной блок с 00:00 до 06:00
    /// относится к предыдущему дню: «ночь с пятницы на субботу» — это пятница.
    /// </summary>
    public TimeOnly DayStartTime { get; private set; }

    /// <summary>Активный шаблон сетки канала (один на канал).</summary>
    public Guid? TemplateId { get; private set; }

    public const int DefaultUtcOffsetMinutes = 180;
    public static readonly TimeOnly DefaultDayStartTime = new(6, 0);

    // ── Настройки ТВ-заставок. Условия показа (как часто, на смене шоу или между сериями)
    //    живут в элементах стыка; на канале осталось только общее для всех заставок. ──

    /// <summary>Вставлять ли ТВ-заставки вообще: общий выключатель канала.</summary>
    public bool BumpersEnabled { get; private set; }

    /// <summary>Как выбирать подблок заставки на переходе (случайно/по весам/всегда первый).</summary>
    public BumperSelection BumperSelection { get; private set; }

    public BumperFont BumperFont { get; private set; }

    private const string DefaultTemplateName = "Заставка 1";

    /// <summary>Ассет-заглушка на случай пустого расписания (аварийная подстраховка).</summary>
    public Guid? FillerAssetId { get; private set; }

    // ── Зрительская часть (см. 6.8). Всё рисуется на клиенте поверх <video>, ffmpeg не трогает,
    //    и всё по умолчанию выключено: канал без логотипа и без шума — законная конфигурация. ──

    /// <summary>Логотип-оверлей: ссылка на реестр изображений или null (логотипа нет).</summary>
    public Guid? LogoImageId { get; private set; }

    public LogoCorner LogoCorner { get; private set; }

    /// <summary>Прозрачность логотипа, 0..1.</summary>
    public double LogoOpacity { get; private set; } = 0.8;

    /// <summary>Показывать ли часы поверх картинки.</summary>
    public bool ShowClock { get; private set; }

    /// <summary>Сила аналогового фильтра, 0..1 (0 — выключен).</summary>
    public double AnalogFilterStrength { get; private set; }

    public DateTimeOffset CreatedAt { get; private set; }

    /// <summary>Блоки заставок (звук+стиль); первый (Position 0) — дефолтный, порядок — по Position.</summary>
    public IReadOnlyList<BumperTemplate> BumperTemplates => _bumperTemplates;

    private Channel() { }

    public static Channel Create(string name, string slug, DateTimeOffset epochUtc)
    {
        var channel = new Channel
        {
            Id = Guid.NewGuid(),
            Name = name,
            Slug = slug,
            IsEnabled = true,
            EpochUtc = epochUtc,
            BumpersEnabled = false,
            BumperSelection = BumperSelection.WeightedRandom,
            BumperFont = BumperFont.Sans,
            LogoOpacity = 0.8,
            UtcOffsetMinutes = DefaultUtcOffsetMinutes,
            DayStartTime = DefaultDayStartTime,
            CreatedAt = DateTimeOffset.UtcNow,
        };
        // На канале всегда есть дефолтный блок заставки (без звука → синтезированный джингл).
        channel._bumperTemplates.Add(BumperTemplate.Create(channel.Id, 0, DefaultTemplateName));
        return channel;
    }

    public void UpdateSettings(
        string name,
        bool isEnabled,
        bool bumpersEnabled,
        Guid? fillerAssetId
    )
    {
        Name = name;
        IsEnabled = isEnabled;
        BumpersEnabled = bumpersEnabled;
        FillerAssetId = fillerAssetId;
    }

    /// <summary>Общие настройки ТВ-заставок канала: шрифт и стратегия выбора подблока.</summary>
    public void UpdateBumperSettings(BumperFont font, BumperSelection selection)
    {
        BumperFont = font;
        BumperSelection = selection;
    }

    /// <summary>Оверлеи и фильтр зрительской части. Всё опционально; силы зажимаются в 0..1.</summary>
    public void UpdateViewerSettings(
        Guid? logoImageId,
        LogoCorner logoCorner,
        double logoOpacity,
        bool showClock,
        double analogFilterStrength
    )
    {
        LogoImageId = logoImageId;
        LogoCorner = logoCorner;
        LogoOpacity = Math.Clamp(logoOpacity, 0.0, 1.0);
        ShowClock = showClock;
        AnalogFilterStrength = Math.Clamp(analogFilterStrength, 0.0, 1.0);
    }

    /// <summary>Добавить блок заставки в конец списка. Возвращает созданный блок.</summary>
    public BumperTemplate AddBumperTemplate(string name)
    {
        var nextPosition =
            _bumperTemplates.Count == 0 ? 0 : _bumperTemplates.Max(t => t.Position) + 1;
        var template = BumperTemplate.Create(Id, nextPosition, name);
        _bumperTemplates.Add(template);
        return template;
    }

    public BumperTemplate? FindBumperTemplate(Guid templateId) =>
        _bumperTemplates.FirstOrDefault(t => t.Id == templateId);

    /// <summary>Удалить блок заставки. Дефолтный (Position 0) удалить нельзя — вернёт false.</summary>
    public bool RemoveBumperTemplate(Guid templateId)
    {
        var template = _bumperTemplates.FirstOrDefault(t => t.Id == templateId);
        if (template is null || template.IsDefault)
            return false;
        _bumperTemplates.Remove(template);
        return true;
    }

    /// <summary>Привязать активный шаблон сетки.</summary>
    public void SetTemplate(Guid? templateId) => TemplateId = templateId;

    /// <summary>
    /// Настройки времени канала: номер, смещение от UTC и начало вещательных суток. Смещение
    /// ограничено сутками — за пределами этого диапазона сетка потеряла бы связь с календарём.
    /// </summary>
    public void UpdateTimeSettings(int? number, int utcOffsetMinutes, TimeOnly dayStartTime)
    {
        Number = number is > 0 ? number : null;
        UtcOffsetMinutes = Math.Clamp(utcOffsetMinutes, -12 * 60, 14 * 60);
        DayStartTime = dayStartTime;
    }
}
