#[cfg(any(not(coverage), test))]
use anyhow::{Context, Result, bail};

#[cfg(not(coverage))]
use lesavka_client::sync_probe::analyze::{SyncAnalysisOptions, analyze_capture};

#[cfg(not(coverage))]
fn main() -> Result<()> {
    let (capture_path, emit_json) = parse_args(std::env::args().skip(1))?;
    let report = analyze_capture(&capture_path, &SyncAnalysisOptions::default())
        .with_context(|| format!("analyzing sync capture {}", capture_path.display()))?;

    if emit_json {
        println!(
            "{}",
            serde_json::to_string_pretty(&report).context("serializing JSON report")?
        );
    } else {
        println!("A/V sync report for {}", capture_path.display());
        println!("- video onsets: {}", report.video_event_count);
        println!("- audio onsets: {}", report.audio_event_count);
        println!("- paired pulses: {}", report.paired_event_count);
        println!(
            "- first skew: {:+.1} ms (audio after video is positive)",
            report.first_skew_ms
        );
        println!("- last skew: {:+.1} ms", report.last_skew_ms);
        println!("- mean skew: {:+.1} ms", report.mean_skew_ms);
        println!("- median skew: {:+.1} ms", report.median_skew_ms);
        println!("- max abs skew: {:.1} ms", report.max_abs_skew_ms);
        println!("- drift: {:+.1} ms", report.drift_ms);
    }

    Ok(())
}

#[cfg(any(not(coverage), test))]
fn parse_args<I, S>(args: I) -> Result<(std::path::PathBuf, bool)>
where
    I: IntoIterator<Item = S>,
    S: Into<String>,
{
    let args = args.into_iter().map(Into::into).collect::<Vec<_>>();
    if args.is_empty() || args.iter().any(|arg| arg == "--help" || arg == "-h") {
        println!("Usage: lesavka-sync-analyze <capture.mkv> [--json]");
        std::process::exit(0);
    }

    let mut emit_json = false;
    let mut capture_path = None::<std::path::PathBuf>;
    for arg in args {
        if arg == "--json" {
            emit_json = true;
            continue;
        }
        if capture_path.is_some() {
            bail!("unexpected extra argument `{arg}`");
        }
        capture_path = Some(std::path::PathBuf::from(arg));
    }

    let capture_path = capture_path.context("capture path is required")?;
    Ok((capture_path, emit_json))
}

#[cfg(coverage)]
fn main() {}

#[cfg(test)]
mod tests {
    use super::parse_args;

    #[test]
    fn parse_args_accepts_capture_path_and_json_flag() {
        let (path, json) = parse_args(["capture.mkv", "--json"]).expect("args");
        assert_eq!(path, std::path::PathBuf::from("capture.mkv"));
        assert!(json);
    }

    #[test]
    fn parse_args_rejects_extra_positional_arguments() {
        assert!(parse_args(["one.mkv", "two.mkv"]).is_err());
    }

    #[test]
    fn parse_args_requires_a_capture_path() {
        let error = parse_args(["--json"]).expect_err("missing capture path should fail");
        assert!(
            error.to_string().contains("capture path is required"),
            "unexpected error: {error:#}"
        );
    }

    #[test]
    fn coverage_main_stub_is_non_panicking() {
        let _ = super::main();
    }
}
