namespace TeleWave.Domain.Programming;

/// <summary>
/// Шаблон стыка: что играет между программами. Реклама и заставки перестают быть свойствами канала
/// и становятся врезками стыка — за счёт этого в прайм можно поставить три ролика и заставку,
/// а ночью один длинный ролик, чего одной настройкой на канал не сделать.
/// </summary>
public class JunctionTemplate
{
    private readonly List<JunctionElement> _elements = [];

    public Guid Id { get; private set; }
    public Guid ChannelId { get; private set; }
    public string Name { get; private set; } = string.Empty;
    public DateTimeOffset CreatedAt { get; private set; }

    /// <summary>Врезки в порядке показа (backing-field для EF).</summary>
    public IReadOnlyList<JunctionElement> Elements => _elements;

    private JunctionTemplate() { }

    public static JunctionTemplate Create(Guid channelId, string name) =>
        new()
        {
            Id = Guid.NewGuid(),
            ChannelId = channelId,
            Name = name.Trim(),
            CreatedAt = DateTimeOffset.UtcNow,
        };

    public void Rename(string name) => Name = name.Trim();

    public JunctionElement AddElement(JunctionElementKind kind)
    {
        var nextPosition = _elements.Count == 0 ? 0 : _elements.Max(e => e.Position) + 1;
        var element = JunctionElement.Create(Id, nextPosition, kind);
        _elements.Add(element);
        return element;
    }

    public JunctionElement? FindElement(Guid elementId) =>
        _elements.FirstOrDefault(e => e.Id == elementId);

    public bool RemoveElement(Guid elementId)
    {
        var element = _elements.FirstOrDefault(e => e.Id == elementId);
        if (element is null)
            return false;
        _elements.Remove(element);
        return true;
    }

    /// <summary>
    /// Переставляет врезки в порядке переданных идентификаторов; не упомянутые остаются после них,
    /// сохраняя относительный порядок. Порядок здесь — это порядок показа в эфире.
    /// </summary>
    public void Reorder(IEnumerable<Guid> elementIdsInOrder)
    {
        var known = _elements.Select(e => e.Id).ToHashSet();
        var requested = elementIdsInOrder.Distinct().Where(known.Contains).ToList();
        var rest = _elements
            .Where(e => !requested.Contains(e.Id))
            .OrderBy(e => e.Position)
            .Select(e => e.Id);

        var position = 0;
        foreach (var id in requested.Concat(rest))
            _elements.First(e => e.Id == id).SetPosition(position++);
    }
}
