using System;
using System.Collections.Generic;
using System.Globalization;
namespace ytLive.Models;
/// Which of the eight facts the creator put on screen, and how each renders.
///
/// Every tile is optional because the strip is an ego wall — the creator chooses the
/// billboard. Goal tiles carry their targets here so the rendering stays a pure function of the
/// state plus this selection.
public sealed class ScoreboardTiles
{
/// Nothing enabled — the layer draws nothing.
public static ScoreboardTiles None => new();
/// The selection a new layer starts with: the six session facts, no channel polls
/// (those need the Analytics scope).
public static ScoreboardTiles Default => new()
{
SuperChats = true, NewMembers = true, Milestones = true,
LastMember = true, LastSuperChat = true, LastGift = true,
};
public bool Subscribers { get; set; }
public bool WatchHours { get; set; }
public bool SuperChats { get; set; }
public bool NewMembers { get; set; }
public bool Milestones { get; set; }
public bool LastMember { get; set; }
public bool LastSuperChat { get; set; }
public bool LastGift { get; set; }
/// Goal targets for the two polled tiles; 0 means "no goal, show the raw number".
public int SubscriberGoal { get; set; }
public int WatchHourGoal { get; set; }
/// Channel state fed by the polled side (subscribers, watch hours). Kept here so the
/// strip is built from ONE source of truth: tallies + channel values + this selection.
public int SubscriberCount { get; set; }
public int WatchHoursValue { get; set; }
/// Cache key: the selection alone. Values are folded in by the presenter's fingerprint,
/// so this only needs to change when the ENABLED SET changes.
public string CacheKey =>
$"{Subscribers}{WatchHours}{SuperChats}{NewMembers}{Milestones}{LastMember}{LastSuperChat}{LastGift}" +
$"{SubscriberGoal}{WatchHourGoal}";
/// The strip's text, in reading order. Empty strings are dropped by the renderer.
public IReadOnlyList Build(ScoreboardState state)
{
var tiles = new List(8);
if (Subscribers)
tiles.Add(SubscriberGoal > 0
? $"Subs {SubscriberCount:N0}/{SubscriberGoal:N0}"
: $"Subs {SubscriberCount:N0}");
if (WatchHours)
// Analytics is a DAILY aggregate, so the tile must not imply live data.
tiles.Add(WatchHourGoal > 0
? $"Hours {WatchHoursValue:N0}/{WatchHourGoal:N0} (as of {DateTime.Now:MMM d})"
: $"Hours {WatchHoursValue:N0} (as of {DateTime.Now:MMM d})");
if (SuperChats && state.SessionTotal.Total > 0)
tiles.Add(state.HasMixedCurrencies
? $"SC {state.SessionTotal.Currency} {state.SessionTotal.Total:0.00} +{state.MoneyByCurrency.Count - 1}"
: $"SC {state.SessionTotal.Currency} {state.SessionTotal.Total:0.00}");
if (NewMembers && state.NewMembers > 0)
tiles.Add($"Members +{state.NewMembers}");
if (Milestones && state.MilestoneCount > 0)
tiles.Add($"Milestones {state.MilestoneCount}");
if (LastMember && !string.IsNullOrWhiteSpace(state.LastMemberName))
tiles.Add($"Last member {state.LastMemberName}");
if (LastSuperChat && !string.IsNullOrWhiteSpace(state.LastSuperChatDisplay))
tiles.Add($"Last SC {state.LastSuperChatDisplay}");
if (LastGift && !string.IsNullOrWhiteSpace(state.LastGifterName))
tiles.Add($"Last gift {state.LastGifterName} ×{Math.Max(1, state.LastGiftCount)}");
return tiles;
}
}