impl Handler {
    async fn new(gadget: UsbGadget) -> anyhow::Result<Self> {
        #[cfg(not(coverage))]
        if runtime_support::allow_gadget_cycle() {
            info!("🛠️  Initial USB recovery…");
            match UsbGadget::current_controller_state() {
                Ok((ctrl, state)) if !UsbGadget::host_enumerated_state(&state) => {
                    warn!("⚠️ UDC {ctrl} is {state}; forcing gadget recovery before opening HID");
                    if let Err(error) = gadget.recover_enumeration() {
                        warn!("⚠️ initial USB recovery did not enumerate the host: {error:#}");
                    }
                }
                Ok(_) => {
                    let _ = gadget.cycle(); // ignore failure - may boot without host
                }
                Err(error) => {
                    warn!("⚠️ UDC state unavailable during startup: {error:#}");
                    let _ = gadget.cycle(); // preserve the old best-effort startup path
                }
            }
        }
        #[cfg(not(coverage))]
        {
            if !runtime_support::allow_gadget_cycle() {
                if runtime_support::external_uvc_helper_owns_gadget() {
                    info!(
                        "🔒 gadget cycle disabled at startup because external UVC helper owns the gadget"
                    );
                } else {
                    info!(
                        "🔒 gadget cycle disabled at startup (set LESAVKA_ALLOW_GADGET_CYCLE=1 to enable)"
                    );
                }
            }
            info!("🛠️  opening HID endpoints …");
        }
        let kb_path = hid_endpoint(0);
        let ms_path = hid_endpoint(1);
        let kb = runtime_support::open_hid_if_ready(&kb_path).await?;
        let ms = runtime_support::open_hid_if_ready(&ms_path).await?;
        #[cfg(not(coverage))]
        if kb.is_some() && ms.is_some() {
            info!("✅ HID endpoints ready");
        } else {
            warn!("⌛ HID endpoints are not ready; relay will keep running and open them lazily");
        }

        let upstream_media_rt = Arc::new(UpstreamMediaRuntime::new());
        let calibration = Arc::new(CalibrationStore::load(upstream_media_rt.clone()));
        #[cfg(not(coverage))]
        lesavka_server::blind_healer::spawn_blind_healer(
            upstream_media_rt.clone(),
            calibration.clone(),
        );

        Ok(Self {
            kb: Arc::new(Mutex::new(kb)),
            ms: Arc::new(Mutex::new(ms)),
            gadget,
            did_cycle: Arc::new(AtomicBool::new(false)),
            camera_rt: Arc::new(CameraRuntime::new()),
            upstream_media_rt,
            calibration,
            capture_power: CapturePowerManager::new(),
            eye_hubs: Arc::new(Mutex::new(HashMap::new())),
        })
    }

    async fn reopen_hid(&self) -> anyhow::Result<()> {
        let kb_new = runtime_support::open_hid_if_ready(&hid_endpoint(0)).await?;
        let ms_new = runtime_support::open_hid_if_ready(&hid_endpoint(1)).await?;
        *self.kb.lock().await = kb_new;
        *self.ms.lock().await = ms_new;
        Ok(())
    }

    fn detected_capture_devices_from_symlinks() -> u32 {
        ["/dev/lesavka_l_eye", "/dev/lesavka_r_eye"]
            .into_iter()
            .filter(|path| {
                std::fs::metadata(path)
                    .ok()
                    .is_some_and(|metadata| metadata.file_type().is_char_device())
            })
            .count() as u32
    }

    #[cfg(not(coverage))]
    fn detected_capture_devices_from_udev() -> u32 {
        let Ok(mut enumerator) = udev::Enumerator::new() else {
            return 0;
        };
        let _ = enumerator.match_subsystem("video4linux");
        let Ok(devices) = enumerator.scan_devices() else {
            return 0;
        };
        devices
            .filter(|device| {
                device
                    .attribute_value("index")
                    .and_then(|value| value.to_str())
                    == Some("0")
                    && device
                        .property_value("ID_VENDOR_ID")
                        .and_then(|value| value.to_str())
                        == Some("07ca")
                    && device
                        .property_value("ID_MODEL_ID")
                        .and_then(|value| value.to_str())
                        == Some("3311")
            })
            .count()
            .min(2) as u32
    }

    #[cfg(coverage)]
    fn detected_capture_devices_from_udev() -> u32 {
        std::env::var("LESAVKA_TEST_UDEV_CAPTURE_DEVICES")
            .ok()
            .and_then(|value| value.parse::<u32>().ok())
            .unwrap_or(0)
            .min(2)
    }

    async fn active_eye_source_count(&self) -> u32 {
        self.eye_hubs
            .lock()
            .await
            .iter()
            .filter_map(|(key, hub)| hub.running.load(Ordering::Relaxed).then_some(key.source_id))
            .collect::<HashSet<_>>()
            .len()
            .min(2) as u32
    }

    async fn with_detected_capture_devices(
        &self,
        mut state: CapturePowerState,
    ) -> CapturePowerState {
        state.detected_devices = Self::detected_capture_devices_from_udev()
            .max(Self::detected_capture_devices_from_symlinks())
            .max(self.active_eye_source_count().await);
        state
    }
}
