using System;
using System.Collections.Generic;
using System.Linq;
namespace ytLive.Models;
/// The session's scoreboard tallies — the facts a creator puts on screen to prove the
/// channel is moving.
///
/// Scope is the RUNNING SESSION (creator ruling 2026-10-30): the counts start at zero when a
/// session starts and die with it. No persistence, nothing to reconcile, and every number is true
/// for exactly what this session saw — a stored "today" that survives a Windows restart would be
/// right only for the hours the app happened to be running, which is a lie the creator cannot
/// check.
///
/// The two polled tiles (subscribers, watch hours) are NOT here: they are channel state, not
/// session events, and live on the polled side of the scoreboard.
public sealed class ScoreboardState
{
/// Super Chat + Super Sticker money, per currency. Adding across currencies would be
/// the one arithmetic nobody forgives, so totals are keyed and only the dominant one is shown.
private readonly Dictionary _moneyByCurrency = new(StringComparer.OrdinalIgnoreCase);
public string? LastMemberName { get; private set; }
public string? LastSuperChatAuthor { get; private set; }
public string? LastSuperChatDisplay { get; private set; }
public double LastSuperChatAmount { get; private set; }
public string? LastSuperChatCurrency { get; private set; }
public string? LastGifterName { get; private set; }
public int LastGiftCount { get; private set; }
/// Members gained this session: paid memberships plus gifted ones (counted once, from
/// the GIFT PURCHASE — see ).
public int NewMembers { get; private set; }
public int MilestoneCount { get; private set; }
public IReadOnlyDictionary MoneyByCurrency => _moneyByCurrency;
/// The session's dominant currency (most money) and its total, or ("", 0) when nothing
/// has come in. Chosen by VALUE so a single EUR tip beside ten USD cents cannot flip the display
/// to the smaller currency.
public (string Currency, double Total) SessionTotal =>
_moneyByCurrency.Count == 0
? (string.Empty, 0)
: _moneyByCurrency.OrderByDescending(pair => pair.Value).First() is var top
? (top.Key, top.Value)
: (string.Empty, 0);
public bool HasMixedCurrencies => _moneyByCurrency.Count > 1;
/// Folds one chat message into the tallies. Every event kind the poll decodes is
/// handled; plain chat is ignored.
///
/// giftMembershipReceivedEvent deliberately does nothing. It is the RECIPIENT's view
/// of a purchase already counted by membershipGiftingEvent, so counting it would double both the
/// members and the money (creator ruling 2026-10-30: "come on").
public void Apply(ChatMessage message)
{
switch (message.Kind)
{
case ChatEventKind.NewMember:
NewMembers++;
LastMemberName = message.AuthorName;
break;
case ChatEventKind.MemberGift:
// The purchase: one event carrying how many memberships were bought.
var count = Math.Max(1, message.GiftNewMembersCount);
NewMembers += count;
LastGiftCount = count;
LastGifterName = message.AuthorName;
break;
case ChatEventKind.GiftReceived:
break; // recipient side of the event above — never counted
case ChatEventKind.MemberMilestone:
MilestoneCount++;
break;
case ChatEventKind.SuperChat:
case ChatEventKind.SuperSticker:
LastSuperChatAuthor = message.AuthorName;
LastSuperChatAmount = message.SuperChatAmount;
LastSuperChatCurrency = string.IsNullOrWhiteSpace(message.SuperChatCurrency)
? "USD"
: message.SuperChatCurrency!;
LastSuperChatDisplay = message.SuperChatDisplayString
?? FormatMoney(LastSuperChatAmount, LastSuperChatCurrency);
if (message.SuperChatAmount > 0)
{
_moneyByCurrency.TryGetValue(LastSuperChatCurrency!, out var running);
_moneyByCurrency[LastSuperChatCurrency!] = running + message.SuperChatAmount;
}
break;
}
}
/// A new session starts from zero.
public void Reset() => CopyFrom(new ScoreboardState());
private void CopyFrom(ScoreboardState other)
{
_moneyByCurrency.Clear();
foreach (var pair in other._moneyByCurrency) _moneyByCurrency[pair.Key] = pair.Value;
LastMemberName = other.LastMemberName;
LastSuperChatAuthor = other.LastSuperChatAuthor;
LastSuperChatDisplay = other.LastSuperChatDisplay;
LastSuperChatAmount = other.LastSuperChatAmount;
LastSuperChatCurrency = other.LastSuperChatCurrency;
LastGifterName = other.LastGifterName;
LastGiftCount = other.LastGiftCount;
NewMembers = other.NewMembers;
MilestoneCount = other.MilestoneCount;
}
private static string FormatMoney(double amount, string? currency) =>
string.IsNullOrEmpty(currency) ? amount.ToString("0.00") : $"{currency} {amount:0.00}";
}