use game_audio::track::{Refresh, parse, resolve_imports};
use std::{
    path::{Path, PathBuf},
    process::ExitCode,
};

/// Collects every `.track` file under `dir`, recursing into subdirectories.
fn collect(dir: &Path, out: &mut Vec<PathBuf>) -> Result<(), std::io::Error> {
    for entry in std::fs::read_dir(dir)? {
        let path = entry?.path();
        if path.is_dir() {
            collect(&path, out)?;
        } else if path
            .extension()
            .is_some_and(|extension| extension == "track")
        {
            out.push(path);
        }
    }
    Ok(())
}

/// Parses and resolves imports for a single track, printing its result and
/// returning whether it succeeded.
fn check(path: &Path, refresh: Refresh) -> bool {
    let label = path.display();
    let source = match std::fs::read_to_string(path) {
        Ok(source) => source,
        Err(error) => {
            println!("FAIL {label}: read: {error}");
            return false;
        }
    };
    let mut song = match parse(&source) {
        Ok(song) => song,
        Err(error) => {
            println!("FAIL {label}: parse: {error}");
            return false;
        }
    };
    if let Err(error) = resolve_imports(&mut song, path.parent(), refresh) {
        println!("FAIL {label}: import: {error}");
        return false;
    }
    let notes: usize = song.layers.iter().map(|layer| layer.notes.len()).sum();
    println!("ok   {label}: {} layers, {notes} notes", song.layers.len());
    true
}

fn main() -> ExitCode {
    let mut refresh = Refresh::Cached;
    let mut roots: Vec<PathBuf> = Vec::new();
    for arg in std::env::args().skip(1) {
        if arg == "--refresh" {
            refresh = Refresh::Force;
        } else {
            roots.push(PathBuf::from(arg));
        }
    }
    if roots.is_empty() {
        eprintln!(
            "usage: track-check [--refresh] <path>...\n  \
             parses and resolves each .track file (directories recurse); \
             no audio device needed"
        );
        return ExitCode::FAILURE;
    }

    let mut files = Vec::new();
    for root in &roots {
        if root.is_dir() {
            if let Err(error) = collect(root, &mut files) {
                eprintln!("error: reading {}: {error}", root.display());
                return ExitCode::FAILURE;
            }
        } else {
            files.push(root.clone());
        }
    }
    files.sort();

    let ok = files.iter().filter(|path| check(path, refresh)).count();
    let failed = files.len() - ok;
    if failed == 0 {
        println!("all {ok} tracks ok");
        ExitCode::SUCCESS
    } else {
        eprintln!("{failed} of {} tracks failed", files.len());
        ExitCode::FAILURE
    }
}
