//! `probe` — BLE protocol discovery against real hardware.
//!
//! This is the Phase 0 tool from REQUIREMENTS.md §9: TASK-1 (enumerate the
//! D100's services, dump its capability characteristics, log decoded Indoor
//! Bike Data, resolve whether op code `0x11` works) and TASK-2 (write a control
//! command and confirm a physical resistance change).
//!
//! It is intentionally separate from the app: it prints raw bytes next to
//! decoded values (NFR-8) so that a decoder bug shows up as a disagreement on
//! screen rather than as a strange number in a chart.

mod cli;
mod commands;

use std::time::Duration;

use anyhow::Result;
use tracing_subscriber::EnvFilter;

/// How long to look for the device before giving up.
const SCAN_TIMEOUT: Duration = Duration::from_secs(20);

#[tokio::main]
async fn main() -> Result<()> {
    let args = match cli::parse(std::env::args().skip(1)) {
        Ok(args) => args,
        Err(e) => {
            eprintln!("error: {e}\n");
            eprint!("{}", cli::USAGE);
            std::process::exit(2);
        }
    };

    init_logging(args.verbose);

    match args.command {
        cli::Command::Help => {
            print!("{}", cli::USAGE);
            Ok(())
        }
        cli::Command::Scan { duration, all } => commands::scan_cmd(duration, all).await,
        cli::Command::Inspect { device } => commands::inspect(&device, SCAN_TIMEOUT).await,
        cli::Command::Monitor { device, duration } => {
            commands::monitor(&device, duration, SCAN_TIMEOUT).await
        }
        cli::Command::Set {
            device,
            target,
            simulation,
            hold,
        } => commands::set(&device, target, simulation, hold, SCAN_TIMEOUT).await,
        cli::Command::Listen {
            device,
            duration,
            handshake,
        } => commands::listen(&device, duration, handshake, SCAN_TIMEOUT).await,
        cli::Command::Zwift {
            device,
            duration,
            no_handshake,
            buttons_only,
        } => {
            commands::zwift_cmd(&device, duration, no_handshake, buttons_only, SCAN_TIMEOUT).await
        }
    }
}

fn init_logging(verbose: bool) {
    // `-v` turns on the raw-frame logging required by NFR-8. RUST_LOG still
    // wins, so `RUST_LOG=trace` gets every notification.
    let default = if verbose {
        "bikecontrol_ble=debug,probe=debug,info"
    } else {
        "warn"
    };
    tracing_subscriber::fmt()
        .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| default.into()))
        .with_target(false)
        .without_time()
        .init();
}
