use std::{path::PathBuf, process::ExitCode};

enum Output {
    Wav(PathBuf),
    Mp3(PathBuf),
    Stems(PathBuf),
}

fn main() -> ExitCode {
    let arguments: Vec<String> = std::env::args().skip(1).collect();
    let stems = arguments.iter().any(|argument| argument == "--stems");
    let mut positional = arguments.iter().filter(|argument| *argument != "--stems");
    let Some(input) = positional.next().map(PathBuf::from) else {
        eprintln!(
            "Usage: track-export <input.track> [output.wav|output.mp3 | --stems [directory]]"
        );
        return ExitCode::FAILURE;
    };
    let output = if stems {
        Output::Stems(
            positional
                .next()
                .map_or_else(|| input.with_extension("stems"), PathBuf::from),
        )
    } else {
        let path = positional
            .next()
            .map_or_else(|| input.with_extension("wav"), PathBuf::from);
        match path.extension().and_then(|extension| extension.to_str()) {
            Some("wav") => Output::Wav(path),
            Some("mp3") => Output::Mp3(path),
            _ => {
                eprintln!("Unsupported output format; use .wav, .mp3, or --stems");
                return ExitCode::FAILURE;
            }
        }
    };

    let source = match std::fs::read_to_string(&input) {
        Ok(source) => source,
        Err(error) => {
            eprintln!("Reading {input}: {error}", input = input.display());
            return ExitCode::FAILURE;
        }
    };
    let mut song = match game_audio::track::parse(&source) {
        Ok(song) => song,
        Err(error) => {
            eprintln!("Parsing {input}: {error}", input = input.display());
            return ExitCode::FAILURE;
        }
    };
    if let Err(error) = game_audio::track::resolve_imports(
        &mut song,
        input.parent(),
        game_audio::track::Refresh::Cached,
    ) {
        eprintln!(
            "Resolving imports for {input}: {error}",
            input = input.display()
        );
        return ExitCode::FAILURE;
    }

    let (result, target) = match &output {
        Output::Wav(path) => (
            game_audio::track::export_wav(&song, input.parent(), path),
            path.clone(),
        ),
        Output::Mp3(path) => (
            game_audio::track::export_mp3(&song, input.parent(), path),
            path.clone(),
        ),
        Output::Stems(directory) => (
            game_audio::track::export_stems(&song, input.parent(), directory).map(|written| {
                eprintln!("{} stems", written.len());
            }),
            directory.clone(),
        ),
    };
    match result {
        Ok(()) => {
            eprintln!(
                "Exported {input} → {target}",
                input = input.display(),
                target = target.display()
            );
            ExitCode::SUCCESS
        }
        Err(error) => {
            eprintln!("Exporting {target}: {error}", target = target.display());
            ExitCode::FAILURE
        }
    }
}
