impl Handler {
    /// Reopen HID handles and verify enumeration without cycling the USB gadget.
    async fn recover_usb_reply(&self) -> Result<Response<ResetUsbReply>, Status> {
        #[cfg(not(coverage))]
        info!("🛟 RecoverUsb() soft recovery called");

        self.reopen_hid()
            .await
            .map_err(|e| Status::internal(format!("soft USB HID reopen failed: {e:#}")))?;

        match current_controller_state_after_recovery() {
            Ok((ctrl, state)) if UsbGadget::host_enumerated_state(&state) => {
                #[cfg(not(coverage))]
                info!("✅ soft USB recovery verified host enumeration ctrl={ctrl} state={state}");
                Ok(Response::new(ResetUsbReply { ok: true }))
            }
            Ok((ctrl, state)) => Err(Status::failed_precondition(format!(
                "soft USB recovery refused hard reset: UDC {ctrl} is {state}; use explicit hard ResetUsb only after closing remote UVC/audio consumers"
            ))),
            Err(err) => Err(Status::failed_precondition(format!(
                "soft USB recovery could not read UDC state: {err:#}"
            ))),
        }
    }

    async fn recover_uac_reply(&self) -> Result<Response<ResetUsbReply>, Status> {
        #[cfg(not(coverage))]
        info!("🛟 RecoverUac() soft recovery called");

        self.upstream_media_rt.soft_recover_microphone();
        Ok(Response::new(ResetUsbReply { ok: true }))
    }

    /// Retire the current webcam relay sink without detaching the UVC gadget.
    async fn recover_uvc_reply(&self) -> Result<Response<ResetUsbReply>, Status> {
        #[cfg(not(coverage))]
        info!("🛟 RecoverUvc() soft recovery called");

        let cfg = camera::current_camera_config();
        if cfg.output != camera::CameraOutput::Uvc {
            return Err(Status::failed_precondition(format!(
                "soft UVC recovery skipped because active camera output is {}",
                cfg.output.as_str()
            )));
        }

        self.camera_rt.soft_recover().await;
        Ok(Response::new(ResetUsbReply { ok: true }))
    }

    /// Keeps `paste_text_reply` explicit because it sits on relay RPC orchestration, where hardware failures must surface without stopping the server.
    /// Inputs are the typed parameters; output is the return value or side effect.
    async fn paste_text_reply(
        &self,
        req: Request<PasteRequest>,
    ) -> Result<Response<PasteReply>, Status> {
        let req = req.into_inner();
        let text = paste::decrypt(&req).map_err(|e| Status::unauthenticated(format!("{e}")))?;
        if let Err(e) = paste::type_text(&self.kb, &hid_endpoint(0), &text).await {
            return Ok(Response::new(PasteReply {
                ok: false,
                error: format!("{e}"),
            }));
        }
        Ok(Response::new(PasteReply {
            ok: true,
            error: String::new(),
        }))
    }

    /// Keeps `reset_usb_reply` explicit because it sits on relay RPC orchestration, where hardware failures must surface without stopping the server.
    /// Inputs are the typed parameters; output is the return value or side effect.
    async fn reset_usb_reply(&self) -> Result<Response<ResetUsbReply>, Status> {
        #[cfg(not(coverage))]
        info!("🔴 explicit ResetUsb() called");

        match self.gadget.recover_enumeration() {
            Ok(_) => {
                if let Err(e) = self.reopen_hid().await {
                    #[cfg(not(coverage))]
                    error!("💥 reopen HID failed: {e:#}");
                    return Err(Status::internal(e.to_string()));
                }
                if let Err(e) = restart_uvc_helper() {
                    #[cfg(not(coverage))]
                    error!("💥 restart UVC helper failed: {e:#}");
                    return Err(Status::internal(e.to_string()));
                }
                match current_controller_state_after_recovery() {
                    Ok((ctrl, state)) if UsbGadget::host_enumerated_state(&state) => {
                        #[cfg(not(coverage))]
                        info!(
                            "✅ USB host enumerated gadget after recovery ctrl={ctrl} state={state}"
                        );
                    }
                    Ok((ctrl, state)) => {
                        let message = format!(
                            "USB gadget recovery ran, but UDC {ctrl} is still {state}; the controlled host has not enumerated the relay HID/audio/video gadget"
                        );
                        #[cfg(not(coverage))]
                        warn!("⚠️ {message}");
                        return Err(Status::failed_precondition(message));
                    }
                    Err(err) => {
                        let message = format!(
                            "USB gadget recovery ran, but the relay cannot read UDC state: {err:#}"
                        );
                        #[cfg(not(coverage))]
                        warn!("⚠️ {message}");
                        return Err(Status::failed_precondition(message));
                    }
                }
                Ok(Response::new(ResetUsbReply { ok: true }))
            }
            Err(e) => {
                #[cfg(not(coverage))]
                error!("💥 USB recovery failed: {e:#}");
                let message = format!("{e:#}");
                if message.contains("still not attached") || message.contains("not attached") {
                    Err(Status::failed_precondition(message))
                } else {
                    Err(Status::internal(message))
                }
            }
        }
    }

    async fn get_capture_power_reply(&self) -> Result<Response<CapturePowerState>, Status> {
        let state = self
            .capture_power
            .snapshot()
            .await
            .map_err(|e| Status::internal(format!("{e:#}")))?;
        Ok(Response::new(
            self.with_detected_capture_devices(state).await,
        ))
    }

    /// Keeps `set_capture_power_reply` explicit because it sits on relay RPC orchestration, where hardware failures must surface without stopping the server.
    /// Inputs are the typed parameters; output is the return value or side effect.
    async fn set_capture_power_reply(
        &self,
        req: Request<SetCapturePowerRequest>,
    ) -> Result<Response<CapturePowerState>, Status> {
        let req = req.into_inner();
        let result = match CapturePowerCommand::try_from(req.command)
            .unwrap_or(CapturePowerCommand::Unspecified)
        {
            CapturePowerCommand::Auto => self.capture_power.set_auto().await,
            CapturePowerCommand::ForceOn => self.capture_power.set_manual(true).await,
            CapturePowerCommand::ForceOff => self.capture_power.set_manual(false).await,
            CapturePowerCommand::Unspecified => self.capture_power.set_manual(req.enabled).await,
        };
        let state = result.map_err(|e| Status::internal(format!("{e:#}")))?;
        Ok(Response::new(
            self.with_detected_capture_devices(state).await,
        ))
    }

    async fn get_calibration_reply(&self) -> Result<Response<CalibrationState>, Status> {
        Ok(Response::new(self.calibration.current()))
    }

    async fn calibrate_reply(
        &self,
        req: Request<CalibrationRequest>,
    ) -> Result<Response<CalibrationState>, Status> {
        self.calibration
            .apply(req.into_inner())
            .map(Response::new)
            .map_err(|e| Status::internal(format!("{e:#}")))
    }

    /// Keeps `get_upstream_sync_reply` explicit because it sits on relay RPC orchestration, where hardware failures must surface without stopping the server.
    /// Inputs are the typed parameters; output is the return value or side effect.
    async fn get_upstream_sync_reply(&self) -> Result<Response<UpstreamSyncState>, Status> {
        let snapshot = self.upstream_media_rt.snapshot();
        Ok(Response::new(UpstreamSyncState {
            session_id: snapshot.session_id,
            phase: snapshot.phase.to_string(),
            latest_camera_remote_pts_us: snapshot.latest_camera_remote_pts_us,
            latest_microphone_remote_pts_us: snapshot.latest_microphone_remote_pts_us,
            last_video_presented_pts_us: snapshot.last_video_presented_pts_us,
            last_audio_presented_pts_us: snapshot.last_audio_presented_pts_us,
            live_lag_ms: snapshot.live_lag_ms.map(|value| value as f32),
            planner_skew_ms: snapshot.planner_skew_ms.map(|value| value as f32),
            stale_audio_drops: snapshot.stale_audio_drops,
            stale_video_drops: snapshot.stale_video_drops,
            skew_video_drops: snapshot.skew_video_drops,
            freshness_reanchors: snapshot.freshness_reanchors,
            startup_timeouts: snapshot.startup_timeouts,
            video_freezes: snapshot.video_freezes,
            last_reason: snapshot.last_reason,
            client_capture_skew_ms: snapshot.client_capture_skew_ms.map(|value| value as f32),
            client_send_skew_ms: snapshot.client_send_skew_ms.map(|value| value as f32),
            server_receive_skew_ms: snapshot.server_receive_skew_ms.map(|value| value as f32),
            camera_client_queue_age_ms: snapshot
                .camera_client_queue_age_ms
                .map(|value| value as f32),
            microphone_client_queue_age_ms: snapshot
                .microphone_client_queue_age_ms
                .map(|value| value as f32),
            camera_server_receive_age_ms: snapshot
                .camera_server_receive_age_ms
                .map(|value| value as f32),
            microphone_server_receive_age_ms: snapshot
                .microphone_server_receive_age_ms
                .map(|value| value as f32),
            client_capture_abs_skew_p95_ms: snapshot
                .client_capture_abs_skew_p95_ms
                .map(|value| value as f32),
            client_send_abs_skew_p95_ms: snapshot
                .client_send_abs_skew_p95_ms
                .map(|value| value as f32),
            server_receive_abs_skew_p95_ms: snapshot
                .server_receive_abs_skew_p95_ms
                .map(|value| value as f32),
            camera_client_queue_age_p95_ms: snapshot
                .camera_client_queue_age_p95_ms
                .map(|value| value as f32),
            microphone_client_queue_age_p95_ms: snapshot
                .microphone_client_queue_age_p95_ms
                .map(|value| value as f32),
            sink_handoff_skew_ms: snapshot.sink_handoff_skew_ms.map(|value| value as f32),
            sink_handoff_abs_skew_p95_ms: snapshot
                .sink_handoff_abs_skew_p95_ms
                .map(|value| value as f32),
            camera_sink_late_ms: snapshot.camera_sink_late_ms.map(|value| value as f32),
            microphone_sink_late_ms: snapshot
                .microphone_sink_late_ms
                .map(|value| value as f32),
            camera_sink_late_p95_ms: snapshot
                .camera_sink_late_p95_ms
                .map(|value| value as f32),
            microphone_sink_late_p95_ms: snapshot
                .microphone_sink_late_p95_ms
                .map(|value| value as f32),
            client_timing_window_samples: snapshot.client_timing_window_samples,
            sink_handoff_window_samples: snapshot.sink_handoff_window_samples,
        }))
    }
}
