fn audio_usb_auto_recover_enabled() -> bool {
    std::env::var("LESAVKA_AUDIO_AUTO_RECOVER_USB")
        .map(|raw| {
            !matches!(
                raw.trim().to_ascii_lowercase().as_str(),
                "0" | "false" | "no" | "off"
            )
        })
        .unwrap_or(false)
}

fn audio_usb_recover_after() -> u32 {
    std::env::var("LESAVKA_AUDIO_AUTO_RECOVER_AFTER")
        .ok()
        .and_then(|raw| raw.parse::<u32>().ok())
        .filter(|value| *value > 0)
        .unwrap_or(3)
}

fn audio_usb_recover_cooldown() -> Duration {
    let millis = std::env::var("LESAVKA_AUDIO_AUTO_RECOVER_COOLDOWN_MS")
        .ok()
        .and_then(|raw| raw.parse::<u64>().ok())
        .unwrap_or(60_000);
    Duration::from_millis(millis)
}

fn is_recoverable_remote_audio_error(message: &str) -> bool {
    message.contains("remote speaker capture produced no audio samples")
        || message.contains("remote speaker capture stalled")
        || message.contains("remote speaker capture cadence is too low")
}

#[cfg(not(coverage))]
#[derive(Debug, Default)]
struct AudioFailureLogLimiter {
    last_warn_at: Option<Instant>,
    suppressed_repeats: u64,
    last_message: String,
}

#[cfg(not(coverage))]
/// Rate-limit repeated remote audio failures so operators see state changes, not log floods.
impl AudioFailureLogLimiter {
    /// Emit the first failure promptly, then aggregate identical repeats.
    fn record(&mut self, context: &'static str, message: &str) {
        let same_message = self.last_message == message;
        let should_warn = !same_message
            || self
                .last_warn_at
                .map(|last| last.elapsed() >= AUDIO_FAILURE_WARN_INTERVAL)
                .unwrap_or(true);

        if should_warn {
            tracing::warn!(
                context,
                suppressed_repeats = self.suppressed_repeats,
                "❌🔊 audio stream unhealthy: {message}"
            );
            self.last_warn_at = Some(Instant::now());
            self.suppressed_repeats = 0;
            self.last_message.clear();
            self.last_message.push_str(message);
        } else {
            self.suppressed_repeats = self.suppressed_repeats.saturating_add(1);
            tracing::debug!(
                context,
                suppressed_repeats = self.suppressed_repeats,
                "audio stream repeated unhealthy state suppressed from WARN noise: {message}"
            );
        }
    }
}

#[cfg(not(coverage))]
const AUDIO_FAILURE_WARN_INTERVAL: Duration = Duration::from_secs(30);

pub(crate) fn keyboard_stream_report(
    report: Result<KeyboardReport, BroadcastStreamRecvError>,
    remote_capture_enabled: bool,
    remote_capture_was_enabled: &mut bool,
) -> Option<KeyboardReport> {
    if !remote_capture_enabled {
        let emit_reset = *remote_capture_was_enabled;
        *remote_capture_was_enabled = false;
        return emit_reset.then_some(KeyboardReport { data: vec![0; 8] });
    }
    *remote_capture_was_enabled = true;
    match report {
        Ok(report) => Some(report),
        Err(BroadcastStreamRecvError::Lagged(skipped)) => {
            warn!(
                skipped,
                "⌨️ live keyboard stream lagged; sending a clean reset report before continuing"
            );
            Some(KeyboardReport { data: vec![0; 8] })
        }
    }
}

pub(crate) fn mouse_stream_report(
    report: Result<MouseReport, BroadcastStreamRecvError>,
    remote_capture_enabled: bool,
    remote_capture_was_enabled: &mut bool,
) -> Option<MouseReport> {
    if !remote_capture_enabled {
        let emit_reset = *remote_capture_was_enabled;
        *remote_capture_was_enabled = false;
        return emit_reset.then_some(MouseReport { data: vec![0; 4] });
    }
    *remote_capture_was_enabled = true;
    match report {
        Ok(report) => Some(report),
        Err(BroadcastStreamRecvError::Lagged(skipped)) => {
            warn!(
                skipped,
                "🖱️ live mouse stream lagged; sending a neutral report before continuing"
            );
            Some(MouseReport { data: vec![0; 4] })
        }
    }
}
