impl SnapshotReport {
    pub fn from_state(state: &LauncherState, log: &DiagnosticsLog, probe_command: String) -> Self {
        let left_capture = state
            .display_capture_size_choice(0)
            .unwrap_or_else(|| state.capture_size_choice(0));
        let right_capture = state
            .display_capture_size_choice(1)
            .unwrap_or_else(|| state.capture_size_choice(1));
        let left_breakout = state.breakout_size_choice(0);
        let right_breakout = state.breakout_size_choice(1);
        let latest = log.latest();
        let left_stream_caps = latest
            .map(|sample| sample.left_stream_caps_label.clone())
            .unwrap_or_default();
        let right_stream_caps = latest
            .map(|sample| sample.right_stream_caps_label.clone())
            .unwrap_or_default();
        Self {
            client_version: crate::VERSION.to_string(),
            server_version: state.server_version.clone(),
            server_available: state.server_available,
            routing: state.routing,
            view_mode: state.view_mode,
            remote_active: state.remote_active,
            power_state: format!(
                "{} | {} | leases {}",
                state.capture_power.mode,
                state.capture_power.detail,
                state.capture_power.active_leases
            ),
            client_process_cpu_pct: latest
                .map(|sample| sample.client_process_cpu_pct)
                .unwrap_or(0.0),
            server_process_cpu_pct: latest
                .map(|sample| sample.server_process_cpu_pct)
                .unwrap_or(0.0),
            preview_source: format!(
                "{}x{} @ {} fps",
                state.preview_source.width, state.preview_source.height, state.preview_source.fps
            ),
            client_display_limit: format!(
                "{}x{}",
                state.breakout_display.width, state.breakout_display.height
            ),
            left_surface: state.display_surface(0).label().to_string(),
            left_feed_source: match state.feed_source_preset(0) {
                super::state::FeedSourcePreset::ThisEye => "Left Eye".to_string(),
                super::state::FeedSourcePreset::OtherEye => "Right Eye (mirrored)".to_string(),
                super::state::FeedSourcePreset::Off => "Off".to_string(),
            },
            left_capture_profile: capture_profile_label(&left_capture, &left_stream_caps),
            left_capture_transport: left_capture.preset.transport_label().to_string(),
            left_breakout_profile: format!(
                "{} | {}x{}",
                left_breakout.preset.label(),
                left_breakout.width,
                left_breakout.height
            ),
            left_decoder_label: latest
                .map(|sample| {
                    if sample.left_decoder_label.is_empty() {
                        "pending".to_string()
                    } else {
                        sample.left_decoder_label.clone()
                    }
                })
                .unwrap_or_else(|| "pending".to_string()),
            left_stream_spread_ms: latest
                .map(|sample| sample.left_stream_spread_ms)
                .unwrap_or(0.0),
            left_packet_gap_peak_ms: latest
                .map(|sample| sample.left_packet_gap_peak_ms)
                .unwrap_or(0.0),
            left_present_gap_peak_ms: latest
                .map(|sample| sample.left_present_gap_peak_ms)
                .unwrap_or(0.0),
            left_queue_depth: latest.map(|sample| sample.left_queue_depth).unwrap_or(0),
            left_queue_peak: latest.map(|sample| sample.left_queue_peak).unwrap_or(0),
            left_server_source_gap_peak_ms: latest
                .map(|sample| sample.left_server_source_gap_peak_ms)
                .unwrap_or(0.0),
            left_server_send_gap_peak_ms: latest
                .map(|sample| sample.left_server_send_gap_peak_ms)
                .unwrap_or(0.0),
            left_server_queue_peak: latest
                .map(|sample| sample.left_server_queue_peak)
                .unwrap_or(0),
            left_server_encoder_label: latest
                .map(|sample| {
                    if sample.left_server_encoder_label.is_empty() {
                        "pending".to_string()
                    } else {
                        sample.left_server_encoder_label.clone()
                    }
                })
                .unwrap_or_else(|| "pending".to_string()),
            left_stream_caps_label: latest
                .map(|sample| {
                    if sample.left_stream_caps_label.is_empty() {
                        "pending".to_string()
                    } else {
                        sample.left_stream_caps_label.clone()
                    }
                })
                .unwrap_or_else(|| "pending".to_string()),
            left_decoded_caps_label: latest
                .map(|sample| {
                    if sample.left_decoded_caps_label.is_empty() {
                        "pending".to_string()
                    } else {
                        sample.left_decoded_caps_label.clone()
                    }
                })
                .unwrap_or_else(|| "pending".to_string()),
            left_rendered_caps_label: latest
                .map(|sample| {
                    if sample.left_rendered_caps_label.is_empty() {
                        "pending".to_string()
                    } else {
                        sample.left_rendered_caps_label.clone()
                    }
                })
                .unwrap_or_else(|| "pending".to_string()),
            right_surface: state.display_surface(1).label().to_string(),
            right_feed_source: match state.feed_source_preset(1) {
                super::state::FeedSourcePreset::ThisEye => "Right Eye".to_string(),
                super::state::FeedSourcePreset::OtherEye => "Left Eye (mirrored)".to_string(),
                super::state::FeedSourcePreset::Off => "Off".to_string(),
            },
            right_capture_profile: capture_profile_label(&right_capture, &right_stream_caps),
            right_capture_transport: right_capture.preset.transport_label().to_string(),
            right_breakout_profile: format!(
                "{} | {}x{}",
                right_breakout.preset.label(),
                right_breakout.width,
                right_breakout.height
            ),
            right_decoder_label: latest
                .map(|sample| {
                    if sample.right_decoder_label.is_empty() {
                        "pending".to_string()
                    } else {
                        sample.right_decoder_label.clone()
                    }
                })
                .unwrap_or_else(|| "pending".to_string()),
            right_stream_spread_ms: latest
                .map(|sample| sample.right_stream_spread_ms)
                .unwrap_or(0.0),
            right_packet_gap_peak_ms: latest
                .map(|sample| sample.right_packet_gap_peak_ms)
                .unwrap_or(0.0),
            right_present_gap_peak_ms: latest
                .map(|sample| sample.right_present_gap_peak_ms)
                .unwrap_or(0.0),
            right_queue_depth: latest.map(|sample| sample.right_queue_depth).unwrap_or(0),
            right_queue_peak: latest.map(|sample| sample.right_queue_peak).unwrap_or(0),
            right_server_source_gap_peak_ms: latest
                .map(|sample| sample.right_server_source_gap_peak_ms)
                .unwrap_or(0.0),
            right_server_send_gap_peak_ms: latest
                .map(|sample| sample.right_server_send_gap_peak_ms)
                .unwrap_or(0.0),
            right_server_queue_peak: latest
                .map(|sample| sample.right_server_queue_peak)
                .unwrap_or(0),
            right_server_encoder_label: latest
                .map(|sample| {
                    if sample.right_server_encoder_label.is_empty() {
                        "pending".to_string()
                    } else {
                        sample.right_server_encoder_label.clone()
                    }
                })
                .unwrap_or_else(|| "pending".to_string()),
            right_stream_caps_label: latest
                .map(|sample| {
                    if sample.right_stream_caps_label.is_empty() {
                        "pending".to_string()
                    } else {
                        sample.right_stream_caps_label.clone()
                    }
                })
                .unwrap_or_else(|| "pending".to_string()),
            right_decoded_caps_label: latest
                .map(|sample| {
                    if sample.right_decoded_caps_label.is_empty() {
                        "pending".to_string()
                    } else {
                        sample.right_decoded_caps_label.clone()
                    }
                })
                .unwrap_or_else(|| "pending".to_string()),
            right_rendered_caps_label: latest
                .map(|sample| {
                    if sample.right_rendered_caps_label.is_empty() {
                        "pending".to_string()
                    } else {
                        sample.right_rendered_caps_label.clone()
                    }
                })
                .unwrap_or_else(|| "pending".to_string()),
            selected_camera: state.devices.camera.clone(),
            camera_quality_label: state
                .camera_quality
                .map(CameraMode::short_label)
                .unwrap_or_else(|| "default".to_string()),
            selected_microphone: state.devices.microphone.clone(),
            selected_speaker: state.devices.speaker.clone(),
            media_channels: MediaChannelState {
                camera: state.channels.camera,
                microphone: state.channels.microphone,
                audio: state.channels.audio,
            },
            audio_gain_label: state.audio_gain_label(),
            mic_gain_label: state.mic_gain_label(),
            upstream_camera: latest
                .map(|sample| sample.upstream_camera.clone())
                .unwrap_or_default(),
            upstream_microphone: latest
                .map(|sample| sample.upstream_microphone.clone())
                .unwrap_or_default(),
            selected_keyboard: state.devices.keyboard.clone(),
            selected_mouse: state.devices.mouse.clone(),
            status: state.status_line(),
            recent_samples: log.iter().cloned().collect(),
            notes: state.notes.clone(),
            recommendations: recommendations_for(state, log),
            probe_command,
        }
    }

    pub fn to_pretty_json(&self) -> Result<String, serde_json::Error> {
        serde_json::to_string_pretty(self)
    }

    pub fn to_pretty_text(&self) -> String {
        let mut text = String::new();
        let server_version = self.server_version.as_deref().unwrap_or("unknown");
        let server_state = if self.server_available {
            "reachable"
        } else {
            "unreachable"
        };
        let _ = writeln!(text, "Lesavka Diagnostics");
        let _ = writeln!(text, "client: v{}", self.client_version);
        let _ = writeln!(text, "server: {server_version} ({server_state})");
        let _ = writeln!(
            text,
            "session: routing={:?} view={:?} relay={} capture_power={}",
            self.routing,
            self.view_mode,
            if self.remote_active { "active" } else { "idle" },
            self.power_state
        );
        let _ = writeln!(
            text,
            "runtime: client CPU {:.1}% | server CPU {:.1}%",
            self.client_process_cpu_pct, self.server_process_cpu_pct
        );
        let _ = writeln!(text, "source feed: {}", self.preview_source);
        let _ = writeln!(text, "display limit: {}", self.client_display_limit);
        let _ = writeln!(text);
        let _ = writeln!(text, "left eye");
        let _ = writeln!(text, "  surface: {}", self.left_surface);
        let _ = writeln!(text, "  source: {}", self.left_feed_source);
        let _ = writeln!(text, "  capture: {}", self.left_capture_profile);
        let _ = writeln!(text, "  transport: {}", self.left_capture_transport);
        let _ = writeln!(text, "  breakout: {}", self.left_breakout_profile);
        let _ = writeln!(
            text,
            "  live: decoder={} spread={:.1}ms gaps={:.0}/{:.0}ms queue={}/{}",
            self.left_decoder_label,
            self.left_stream_spread_ms,
            self.left_packet_gap_peak_ms,
            self.left_present_gap_peak_ms,
            self.left_queue_depth,
            self.left_queue_peak
        );
        let _ = writeln!(text, "  stream caps: {}", self.left_stream_caps_label);
        let _ = writeln!(text, "  decoded caps: {}", self.left_decoded_caps_label);
        let _ = writeln!(text, "  rendered caps: {}", self.left_rendered_caps_label);
        let _ = writeln!(
            text,
            "  server: encoder={} cpu={:.1}% gaps={:.0}/{:.0}ms queue-peak={}",
            self.left_server_encoder_label,
            self.server_process_cpu_pct,
            self.left_server_source_gap_peak_ms,
            self.left_server_send_gap_peak_ms,
            self.left_server_queue_peak
        );
        let _ = writeln!(text, "right eye");
        let _ = writeln!(text, "  surface: {}", self.right_surface);
        let _ = writeln!(text, "  source: {}", self.right_feed_source);
        let _ = writeln!(text, "  capture: {}", self.right_capture_profile);
        let _ = writeln!(text, "  transport: {}", self.right_capture_transport);
        let _ = writeln!(text, "  breakout: {}", self.right_breakout_profile);
        let _ = writeln!(
            text,
            "  live: decoder={} spread={:.1}ms gaps={:.0}/{:.0}ms queue={}/{}",
            self.right_decoder_label,
            self.right_stream_spread_ms,
            self.right_packet_gap_peak_ms,
            self.right_present_gap_peak_ms,
            self.right_queue_depth,
            self.right_queue_peak
        );
        let _ = writeln!(text, "  stream caps: {}", self.right_stream_caps_label);
        let _ = writeln!(text, "  decoded caps: {}", self.right_decoded_caps_label);
        let _ = writeln!(text, "  rendered caps: {}", self.right_rendered_caps_label);
        let _ = writeln!(
            text,
            "  server: encoder={} cpu={:.1}% gaps={:.0}/{:.0}ms queue-peak={}",
            self.right_server_encoder_label,
            self.server_process_cpu_pct,
            self.right_server_source_gap_peak_ms,
            self.right_server_send_gap_peak_ms,
            self.right_server_queue_peak
        );
        let _ = writeln!(text);
        let _ = writeln!(text, "media staging");
        let _ = writeln!(
            text,
            "  camera: {} | quality={} | enabled={}",
            self.selected_camera.as_deref().unwrap_or("auto"),
            self.camera_quality_label,
            self.media_channels.camera
        );
        let _ = writeln!(
            text,
            "  speaker: {} | volume={} | enabled={}",
            self.selected_speaker.as_deref().unwrap_or("auto"),
            self.audio_gain_label,
            self.media_channels.audio
        );
        let _ = writeln!(
            text,
            "  microphone: {} | gain={} | enabled={}",
            self.selected_microphone.as_deref().unwrap_or("auto"),
            self.mic_gain_label,
            self.media_channels.microphone
        );
        let _ = writeln!(
            text,
            "  uplink camera: {}",
            uplink_summary(&self.upstream_camera)
        );
        let _ = writeln!(
            text,
            "  uplink microphone: {}",
            uplink_summary(&self.upstream_microphone)
        );
        let _ = writeln!(
            text,
            "  keyboard: {}",
            self.selected_keyboard.as_deref().unwrap_or("all")
        );
        let _ = writeln!(
            text,
            "  mouse: {}",
            self.selected_mouse.as_deref().unwrap_or("all")
        );
        let _ = writeln!(text);
        let _ = writeln!(text, "current UI state");
        let _ = writeln!(text, "  {}", self.status);
        let _ = writeln!(text);
        let _ = writeln!(text, "recent samples");
        if self.recent_samples.is_empty() {
            let _ = writeln!(
                text,
                "  no live RTT/probe-spread/loss samples yet; this report is currently a launcher state snapshot."
            );
        } else {
            for sample in &self.recent_samples {
                let _ = writeln!(
                    text,
                    "  rtt={:.1}ms probe-spread={:.1}ms input-floor={:.1}ms cpu={:.1}/{:.1}% probe-loss={:.1}% video-loss={:.1}% left={:.1}/{:.1}/{:.1}fps right={:.1}/{:.1}/{:.1}fps dropped={} queue={}/{} peaks=l{:.0}/{:.0}ms r{:.0}/{:.0}ms server=l{}:{:.0}/{:.0}/{} r{}:{:.0}/{:.0}/{}",
                    sample.rtt_ms,
                    sample.probe_spread_ms,
                    sample.input_latency_ms,
                    sample.client_process_cpu_pct,
                    sample.server_process_cpu_pct,
                    sample.probe_loss_pct,
                    sample.video_loss_pct,
                    sample.left_receive_fps,
                    sample.left_present_fps,
                    sample.left_server_fps,
                    sample.right_receive_fps,
                    sample.right_present_fps,
                    sample.right_server_fps,
                    sample.dropped_frames,
                    sample.queue_depth,
                    sample.left_queue_peak.max(sample.right_queue_peak),
                    sample.left_packet_gap_peak_ms,
                    sample.left_present_gap_peak_ms,
                    sample.right_packet_gap_peak_ms,
                    sample.right_present_gap_peak_ms,
                    sample.left_server_encoder_label,
                    sample.left_server_source_gap_peak_ms,
                    sample.left_server_send_gap_peak_ms,
                    sample.left_server_queue_peak,
                    sample.right_server_encoder_label,
                    sample.right_server_source_gap_peak_ms,
                    sample.right_server_send_gap_peak_ms,
                    sample.right_server_queue_peak
                );
                let _ = writeln!(
                    text,
                    "    uplink: cam={} mic={}",
                    uplink_summary(&sample.upstream_camera),
                    uplink_summary(&sample.upstream_microphone)
                );
            }
        }
        let _ = writeln!(text);
        let _ = writeln!(text, "recommendations");
        for item in &self.recommendations {
            let _ = writeln!(text, "  - {item}");
        }
        if !self.notes.is_empty() {
            let _ = writeln!(text);
            let _ = writeln!(text, "notes");
            for item in &self.notes {
                let _ = writeln!(text, "  - {item}");
            }
        }
        let _ = writeln!(text);
        let _ = writeln!(text, "quality probe");
        let _ = writeln!(text, "  {}", self.probe_command);
        text
    }
}

fn uplink_summary(stream: &crate::uplink_telemetry::UpstreamStreamTelemetry) -> String {
    if !stream.enabled {
        return "disabled".to_string();
    }
    let connection = if stream.connected {
        "live"
    } else if stream.reconnect_count > 0 {
        "reconnecting"
    } else {
        "idle"
    };
    let error = if stream.last_error.is_empty() {
        "ok".to_string()
    } else {
        stream.last_error.clone()
    };
    format!(
        "{connection} queue={}/{} enq-age={:.0}/{:.0}ms delivery={:.0}/{:.0}ms block-peak={:.0}ms reconnects={} streamed={} drops(total/full/stale)={}/{}/{} error={error}",
        stream.queue_depth,
        stream.queue_peak,
        stream.latest_enqueue_age_ms,
        stream.enqueue_age_peak_ms,
        stream.latest_delivery_age_ms,
        stream.delivery_age_peak_ms,
        stream.enqueue_block_peak_ms,
        stream.reconnect_count,
        stream.packets_streamed,
        stream.dropped_packets,
        stream.dropped_queue_full_packets,
        stream.dropped_stale_packets
    )
}
