using System;
namespace AcDream.App.Streaming;
///
/// Diagnostic owner for the streaming/reveal probe family (CLAUDE.md Code
/// Structure Rules §5 — one static class per subsystem, typed properties read
/// from the environment once at startup, never per-call-site
/// GetEnvironmentVariable reads).
///
internal static class StreamingDiagnostics
{
///
/// #280 A/B measurement probe. When set, the outdoor reveal gate uses this
/// landblock radius instead of the derived streaming window, so the same
/// binary can run the connected route once with the pre-fix behaviour
/// (ACDREAM_PROBE_REVEAL_RADIUS=1, the old
/// OutdoorNeighborhoodRadius) and once without. It is NOT a user
/// setting: it is not surfaced in Settings, not persisted, and not part of
/// RuntimeOptions. A shipped prefetch knob would re-expose exactly
/// the reveal-window/visible-window decoupling #280 exists to close.
/// Unset or unparseable leaves the derivation in charge.
///
public static int? RevealRadiusOverride { get; } =
ParseRadius(
Environment.GetEnvironmentVariable("ACDREAM_PROBE_REVEAL_RADIUS"));
///
/// Applies to a live streaming window.
///
public static StreamingRevealWindow ApplyRevealRadiusOverride(
StreamingRevealWindow window) =>
ApplyRevealRadiusOverride(window, RevealRadiusOverride);
///
/// Pure override application, separated so the clamp is testable without
/// mutating process-wide diagnostic state. The near radius is clamped to
/// the (possibly overridden) far radius: the gate's inner arm demands
/// Near-tier publication, so it can never exceed the outer arm.
///
public static StreamingRevealWindow ApplyRevealRadiusOverride(
StreamingRevealWindow window,
int? overrideRadius)
{
if (overrideRadius is not { } radius)
return window;
int far = Math.Max(0, radius);
return new StreamingRevealWindow(
Math.Clamp(window.NearRadius, 0, far),
far);
}
///
/// The floor is 1, not 0. An outdoor destination's acknowledgement must
/// carry RequiredRenderRadius >= 1 or
/// RuntimeWorldTransitState.AcknowledgeDestinationReadiness fails
/// invalid-readiness-shape on every frame — so accepting 0 here
/// would hang the very A/B route this probe exists to measure. Reject it
/// at the parser rather than let the two halves of one commit disagree.
///
internal static int? ParseRadius(string? raw) =>
int.TryParse(raw, out int value) && value >= 1 ? value : null;
}