namespace ytLive.Models;
///
/// The one entitlement rule, as pure policy: what a build is allowed to emit.
///
/// Why pixels and a frame rate rather than "720p". The paid delta is output
/// quality, and a resolution name is a marketing word, not a measurable bound — 720p60 is
/// 921,600 pixels at twice the frame rate, and vertical 720×1280 is the same pixel count
/// with a different shape. A pixel budget plus a frame-rate ceiling expresses the same
/// promise without a lookup table that a new tier can quietly escape.
///
/// Fail closed. Callers pass pro: false when entitlement is unknown,
/// unreadable or unresolved; the free profile is the default state, never the fallback
/// after an error.
///
/// This is not a feature gate. Every feature, scene, overlay and the recording
/// itself stay available; only the emitted frame gets smaller. Never call it for anything
/// else — a cracked build buys almost nothing, which is the entire anti-piracy posture.
///
public static class OutputProfile
{
/// Free ceiling: 921,600 px = 1280×720, i.e. one 720p frame.
public const int FreeMaxPixels = 921_600;
/// Free ceiling: 30 frames per second.
public const int FreeMaxFps = 30;
/// The named tier a capped build falls back to when nothing else qualifies.
public const string FreeFallbackName = "720p30";
/// Is this tier within the free budget? Pro may use anything shipped.
public static bool IsAllowed(QualityOption tier, bool pro)
=> pro
|| (tier.Width * tier.Height <= FreeMaxPixels && tier.Fps <= FreeMaxFps);
///
/// The tier a session will actually encode. A permitted selection passes through
/// untouched (so Pro and the free tier's own 720p30 keep their exact bitrate); a
/// disallowed one resolves to the best tier the budget allows — most pixels first, then
/// most frames, then the cheapest bitrate — because silently encoding something the user
/// did not pick is only acceptable if it is the closest legal thing to it.
///
/// What the dropdown says.
/// Every tier the build offers.
/// Entitlement, already resolved. Unknown must be passed as false.
public static QualityOption Resolve(
QualityOption requested, IEnumerable shipped, bool pro)
{
if (IsAllowed(requested, pro)) return requested;
var candidate = shipped
.Where(tier => IsAllowed(tier, pro))
.OrderByDescending(tier => tier.Width * tier.Height)
.ThenByDescending(tier => tier.Fps)
.ThenBy(tier => tier.Bitrate)
.FirstOrDefault();
// No shipped tier fits the budget: build the floor explicitly rather than trusting
// the table. A build whose tiers were all redefined must still emit a legal frame.
return candidate ?? new QualityOption(FreeFallbackName, FreeMaxFps, 6.0, 1280, 720);
}
}