import sys, os
sys.path.insert(0, os.path.dirname(__file__))
from crlfpatch import patch

P = 'crates/spt/tests/serving_in_brain_e2e.rs'
patch(P, [
('''//! The rebind gap is MEASURED by a prober hammering the port across the
//! refresh with `Connection: close` requests (the server closes first, so
//! server-side TIME_WAIT is live on the port when the brain dies — the case
//! where SO_REUSEADDR semantics matter). It is REPORTED, never gated on a
//! budget: a product constant raced under load is a false red.
''',
'''//! The rebind gap is MEASURED by a prober hammering the port across the
//! refresh with `Connection: close` requests (the server closes first, so
//! server-side TIME_WAIT is live on the port when the brain dies — the case
//! where SO_REUSEADDR semantics matter). Each sample is an INTERVAL, not an
//! instant — a refused connect on Windows loopback takes most of a second —
//! so the gap is reported as a bracket: LOWER = the down time the failed
//! samples prove, UPPER = last success START before the outage to first
//! success END after it. It is REPORTED, never gated on a budget: a product
//! constant raced under load is a false red.
'''),
('''    let samples: Arc<Mutex<Vec<(Instant, bool)>>> = Arc::new(Mutex::new(Vec::new()));
''',
'''    // (attempt start, attempt end, served 200)
    let samples: Arc<Mutex<Vec<(Instant, Instant, bool)>>> = Arc::new(Mutex::new(Vec::new()));
'''),
('''            while !stop.load(Ordering::Relaxed) {
                let ok = try_get(port, &probe_path, Duration::from_millis(500))
                    .is_some_and(|(status, _)| status == 200);
                samples.lock().unwrap().push((Instant::now(), ok));
''',
'''            while !stop.load(Ordering::Relaxed) {
                let started = Instant::now();
                let ok = try_get(port, &probe_path, Duration::from_millis(500))
                    .is_some_and(|(status, _)| status == 200);
                samples.lock().unwrap().push((started, Instant::now(), ok));
'''),
('''        let last_ok = samples.lock().unwrap().last().is_some_and(|(t, ok)| *ok && *t > refresh_sent);
''',
'''        let last_ok = samples.lock().unwrap().last().is_some_and(|(s, _, ok)| *ok && *s > refresh_sent);
'''),
('''    // The rebind gap: the longest run of failed samples after the refresh was
    // sent, measured first-failure to first-success-after.
    let samples = samples.lock().unwrap().clone();
    let pre_refresh_failures = samples.iter().filter(|(t, ok)| *t < refresh_sent && !ok).count();
    let mut gap = Duration::ZERO;
    let mut down_since: Option<Instant> = None;
    for (t, ok) in samples.iter().filter(|(t, _)| *t >= refresh_sent) {
        match (ok, down_since) {
            (false, None) => down_since = Some(*t),
            (true, Some(since)) => {
                gap = gap.max(*t - since);
                down_since = None;
            }
            _ => {}
        }
    }
    let never_recovered = down_since.is_some();
''',
'''    // The rebind gap, as a bracket over the one outage the refresh causes:
    // the failed samples after the last pre-outage success. LOWER: the
    // listener was closed at some moment inside the first AND the last failed
    // interval, so it was down at least last.start - first.end. UPPER: it was
    // serving at some moment inside the bracketing successes, so it was down
    // at most next_ok.end - prev_ok.start.
    let samples = samples.lock().unwrap().clone();
    let pre_refresh_failures = samples.iter().filter(|(_, end, ok)| *end < refresh_sent && !ok).count();
    let first_fail = samples.iter().position(|(start, _, ok)| *start >= refresh_sent && !ok);
    let (gap_lower, gap_upper, failed_samples, never_recovered) = match first_fail {
        None => (Duration::ZERO, Duration::ZERO, 0, false),
        Some(first) => {
            let recovered = samples[first..].iter().position(|(_, _, ok)| *ok).map(|i| first + i);
            let last_fail = recovered.map_or(samples.len() - 1, |r| r - 1);
            let lower = samples[last_fail].0.saturating_duration_since(samples[first].1);
            let upper = match (first.checked_sub(1), recovered) {
                (Some(prev), Some(next)) => samples[next].1 - samples[prev].0,
                _ => Duration::MAX,
            };
            (lower, upper, last_fail + 1 - first, recovered.is_none())
        }
    };
'''),
('''         samples={} pre_refresh_failures={pre_refresh_failures} \\
         REBIND_GAP_MS={} never_recovered={never_recovered} ===\\n\\
''',
'''         samples={} pre_refresh_failures={pre_refresh_failures} failed_samples={failed_samples} \\
         REBIND_GAP_MS=[{}, {}] never_recovered={never_recovered} ===\\n\\
'''),
('''        samples.len(),
        gap.as_millis(),
    );
''',
'''        samples.len(),
        gap_lower.as_millis(),
        gap_upper.as_millis(),
    );
'''),
])
