use std::env;
use std::error::Error;
use std::fs;
use std::path::{Path, PathBuf};

use walkdir::WalkDir;
use wow_mpq::compression::flags;
use wow_mpq::{ArchiveBuilder, FormatVersion, ListfileOption};

/// Checks if a directory name looks like a WoW locale folder (e.g. ruRU, enUS, deDE).
fn is_locale_dir(name: &str) -> bool {
    if name.len() != 4 {
        return false;
    }
    let b = name.as_bytes();
    b[0].is_ascii_lowercase()
        && b[1].is_ascii_lowercase()
        && b[2].is_ascii_uppercase()
        && b[3].is_ascii_uppercase()
}

fn build_mpq(input_dir: &Path, output_path: &Path) -> Result<(), Box<dyn Error>> {
    let mut output = output_path.to_path_buf();
    output.set_extension("MPQ");

    if let Some(parent) = output.parent() {
        fs::create_dir_all(parent)?;
    }

    let base = input_dir.canonicalize()?;
    println!("Packing: {} -> {}", base.display(), output.display());

    let mut builder = ArchiveBuilder::new()
        .version(FormatVersion::V2)
        .block_size(7) // 64 KB sectors
        .default_compression(flags::ZLIB)
        .listfile_option(ListfileOption::Generate);

    for entry in WalkDir::new(&base)
        .into_iter()
        .filter_map(|e| e.ok())
        .filter(|e| e.file_type().is_file())
    {
        let full_path = entry.path().canonicalize()?;
        let rel_path = match full_path.strip_prefix(&base) {
            Ok(p) => p,
            Err(_) => {
                eprintln!("Skipping (prefix mismatch): {}", full_path.display());
                continue;
            }
        };
        let archive_path = rel_path.to_string_lossy().replace('\\', "/");
        println!("  Adding: {}", archive_path);
        builder = builder.add_file(&full_path, &archive_path);
    }

    builder.build(&output)?;
    println!("Created: {}", output.display());
    Ok(())
}

fn main() -> Result<(), Box<dyn Error>> {
    let args: Vec<String> = env::args().collect();

    if args.len() != 3 {
        eprintln!("Usage: tool <src_dir> <output_dir>");
        eprintln!();
        eprintln!("  src_dir    — root of source tree (must contain Data/)");
        eprintln!("  output_dir — destination root; MPQs are placed mirroring the src structure");
        std::process::exit(1);
    }

    let src_dir = PathBuf::from(&args[1]);
    let output_dir = PathBuf::from(&args[2]);

    let data_dir = src_dir.join("Data");
    if !data_dir.exists() {
        return Err(format!("Data/ not found inside src_dir: {}", src_dir.display()).into());
    }

    for entry in fs::read_dir(&data_dir)? {
        let entry = entry?;
        let path = entry.path();
        if !path.is_dir() {
            continue;
        }

        let name = entry.file_name().to_string_lossy().to_string();

        if is_locale_dir(&name) {
            // e.g. ruRU/ — iterate its immediate subdirs, each is a patch source
            for sub in fs::read_dir(&path)? {
                let sub = sub?;
                let sub_path = sub.path();
                if !sub_path.is_dir() {
                    continue;
                }
                let sub_name = sub.file_name().to_string_lossy().to_string();
                let output_mpq = output_dir.join("Data").join(&name).join(&sub_name);
                build_mpq(&sub_path, &output_mpq)?;
            }
        } else {
            // Regular patch dir, e.g. patch-4/ or patch-Z/
            let output_mpq = output_dir.join("Data").join(&name);
            build_mpq(&path, &output_mpq)?;
        }
    }

    println!("\nAll MPQ archives built successfully.");
    Ok(())
}
