//! OpenXR headless smoke test: loader -> runtime -> system -> session.
//!
//! Proves the OpenXR stack end to end with no headset and no window, using
//! the XR_MND_headless extension (Monado supports it). This is the CI gate
//! for VR bring-up; the graphics interop builds on top of a passing run.
//!
//! Run on a machine with an OpenXR runtime:
//!   cargo run --features xr --example xr_smoke

use std::time::{Duration, Instant};

fn main() {
    // 1. The loader (libopenxr_loader.so via the system).
    let entry = match unsafe { openxr::Entry::load() } {
        Ok(e) => e,
        Err(e) => {
            eprintln!("XR SMOKE FAIL: OpenXR loader not found: {e}");
            std::process::exit(1);
        }
    };
    println!("loader: ok");

    // 2. The runtime must offer headless sessions.
    let available = entry.enumerate_extensions().expect("enumerate_extensions");
    println!("XR_MND_headless available: {}", available.mnd_headless);
    if !available.mnd_headless {
        eprintln!("XR SMOKE FAIL: runtime lacks XR_MND_headless");
        std::process::exit(1);
    }
    let mut exts = openxr::ExtensionSet::default();
    exts.mnd_headless = true;

    // 3. Instance.
    let instance = entry
        .create_instance(
            &openxr::ApplicationInfo {
                application_name: "mawida-xr-smoke",
                engine_name: "mawida",
                ..Default::default()
            },
            &exts,
            &[],
        )
        .expect("create_instance");
    let props = instance.properties().expect("instance properties");
    println!("runtime: {} {}", props.runtime_name, props.runtime_version);

    // 4. A system: under Monado's simulated driver this is the fake HMD.
    let system = instance
        .system(openxr::FormFactor::HEAD_MOUNTED_DISPLAY)
        .expect("no XR system; is a (simulated) HMD driver active?");
    let sys_props = instance.system_properties(system).expect("system_properties");
    println!(
        "system: {} (vendor {:#x})",
        sys_props.system_name, sys_props.vendor_id
    );

    // 5. A headless session: no graphics binding, no swapchain.
    let (session, _frame_waiter, _frame_stream) = unsafe {
        instance.create_session::<openxr::headless::Headless>(
            system,
            &openxr::headless::SessionCreateInfo {},
        )
    }
    .expect("create headless session");
    println!("session: created");

    // 6. Pump events briefly; a healthy runtime moves the session toward
    // READY. Session creation above is the core proof either way.
    let mut buf = openxr::EventDataBuffer::new();
    let start = Instant::now();
    let mut last_state = openxr::SessionState::UNKNOWN;
    while start.elapsed() < Duration::from_secs(3) {
        while let Some(event) = instance.poll_event(&mut buf).expect("poll_event") {
            if let openxr::Event::SessionStateChanged(s) = event {
                last_state = s.state();
                println!("session state: {last_state:?}");
            }
        }
        if last_state == openxr::SessionState::READY
            || last_state == openxr::SessionState::FOCUSED
        {
            break;
        }
        std::thread::sleep(Duration::from_millis(50));
    }

    drop(session);
    println!("XR SMOKE PASS (final state: {last_state:?})");
}
