impl UsbGadget {
    fn sysfs_root() -> String {
        env::var("LESAVKA_GADGET_SYSFS_ROOT").unwrap_or_else(|_| "/sys".to_string())
    }

    fn configfs_root() -> String {
        env::var("LESAVKA_GADGET_CONFIGFS_ROOT")
            .unwrap_or_else(|_| "/sys/kernel/config/usb_gadget".to_string())
    }

    pub fn new(name: &'static str) -> Self {
        Self {
            udc_file: Box::leak(
                format!("{}/{}{}", Self::configfs_root(), name, "/UDC").into_boxed_str(),
            ),
        }
    }

    pub fn state(ctrl: &str) -> anyhow::Result<String> {
        let p = format!("{}/class/udc/{ctrl}/state", Self::sysfs_root());
        Ok(std::fs::read_to_string(p)?.trim().to_owned())
    }

    pub fn current_controller_state() -> anyhow::Result<(String, String)> {
        let ctrl = Self::find_controller()?;
        let state = Self::state(&ctrl)?;
        Ok((ctrl, state))
    }

    pub fn host_attached_state(state: &str) -> bool {
        matches!(
            state,
            "configured" | "addressed" | "default" | "suspended" | "unknown"
        )
    }

    pub fn host_enumerated_state(state: &str) -> bool {
        matches!(state, "configured" | "addressed" | "default" | "suspended")
    }

    pub fn current_state_detail() -> String {
        match Self::current_controller_state() {
            Ok((ctrl, state)) => format!("UDC {ctrl} state={state}"),
            Err(err) => format!("UDC state unavailable: {err:#}"),
        }
    }

    /*---- helpers ----*/

    /// Find the first controller in /sys/class/udc (e.g. `1000480000.usb`)
    pub fn find_controller() -> Result<String> {
        Ok(fs::read_dir(format!("{}/class/udc", Self::sysfs_root()))?
            .next()
            .transpose()?
            .context("no UDC present")?
            .file_name()
            .to_string_lossy()
            .into_owned())
    }

    /// Busy-loop (≤ `limit_ms`) until `state` matches `wanted`
    fn wait_state(ctrl: &str, wanted: &str, limit_ms: u64) -> Result<()> {
        let path = format!("{}/class/udc/{ctrl}/state", Self::sysfs_root());
        for _ in 0..=limit_ms / 50 {
            let s = fs::read_to_string(&path).unwrap_or_default();
            trace!("⏳ state={s:?}, want={wanted}");
            if s.trim() == wanted {
                return Ok(());
            }
            thread::sleep(Duration::from_millis(50));
        }
        Err(anyhow::anyhow!(
            "UDC never reached '{wanted}' (last = {:?})",
            fs::read_to_string(&path).unwrap_or_default()
        ))
    }

    pub fn wait_state_any(ctrl: &str, limit_ms: u64) -> anyhow::Result<String> {
        let path = format!("{}/class/udc/{ctrl}/state", Self::sysfs_root());
        for _ in 0..=limit_ms / 50 {
            if let Ok(s) = std::fs::read_to_string(&path) {
                let s = s.trim();
                if matches!(s, "configured" | "not attached") {
                    return Ok(s.to_owned());
                }
            }
            std::thread::sleep(std::time::Duration::from_millis(50));
        }
        Err(anyhow::anyhow!(
            "UDC state did not settle within {limit_ms} ms"
        ))
    }

    /// Write `value` (plus “\n”) into a sysfs attribute
    fn write_attr<P: AsRef<Path>>(p: P, value: &str) -> Result<()> {
        OpenOptions::new()
            .write(true)
            .open(p)?
            .write_all(format!("{value}\n").as_bytes())?;
        Ok(())
    }

    // Wait (≤ `limit_ms`) until `/sys/class/udc/<ctrl>` exists again.
    fn wait_udc_present(ctrl: &str, limit_ms: u64) -> Result<()> {
        for _ in 0..=limit_ms / 50 {
            if Path::new(&format!("{}/class/udc/{ctrl}", Self::sysfs_root())).exists() {
                return Ok(());
            }
            thread::sleep(Duration::from_millis(50));
        }
        Err(anyhow::anyhow!(
            "⚠️ UDC {ctrl} did not re-appear within {limit_ms} ms"
        ))
    }

    /// Scan platform devices when /sys/class/udc is empty
    fn probe_platform_udc() -> Result<Option<String>> {
        for entry in fs::read_dir(format!("{}/bus/platform/devices", Self::sysfs_root()))? {
            let p = entry?.file_name().into_string().unwrap();
            if p.ends_with(".usb") {
                return Ok(Some(p));
            }
        }
        Ok(None)
    }

    fn platform_driver(ctrl: &str) -> Option<String> {
        fs::read_link(format!(
            "{}/bus/platform/devices/{ctrl}/driver",
            Self::sysfs_root()
        ))
        .ok()
        .and_then(|path| path.file_name().map(|name| name.to_string_lossy().into_owned()))
    }

    fn soft_connect_path(ctrl: &str) -> Option<String> {
        let path = format!("{}/class/udc/{ctrl}/soft_connect", Self::sysfs_root());
        if !Path::new(&path).exists() {
            return None;
        }
        if env::var("LESAVKA_FORCE_SOFT_CONNECT").ok().as_deref() == Some("1") {
            return Some(path);
        }
        if Self::platform_driver(ctrl).as_deref() == Some("dwc2") {
            return None;
        }
        Some(path)
    }

}
