//! Minted punktfunk-owned audio endpoints — the Windows audio substrate.
//!
//! The audio-substrate decision (`windows-audio-endpoints-and-vbcable.md`, 2026-08-07, spikes
//! S2+S3 measured green): instead of borrowing Steam's primary endpoints and bundling VB-Cable
//! for the mic, the host mints its OWN instances of Valve's streaming-audio drivers —
//!
//! * **"Punktfunk Speakers"** (`SteamStreamingSpeakers.inf`): the client-only loopback sink.
//!   Desktop audio routes here (the wiring plan parks the default playback on it during a
//!   stream), its WASAPI loopback feeds the encoder, and the host stays silent. Measured
//!   clean: 48 kHz stereo f32, loopback peak == rendered peak (S2).
//! * **"Punktfunk Microphone"** (`SteamStreamingMicrophone.inf`): the virtual mic. The host
//!   writes the client's decoded voice into its render side; its capture side surfaces as the
//!   microphone host apps record. Measured bit-faithful render→capture (S3).
//!
//! Provisioning mirrors the pad-audio provider: a background worker at host start, idempotent
//! devnode-per-role with a durable `PunktfunkAudioRole` marker in `Device Parameters` (names
//! are NOT identity — a minted instance is name-identical to Steam's primaries), results
//! published once for the wiring plan to consume BY ID ([`minted_ids`] →
//! [`wiring_plan::MintedIds`]). Everything is best-effort: no Steam driver, a denied install,
//! or `PUNKTFUNK_NO_AUDIO_MINT` leaves the ids empty and the wiring plan falls back to the
//! name-based ladder (Steam primaries → cable → real hardware) unchanged.
//!
//! Endpoints are PERSISTENT by design, like pad endpoints — they survive host restarts and
//! re-resolve by marker on the next start. `punktfunk-host audio-probe` carries the manual
//! `mint` / `plan` inspection paths.

use super::pad_endpoint as pe;
use super::{audio_control, wiring_plan};
use anyhow::{bail, Context, Result};
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::{Arc, Mutex, OnceLock};
use std::thread;
use std::time::{Duration, Instant};

/// Durable role marker in a minted devnode's `Device Parameters` key. pub(crate): the uninstall
/// sweep ([`devnode_cleanup`](super::devnode_cleanup)) matches devnodes on it.
pub(crate) const ROLE_MARKER: &str = "PunktfunkAudioRole";
/// How long to wait for audiosrv to register a freshly minted endpoint.
const ENDPOINT_WAIT: Duration = Duration::from_secs(15);
/// Minimum spacing between provisioning retries once the startup attempt failed
/// ([`ensure_provisioned`] is called from wiring passes, which recur freely).
const RETRY_COOLDOWN: Duration = Duration::from_secs(60);
/// Full passes that ended unlatched before minting gives up for this host lifetime (a service
/// restart re-arms). An unlatched pass that reaches the PnP surface costs the whole BOX, not
/// just us: the driver (re)bind raises a device-change broadcast every running app services,
/// and games rebuild their audio graph on it — a box that cannot mint must not pay that on
/// every retry forever (field-measured 2026-08-12 as Helldivers 2 hitching to 2–5 FPS 1% lows,
/// one hitch per mic-pump reopen).
const MAX_UNLATCHED_ATTEMPTS: u32 = 5;
/// How long [`ensure_blocking`] waits on a pass another thread already runs before giving the
/// wiring plan the unlatched answer (a full cold-boot pass worst-cases around two
/// [`ENDPOINT_WAIT`]s plus the stamp settles).
const BLOCKING_WAIT: Duration = Duration::from_secs(90);

/// The two minted roles. `value` is the persisted marker; the needles drive
/// [`discover_driver`].
#[derive(Clone, Copy, PartialEq)]
enum Role {
    Speakers,
    Mic,
}

impl Role {
    fn value(self) -> u32 {
        match self {
            Role::Speakers => 1,
            Role::Mic => 2,
        }
    }
    fn desc(self) -> &'static str {
        match self {
            Role::Speakers => "Punktfunk Speakers",
            Role::Mic => "Punktfunk Microphone",
        }
    }
    fn needle(self) -> &'static str {
        match self {
            Role::Speakers => "steamstreamingspeakers",
            Role::Mic => "steamstreamingmicrophone",
        }
    }
    fn inf_name(self) -> &'static str {
        match self {
            Role::Speakers => "SteamStreamingSpeakers.inf",
            Role::Mic => "SteamStreamingMicrophone.inf",
        }
    }
    fn label(self) -> &'static str {
        match self {
            Role::Speakers => "speakers",
            Role::Mic => "mic",
        }
    }
}

/// The provider's published result. Partial is possible and usable (one driver leg failing
/// must not cost the other role); consumers read the per-role `Option`s.
#[derive(Debug, Default, Clone)]
pub(crate) struct MintedAudio {
    pub speakers_devnode: Option<String>,
    pub speakers_render: Option<String>,
    pub mic_devnode: Option<String>,
    pub mic_render: Option<String>,
    pub mic_capture: Option<String>,
}

impl MintedAudio {
    fn any(&self) -> bool {
        self.speakers_render.is_some() || self.mic_render.is_some()
    }
}

/// Set once by the worker, and only when at least one role provisioned (the pad provider's R5
/// lesson: latching an empty result turns one transient failure into a process-lifetime
/// disability).
static PROVISIONED: OnceLock<Arc<MintedAudio>> = OnceLock::new();
/// A provisioning attempt is in flight — keeps concurrent askers to one worker.
static PROVISIONING: AtomicBool = AtomicBool::new(false);
/// When the last attempt STARTED — the [`RETRY_COOLDOWN`] anchor.
static LAST_ATTEMPT: Mutex<Option<Instant>> = Mutex::new(None);
/// Completed passes that did not latch, across the worker and the blocking path — the
/// [`MAX_UNLATCHED_ATTEMPTS`] give-up counter.
static UNLATCHED_ATTEMPTS: AtomicU32 = AtomicU32::new(0);

/// Count one finished-but-unlatched pass; the crossing attempt logs the give-up exactly once.
fn record_unlatched_attempt() {
    let n = UNLATCHED_ATTEMPTS.fetch_add(1, Ordering::SeqCst) + 1;
    if n == MAX_UNLATCHED_ATTEMPTS {
        tracing::warn!(
            attempts = n,
            "minted-audio provisioning keeps failing — giving up for this host lifetime so \
             retries stop broadcasting device changes at the whole box; the wiring plan keeps \
             the name-based ladder, a service restart re-arms minting"
        );
    }
}

fn gave_up() -> bool {
    UNLATCHED_ATTEMPTS.load(Ordering::SeqCst) >= MAX_UNLATCHED_ATTEMPTS
}

/// The wiring plan's tier-0 input: the minted ids, or all-empty while nothing is provisioned.
///
/// The mic ids were briefly unpublished during the 2026-08-07 pitch investigation ("voice an
/// octave low") — the eventual measured truth: both pins run stereo/48 kHz fine, the octave
/// came from the driver's DEFAULT endpoints disagreeing (stereo render vs mono capture), and
/// the per-direction stamp sets in [`stamp_identity`] fix it permanently
/// (`audio-probe micpitch`: 440 Hz in → 440 Hz out, peak exact). Full tier-0 restored.
pub(crate) fn minted_ids() -> wiring_plan::MintedIds {
    match PROVISIONED.get() {
        Some(m) => wiring_plan::MintedIds {
            speakers_render: m.speakers_render.clone(),
            mic_render: m.mic_render.clone(),
            mic_capture: m.mic_capture.clone(),
        },
        None => wiring_plan::MintedIds::default(),
    }
}

/// The raw provisioning record — the probe's view (unlike [`minted_ids`], the mic ids are
/// visible here).
pub(crate) fn provisioned() -> Option<Arc<MintedAudio>> {
    PROVISIONED.get().cloned()
}

/// Spawn the provisioning worker (idempotent; returns immediately). Called at host start next
/// to the pad provider, and again from [`ensure_provisioned`] on the retry path.
pub(crate) fn provision_at_startup() {
    if std::env::var_os("PUNKTFUNK_NO_AUDIO_MINT").is_some() || gave_up() {
        return;
    }
    if PROVISIONED.get().is_some() || PROVISIONING.swap(true, Ordering::SeqCst) {
        return;
    }
    *LAST_ATTEMPT.lock().unwrap() = Some(Instant::now());
    let spawned = thread::Builder::new()
        .name("punktfunk-audio-mint".into())
        .spawn(|| {
            match ensure_all() {
                Ok(m) if m.any() => {
                    tracing::info!(
                        speakers = m.speakers_render.as_deref().unwrap_or("-"),
                        mic_render = m.mic_render.as_deref().unwrap_or("-"),
                        mic_capture = m.mic_capture.as_deref().unwrap_or("-"),
                        "minted audio endpoints ready (the wiring plan's tier-0)"
                    );
                    let _ = PROVISIONED.set(Arc::new(m));
                }
                Ok(_) => {
                    tracing::info!(
                        "no minted audio endpoints (Steam's streaming drivers absent?) — the \
                         wiring plan keeps the name-based ladder"
                    );
                    record_unlatched_attempt();
                }
                Err(e) => {
                    tracing::warn!(error = %format!("{e:#}"),
                        "minted-audio provisioning failed — the wiring plan keeps the name-based \
                         ladder and a later wiring pass retries");
                    record_unlatched_attempt();
                }
            }
            PROVISIONING.store(false, Ordering::SeqCst);
        });
    if let Err(e) = spawned {
        PROVISIONING.store(false, Ordering::SeqCst);
        tracing::warn!(error = %e, "could not spawn the minted-audio provisioning thread");
    }
}

/// Retry hook for wiring passes: cheap once latched; while unlatched it re-asks at most every
/// [`RETRY_COOLDOWN`] — a box where Steam arrives later mints on a later pass instead of at
/// the next reboot.
pub(crate) fn ensure_provisioned() {
    if PROVISIONED.get().is_some() || gave_up() {
        return;
    }
    {
        let last = LAST_ATTEMPT.lock().unwrap();
        if last.is_some_and(|t| t.elapsed() < RETRY_COOLDOWN) {
            return;
        }
    }
    provision_at_startup();
}

/// One synchronous provisioning pass over both roles (worker thread + the `audio-probe mint`
/// devtest). Per-role failures degrade to that role being absent.
fn ensure_all() -> Result<MintedAudio> {
    wasapi::initialize_mta()
        .ok()
        .context("CoInitializeEx (MTA, minted-audio)")?;
    let mut out = MintedAudio::default();
    for role in [Role::Speakers, Role::Mic] {
        match ensure_role(role) {
            Ok((devnode, render, capture)) => match role {
                Role::Speakers => {
                    out.speakers_devnode = Some(devnode);
                    out.speakers_render = Some(render);
                }
                Role::Mic => {
                    out.mic_devnode = Some(devnode);
                    out.mic_render = Some(render);
                    out.mic_capture = capture;
                }
            },
            Err(e) => tracing::info!(role = role.label(), error = %format!("{e:#}"),
                "minted-audio role unavailable"),
        }
    }
    Ok(out)
}

/// Ensure one role's devnode + endpoint(s): reuse the marker-matched devnode from an earlier
/// run, else mint one; (re)bind the driver idempotently; wait for audiosrv's endpoints; put
/// back any default device the fresh endpoint grabbed (measured on the pad program: a newly
/// registered endpoint can take either default).
fn ensure_role(role: Role) -> Result<(String, String, Option<String>)> {
    // Steady state: a previous run's devnode with all endpoints live — resolve by marker and
    // return without touching PnP or the default-device policy. The full pass below (re)binds
    // the driver even over an existing devnode, and that bind raises a device-change broadcast
    // every running app services — right at first mint, ruinous from a retry path (each
    // broadcast makes games rebuild their audio graph; see [`MAX_UNLATCHED_ATTEMPTS`]).
    if let Some((devnode, render, capture)) = find_healthy_role(role)? {
        stamp_identity(&render, role, false);
        if let Some(cap) = capture.as_ref() {
            stamp_identity(cap, role, true);
        }
        return Ok((devnode, render, capture));
    }

    let prev_render = audio_control::default_render_id();
    let prev_capture = audio_control::default_capture_id();

    let (hwid, inf) = discover_driver(role.needle(), role.inf_name())?;
    let devnode = match find_role_devnode(role)? {
        Some(inst) => inst,
        None => {
            let inst = pe::create_media_devnode(role.desc(), &hwid, |set, did| {
                pe::write_devparam_dword(set, did, ROLE_MARKER, role.value())
            })?;
            tracing::info!(role = role.label(), devnode = %inst, "minted an audio devnode");
            inst
        }
    };
    pe::bind_driver(&hwid, &inf)?;

    let render = wait_for(&devnode, false)?;
    let capture = match role {
        Role::Mic => Some(wait_for(&devnode, true).with_context(|| {
            format!("the minted mic devnode {devnode} produced no capture endpoint")
        })?),
        Role::Speakers => None,
    };

    // Stamp the human name onto every endpoint of the role. Field-measured necessity, not
    // cosmetics: unstamped, the minted instances read "Lautsprecher (2- Steam Streaming
    // Microphone)" etc. and even the box's owner picked the wrong device out of the Sound
    // settings zoo. The MIC RENDER additionally gets a MONO format set — measured (440 Hz in,
    // 220 Hz out): the driver forwards the render stream RAW into its mono capture side, so a
    // stereo-declared render plays back an octave low; declaring mono makes the engine
    // downmix before the crossing. Minimal stamps only (a wider set makes
    // AudioEndpointBuilder re-mint the endpoint under a new GUID — the pad program measured
    // that); stamping needs the SYSTEM ACL route on the MMDevices keys, so a dev-run devtest
    // may leave them unstamped — the wiring never depends on them (identity is the recorded
    // id).
    stamp_identity(&render, role, false);
    if let Some(cap) = capture.as_ref() {
        stamp_identity(cap, role, true);
    }

    // Freshly registered endpoints can grab a default; the wiring plan owns default policy,
    // not the mint.
    if let Some(prev) = prev_render {
        if audio_control::default_render_id().as_deref() != Some(prev.as_str())
            && audio_control::set_default_endpoint(&prev).is_ok()
        {
            tracing::info!(
                role = role.label(),
                "default playback restored after minting"
            );
        }
    }
    if let Some(prev) = prev_capture {
        if audio_control::default_capture_id().as_deref() != Some(prev.as_str())
            && audio_control::set_default_endpoint(&prev).is_ok()
        {
            tracing::info!(
                role = role.label(),
                "default recording restored after minting"
            );
        }
    }
    Ok((devnode, render, capture))
}

/// The role's marker devnode with EVERY endpoint the role owes already registered, or `None`
/// (missing devnode, missing endpoint, or an enumeration error → the caller runs the full
/// pass). Same endpoint resolvers [`wait_for`] polls, so "healthy" here is exactly the state
/// the full pass would declare ready.
fn find_healthy_role(role: Role) -> Result<Option<(String, String, Option<String>)>> {
    let Some(devnode) = find_role_devnode(role)? else {
        return Ok(None);
    };
    let Some(render) = pe::find_endpoint_for_devnode(&devnode)? else {
        return Ok(None);
    };
    let capture = match role {
        Role::Mic => match pe::find_capture_endpoint_for_devnode(&devnode)? {
            Some(cap) => Some(cap),
            None => return Ok(None),
        },
        Role::Speakers => None,
    };
    Ok(Some((devnode, render, capture)))
}

/// How many stamp/settle passes a name gets before we accept "stored but not yet served"
/// (a settled endpoint takes the stamp on the first pass; a freshly minted one may need the
/// audio stack to notice — it serves after the next Audiosrv restart/reboot at the latest).
const STAMP_ATTEMPTS: usize = 3;
/// Settle time between a stamp write and its served-check (mirrors the pad provisioner:
/// checking immediately reports success on passes that later get reverted).
const STAMP_SETTLE: Duration = Duration::from_millis(1200);

/// `WAVEFORMATEXTENSIBLE`: 2 ch / 48 kHz / 32-bit float, mask 0x3 (FL FR), IEEE-float subtype
/// — the ONE format both sides of the minted microphone declare.
///
/// Measured ground truth (micpitch, 2026-08-07): the driver forwards the render stream RAW
/// into the capture side, and its render pin is STEREO-ONLY (a mono-stamped render turned the
/// endpoint unopenable — `AUDCLNT_E_UNSUPPORTED_FORMAT` on every open, the pad program's
/// incoherent-stamp signature). The driver-default capture side declares MONO, so the raw
/// stereo stream read as mono played voice an octave low. Declaring the CAPTURE side stereo —
/// matching what actually crosses — is the honest fix; the render's stereo float default is
/// stamped explicitly too, pinning the pair coherent (and healing any endpoint a previous
/// build left mono-stamped).
const WFX_F32_2CH_48K: [u8; 40] = [
    0xfe, 0xff, // wFormatTag = WAVE_FORMAT_EXTENSIBLE
    0x02, 0x00, // nChannels = 2
    0x80, 0xbb, 0x00, 0x00, // nSamplesPerSec = 48000
    0x00, 0xdc, 0x05, 0x00, // nAvgBytesPerSec = 384000
    0x08, 0x00, // nBlockAlign = 8
    0x20, 0x00, // wBitsPerSample = 32
    0x16, 0x00, // cbSize = 22
    0x20, 0x00, // wValidBitsPerSample = 32
    0x03, 0x00, 0x00, 0x00, // dwChannelMask = FL | FR
    0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b,
    0x71, // KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
];
/// The PCM16 leg of the stereo set — the pad program's measured coherence rule: the DEVICE
/// format is 16-bit PCM, the mix/host formats float (a float device-format was part of the
/// incoherent sets that made endpoints unopenable).
const WFX_PCM16_2CH_48K: [u8; 40] = [
    0xfe, 0xff, // wFormatTag = WAVE_FORMAT_EXTENSIBLE
    0x02, 0x00, // nChannels = 2
    0x80, 0xbb, 0x00, 0x00, // nSamplesPerSec = 48000
    0x00, 0xee, 0x02, 0x00, // nAvgBytesPerSec = 192000
    0x04, 0x00, // nBlockAlign = 4
    0x10, 0x00, // wBitsPerSample = 16
    0x16, 0x00, // cbSize = 22
    0x10, 0x00, // wValidBitsPerSample = 16
    0x03, 0x00, 0x00, 0x00, // dwChannelMask = FL | FR
    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b,
    0x71, // KSDATAFORMAT_SUBTYPE_PCM
];

/// Best-effort: write the role's display name — plus, on the mic RENDER, the mono format set —
/// onto one endpoint and wait for the audio stack to SERVE it. Never fails the role — an
/// unstamped endpoint still wires correctly by id.
fn stamp_identity(endpoint_id: &str, role: Role, capture: bool) {
    let mut stamps = vec![
        pe::Stamp {
            label: "device-desc",
            key: pe::PKEY_DEVICE_DESC,
            value: pe::StampValue::Str(role.desc()),
        },
        pe::Stamp {
            label: "device-name",
            key: pe::PKEY_ENDPOINT_DEVICE_NAME,
            value: pe::StampValue::Str("Punktfunk"),
        },
    ];
    // The mic pair runs STEREO 48 kHz on both sides — the pins accept it (micpins), and the
    // octave-low voice was the two sides DISAGREEING (stereo render default vs mono capture
    // default). The stamp sets differ per direction, bisected live:
    //   * RENDER: the pad program's proven PCM16-device/float-mix split (its own bisect).
    //   * CAPTURE: the DEVICE format ONLY — the mix/host keys are RENDER-engine properties,
    //     and stamping them onto a capture endpoint broke its shared-mode graph
    //     (IsFormatSupported said 2ch/48k OK while Initialize failed 0x88890008 on a fresh,
    //     once-stamped endpoint; unstamped it opened fine).
    if role == Role::Mic {
        stamps.push(pe::Stamp {
            label: "device-format",
            key: pe::PKEY_DEVICE_FORMAT,
            value: pe::StampValue::Format(&WFX_PCM16_2CH_48K),
        });
        if !capture {
            stamps.extend([
                pe::Stamp {
                    label: "mix-format-2",
                    key: pe::PKEY_MIX_FORMAT_2,
                    value: pe::StampValue::Format(&WFX_F32_2CH_48K),
                },
                pe::Stamp {
                    label: "mix-format-3",
                    key: pe::PKEY_MIX_FORMAT_3,
                    value: pe::StampValue::Format(&WFX_F32_2CH_48K),
                },
                pe::Stamp {
                    label: "host-format",
                    key: pe::PKEY_HOST_FORMAT,
                    value: pe::StampValue::Format(&WFX_F32_2CH_48K),
                },
            ]);
        }
    }
    // Steady state (every boot after the first): the names are already served — no writes,
    // no settle sleeps.
    if pe::stamps_served(endpoint_id, &stamps) {
        return;
    }
    for attempt in 0..STAMP_ATTEMPTS {
        if let Err(e) = pe::write_stamps(endpoint_id, &stamps) {
            tracing::info!(role = role.label(), endpoint = %endpoint_id,
                error = %format!("{e:#}"),
                "could not stamp the minted endpoint's name (needs the SYSTEM ACL route) — \
                 the endpoint still wires correctly, it just keeps the driver's default name");
            return;
        }
        thread::sleep(STAMP_SETTLE);
        if pe::stamps_served(endpoint_id, &stamps) {
            if attempt > 0 {
                tracing::debug!(
                    role = role.label(),
                    attempt = attempt + 1,
                    "minted endpoint name held after a re-pass"
                );
            }
            return;
        }
    }
    tracing::info!(role = role.label(), endpoint = %endpoint_id,
        "minted endpoint name is stored but not yet served — it appears after the next \
         audio-stack restart or reboot");
}

/// Poll audiosrv for the endpoint a minted devnode registers in one direction.
fn wait_for(devnode: &str, capture: bool) -> Result<String> {
    let deadline = Instant::now() + ENDPOINT_WAIT;
    loop {
        let found = if capture {
            pe::find_capture_endpoint_for_devnode(devnode)?
        } else {
            pe::find_endpoint_for_devnode(devnode)?
        };
        if let Some(ep) = found {
            return Ok(ep);
        }
        if Instant::now() >= deadline {
            bail!(
                "no {} endpoint appeared for {devnode} within {}s — is Audiosrv running?",
                if capture { "capture" } else { "render" },
                ENDPOINT_WAIT.as_secs()
            );
        }
        thread::sleep(Duration::from_millis(250));
    }
}

/// The devnode a previous run minted for `role` (marker-matched — names are not identity).
fn find_role_devnode(role: Role) -> Result<Option<String>> {
    let set = pe::media_class_devs()?;
    for i in 0.. {
        let mut did = pe::devinfo_data();
        // SAFETY: live set; `did` is a live out-param with cbSize set.
        if unsafe {
            windows::Win32::Devices::DeviceAndDriverInstallation::SetupDiEnumDeviceInfo(
                set.0, i, &mut did,
            )
        }
        .is_err()
        {
            break;
        }
        if pe::read_devparam_dword(&set, &did, ROLE_MARKER) == Some(role.value()) {
            if let Some(inst) = pe::instance_id(&set, &did) {
                return Ok(Some(inst));
            }
        }
    }
    Ok(None)
}

/// Find the (exact hardware id, INF path) for one of Steam's streaming drivers: prefer any
/// installed devnode whose hardware-id list contains `needle` (its `oemNN.inf` is the driver
/// Windows already trusts), else fall back to Steam's driver directory. Shared with the
/// `audio-probe` devtest.
pub(crate) fn discover_driver(needle: &str, inf_name: &str) -> Result<(String, String)> {
    use windows::Win32::Devices::DeviceAndDriverInstallation::{
        SetupDiEnumDeviceInfo, SPDRP_HARDWAREID,
    };
    let steam_dir_inf = || -> Option<String> {
        let w = super::wasapi_mic::steam_driver_inf_path(inf_name)?;
        let s = String::from_utf16_lossy(&w)
            .trim_end_matches('\0')
            .to_string();
        std::path::Path::new(&s).exists().then_some(s)
    };
    let set = pe::media_class_devs()?;
    for i in 0.. {
        let mut did = pe::devinfo_data();
        // SAFETY: live set; `did` is a live out-param with cbSize set.
        if unsafe { SetupDiEnumDeviceInfo(set.0, i, &mut did) }.is_err() {
            break;
        }
        let Some(hwid) = pe::devnode_multi_sz_prop(&set, &did, SPDRP_HARDWAREID)
            .into_iter()
            .find(|h| h.to_lowercase().contains(needle))
        else {
            continue;
        };
        if let Some(inf) = pe::devnode_inf_path(&set, &did) {
            let windir = std::env::var("WINDIR").unwrap_or_else(|_| r"C:\Windows".into());
            let full = format!(r"{windir}\INF\{inf}");
            if std::path::Path::new(&full).exists() {
                return Ok((hwid, full));
            }
        }
        // Devnode exists but its INF is gone — keep its exact hwid, try Steam's directory.
        if let Some(s) = steam_dir_inf() {
            return Ok((hwid, s));
        }
    }
    // No installed devnode at all: canonical hwid + Steam's directory.
    if let Some(s) = steam_dir_inf() {
        return Ok((format!("ROOT\\{}", inf_name.trim_end_matches(".inf")), s));
    }
    bail!(
        "no installed devnode matches {needle:?} and Steam's driver directory has no \
         {inf_name} — install Steam (it never needs to run)"
    )
}

/// Synchronous provisioning — for the mic pump's resolve and the devtests.
///
/// The pump's FIRST open must not race the startup worker: measured on the target box, the
/// pump wired 2 s before the worker latched, took the cable as its write target, and the next
/// wiring pass would then have pointed the default recording at the minted microphone —
/// which nothing writes into: dead mic-air until a pump reopen. Blocking the first resolve
/// (existing marker devnodes re-resolve in milliseconds; a cold boot pays the one-time mint)
/// keeps the pump's target and the plan's verdict the same thing. Latched calls return
/// immediately; the opt-out env is honoured like everywhere else.
///
/// While UNLATCHED this is where the pump's reopen backoff (capped at 60 s) used to meet an
/// unguarded full pass: one PnP rebind + device-change broadcast roughly every minute, forever,
/// on any box where minting cannot converge (the 2026-08-12 Helldivers 2 field report). Now a
/// pass someone else already runs is WAITED for instead of raced, a failed pass repeats at most
/// every [`RETRY_COOLDOWN`], and [`MAX_UNLATCHED_ATTEMPTS`] failures stop retrying for the
/// host lifetime.
pub(crate) fn ensure_blocking() {
    if std::env::var_os("PUNKTFUNK_NO_AUDIO_MINT").is_some()
        || PROVISIONED.get().is_some()
        || gave_up()
    {
        return;
    }
    // A pass is in flight (the startup worker, or a concurrent resolve): wait for its verdict
    // rather than racing a second SetupAPI/PnP sweep against it — that race is how the pump
    // once ended up wired to the cable while the worker minted (the dead-mic-air deploy race).
    if PROVISIONING.swap(true, Ordering::SeqCst) {
        let deadline = Instant::now() + BLOCKING_WAIT;
        while PROVISIONING.load(Ordering::SeqCst) && Instant::now() < deadline {
            thread::sleep(Duration::from_millis(100));
        }
        return;
    }
    // We own the slot. First-ever resolve runs unconditionally (the cold-boot mint the doc
    // above insists on); after a failed pass the cooldown answers instead of a re-run.
    let run = {
        let mut last = LAST_ATTEMPT.lock().unwrap();
        if last.is_some_and(|t| t.elapsed() < RETRY_COOLDOWN) {
            false
        } else {
            *last = Some(Instant::now());
            true
        }
    };
    if run {
        match ensure_all() {
            Ok(m) if m.any() => {
                let _ = PROVISIONED.set(Arc::new(m));
            }
            _ => record_unlatched_attempt(),
        }
    }
    PROVISIONING.store(false, Ordering::SeqCst);
}

pub(crate) fn devtest_mint() -> Result<()> {
    let m = ensure_all()?;
    println!(
        "audio-mint: speakers devnode={} render={}",
        m.speakers_devnode.as_deref().unwrap_or("-"),
        m.speakers_render.as_deref().unwrap_or("-")
    );
    println!(
        "audio-mint: mic devnode={} render={} capture={}",
        m.mic_devnode.as_deref().unwrap_or("-"),
        m.mic_render.as_deref().unwrap_or("-"),
        m.mic_capture.as_deref().unwrap_or("-")
    );
    if m.any() {
        let _ = PROVISIONED.set(Arc::new(m));
        println!(
            "audio-mint: published for this process — `audio-probe plan` shows the tier-0 pick"
        );
    } else {
        println!("audio-mint: nothing minted (Steam's streaming drivers absent?)");
    }
    Ok(())
}
