impl UsbGadget {
    fn recover_enumeration_internal(&self) -> Result<()> {
        let mut steps = Vec::new();
        steps.push(format!("initial {}", Self::current_state_detail()));

        let cycle_ok = match self.cycle_forced() {
            Ok(()) => {
                steps.push("forced UDC cycle succeeded".to_string());
                true
            }
            Err(err) => {
                steps.push(format!("forced UDC cycle failed: {err:#}"));
                false
            }
        };
        if cycle_ok
            && let Some((ctrl, state)) =
                Self::wait_for_host_attach(Self::recovery_wait_ms("CYCLE", 2_000))
            {
                info!("✅ USB host enumerated after UDC cycle ctrl={ctrl} state={state}");
                return Ok(());
            }

        if !Self::rebuild_helper_available() {
            anyhow::bail!(
                "USB gadget recovery cannot continue because no UDC/controller is available for forced rebuild; {}",
                steps.join("; ")
            );
        }

        match self.run_forced_core_rebuild() {
            Ok(summary) => steps.push(summary),
            Err(err) => steps.push(format!("forced core rebuild failed: {err:#}")),
        }
        if let Some((ctrl, state)) =
            Self::wait_for_host_attach(Self::recovery_wait_ms("REBUILD", 8_000))
        {
            info!("✅ USB host enumerated after forced gadget rebuild ctrl={ctrl} state={state}");
            return Ok(());
        }

        match self.cycle_forced() {
            Ok(()) => steps.push("post-rebuild UDC cycle succeeded".to_string()),
            Err(err) => steps.push(format!("post-rebuild UDC cycle failed: {err:#}")),
        }
        if let Some((ctrl, state)) =
            Self::wait_for_host_attach(Self::recovery_wait_ms("FINAL", 4_000))
        {
            info!("✅ USB host enumerated after post-rebuild UDC cycle ctrl={ctrl} state={state}");
            return Ok(());
        }

        anyhow::bail!(
            "USB gadget is still not attached after aggressive recovery; current {}; steps: {}",
            Self::current_state_detail(),
            steps.join("; ")
        )
    }

    fn rebuild_helper_available() -> bool {
        Self::find_controller().is_ok()
            || matches!(Self::probe_platform_udc(), Ok(Some(_)))
            || env::var("LESAVKA_FORCE_CORE_REBUILD_WITHOUT_UDC").is_ok()
    }

    fn wait_for_host_attach(limit_ms: u64) -> Option<(String, String)> {
        for _ in 0..=limit_ms / 100 {
            if let Ok((ctrl, state)) = Self::current_controller_state()
                && Self::host_enumerated_state(&state) {
                    return Some((ctrl, state));
                }
            thread::sleep(Duration::from_millis(100));
        }
        None
    }

    fn recovery_wait_ms(step: &str, default_ms: u64) -> u64 {
        env::var(format!("LESAVKA_USB_RECOVERY_{step}_WAIT_MS"))
            .ok()
            .and_then(|value| value.parse::<u64>().ok())
            .unwrap_or(default_ms)
    }

    fn core_helper_path() -> String {
        env::var("LESAVKA_CORE_HELPER").unwrap_or_else(|_| "/usr/local/bin/lesavka-core.sh".into())
    }

    fn uvc_fallback_mode() -> String {
        env::var("LESAVKA_UVC_FALLBACK").unwrap_or_else(|_| "0".to_string())
    }

    fn run_forced_core_rebuild(&self) -> Result<String> {
        let helper = Self::core_helper_path();
        let output = Command::new(&helper)
            .env("LESAVKA_ALLOW_GADGET_RESET", "1")
            .env("LESAVKA_ATTACH_WRITE_UDC", "1")
            .env("LESAVKA_DETACH_CLEAR_UDC", "1")
            .env("LESAVKA_RELOAD_UVCVIDEO", "1")
            .env("LESAVKA_UVC_FALLBACK", Self::uvc_fallback_mode())
            .env(
                "LESAVKA_UVC_CODEC",
                env::var("LESAVKA_UVC_CODEC").unwrap_or_else(|_| "mjpeg".to_string()),
            )
            .output()
            .with_context(|| format!("running {helper} with forced gadget rebuild"))?;

        let stdout = Self::tail_text(&output.stdout);
        let stderr = Self::tail_text(&output.stderr);
        if !output.status.success() {
            anyhow::bail!(
                "forced gadget rebuild helper exited with {}; stderr: {}; stdout: {}",
                output.status,
                stderr,
                stdout
            );
        }

        Ok(format!(
            "forced gadget rebuild helper succeeded: stderr: {}; stdout: {}",
            stderr, stdout
        ))
    }

    fn tail_text(bytes: &[u8]) -> String {
        let text = String::from_utf8_lossy(bytes).trim().to_string();
        const LIMIT: usize = 1_200;
        if text.chars().count() <= LIMIT {
            return text;
        }
        let tail: String = text
            .chars()
            .rev()
            .take(LIMIT)
            .collect::<String>()
            .chars()
            .rev()
            .collect();
        format!("...{tail}")
    }

}
