using Microsoft.Data.Sqlite;
using ytLive.Models;
namespace ytLive.Services;
public partial class LayoutStore : IDisposable
{
// ─── YPP journey-tracker snapshots (TASK 39) ───
/// Appends one YPP stats snapshot (append-only history; velocity/ETA
/// read it in a later analytics slice).
public void SaveYppSnapshot(YppStatSnapshot snapshot)
{
using var cmd = _connection.CreateCommand();
cmd.CommandText = """
INSERT INTO YppSnapshot (CapturedAtUtc, SubscriberCount, VideoCount, TotalViews,
UploadsLast90Days, CommunityGuidelinesGoodStanding,
CopyrightStrikesGoodStanding, ContentIdClaimsGoodStanding,
OverallGoodStanding)
VALUES ($captured, $subs, $videos, $views, $uploads, $cg, $cs, $cic, $overall);
""";
cmd.Parameters.AddWithValue("$captured", snapshot.CapturedAtUtc.ToString("o"));
cmd.Parameters.AddWithValue("$subs", snapshot.SubscriberCount);
cmd.Parameters.AddWithValue("$videos", snapshot.VideoCount);
cmd.Parameters.AddWithValue("$views", snapshot.TotalViews);
cmd.Parameters.AddWithValue("$uploads", snapshot.UploadsLast90Days);
cmd.Parameters.AddWithValue("$cg", snapshot.CommunityGuidelinesGoodStanding ? 1 : 0);
cmd.Parameters.AddWithValue("$cs", snapshot.CopyrightStrikesGoodStanding ? 1 : 0);
cmd.Parameters.AddWithValue("$cic", snapshot.ContentIdClaimsGoodStanding ? 1 : 0);
cmd.Parameters.AddWithValue("$overall", snapshot.OverallGoodStanding ? 1 : 0);
cmd.ExecuteNonQuery();
}
/// Most recent snapshots, newest first (defaults to the full history).
public List LoadYppSnapshots(int limit = 365)
{
var result = new List();
using var cmd = _connection.CreateCommand();
cmd.CommandText = """
SELECT CapturedAtUtc, SubscriberCount, VideoCount, TotalViews, UploadsLast90Days,
CommunityGuidelinesGoodStanding, CopyrightStrikesGoodStanding,
ContentIdClaimsGoodStanding, OverallGoodStanding
FROM YppSnapshot ORDER BY CapturedAtUtc DESC LIMIT $limit;
""";
cmd.Parameters.AddWithValue("$limit", limit);
using var reader = cmd.ExecuteReader();
while (reader.Read())
{
result.Add(new YppStatSnapshot
{
CapturedAtUtc = DateTime.TryParse(reader.GetString(0), out var captured) ? captured : DateTime.MinValue,
SubscriberCount = reader.GetInt64(1),
VideoCount = reader.GetInt64(2),
TotalViews = reader.GetInt64(3),
UploadsLast90Days = reader.GetInt32(4),
CommunityGuidelinesGoodStanding = reader.GetInt32(5) != 0,
CopyrightStrikesGoodStanding = reader.GetInt32(6) != 0,
ContentIdClaimsGoodStanding = reader.GetInt32(7) != 0,
OverallGoodStanding = reader.GetInt32(8) != 0,
});
}
return result;
}
/// The manually self-reported YPP checklist state (2FA / AdSense are
/// not readable from any YouTube API — the creator ticks them in-app).
public (bool TwoFactorEnabled, bool AdSenseLinked) LoadYppChecklist()
{
return (GetSetting("Ypp.TwoFactorEnabled") == "1", GetSetting("Ypp.AdSenseLinked") == "1");
}
public void SaveYppChecklist(bool twoFactorEnabled, bool adsenseLinked)
{
UpsertSetting("Ypp.TwoFactorEnabled", twoFactorEnabled ? "1" : "0");
UpsertSetting("Ypp.AdSenseLinked", adsenseLinked ? "1" : "0");
}
/// UTC timestamp of the last successful stats refresh (used to gate
/// the once-daily auto-refresh against the analytics quota-thin API).
public DateTime? LoadYppLastRefreshedAtUtc()
{
var value = GetSetting("Ypp.LastRefreshedAtUtc");
return DateTime.TryParse(value, out var dt) ? dt : null;
}
public void SaveYppLastRefreshedAtUtc(DateTime utc)
{
UpsertSetting("Ypp.LastRefreshedAtUtc", utc.ToString("o"));
}
}