namespace TeleWave.Domain.Programming.Planning;
///
/// Показы, добавленные этим же прогоном. Остывание и потолок повторов считаются по ленте, а она на
/// момент сборки входа содержит только прошлое: горизонт в неделю строится одним прогоном, и без
/// этого счётчика оба правила не видели бы ничего из того, что сами же и поставили.
///
/// Ключ — шоу, как и в снимке из базы: у коллекции остывание общее по всем частям, поэтому элемент
/// спрашивает историю сразу по всем своим шоу.
///
public sealed class RunPlayHistory
{
private readonly Dictionary> _byShow = [];
public void Record(Guid showId, DateTimeOffset startsAtUtc)
{
if (!_byShow.TryGetValue(showId, out var plays))
_byShow[showId] = plays = [];
plays.Add(startsAtUtc);
}
/// Когда элемент последний раз выходил в этом прогоне; null — ещё не выходил.
public DateTimeOffset? LastPlayed(PlanningElement element)
{
DateTimeOffset? last = null;
foreach (var showId in ShowsOf(element))
{
if (!_byShow.TryGetValue(showId, out var plays))
continue;
foreach (var play in plays)
if (last is null || play > last)
last = play;
}
return last;
}
/// Сколько раз элемент выходил в этом прогоне начиная с момента.
public int CountSince(PlanningElement element, DateTimeOffset from)
{
var count = 0;
foreach (var showId in ShowsOf(element))
if (_byShow.TryGetValue(showId, out var plays))
count += plays.Count(play => play >= from);
return count;
}
///
/// Шоу элемента берутся из его единиц: у коллекции их столько же, сколько частей, и это ровно
/// тот набор, по которому историю собирает и оркестратор.
///
private static IEnumerable ShowsOf(PlanningElement element) =>
element.Units.Select(unit => unit.ShowId).Distinct();
}