use std::path::{Path, PathBuf};

const RIRS: &[(&str, &str)] = &[
    (
        "apnic",
        "https://ftp.apnic.net/stats/apnic/delegated-apnic-latest",
    ),
    (
        "arin",
        "https://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest",
    ),
    (
        "ripencc",
        "https://ftp.ripe.net/ripe/stats/delegated-ripencc-latest",
    ),
    (
        "lacnic",
        "https://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-latest",
    ),
    (
        "afrinic",
        "https://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-latest",
    ),
];

fn main() {
    let out_dir = parse_args();

    if let Err(e) = std::fs::create_dir_all(&out_dir) {
        eprintln!(
            "error: cannot create output directory {}: {}",
            out_dir.display(),
            e
        );
        std::process::exit(1);
    }

    let client = reqwest::blocking::Client::builder()
        .user_agent(concat!("ip-rir-fetch/", env!("CARGO_PKG_VERSION")))
        .timeout(std::time::Duration::from_secs(120))
        .build()
        .expect("failed to build HTTP client");

    let mut failed = 0usize;
    for &(name, url) in RIRS {
        eprint!("fetching {} ... ", name);
        match fetch_one(&client, url, &out_dir, name) {
            Ok(bytes) => eprintln!("{} bytes", bytes),
            Err(e) => {
                eprintln!("FAILED: {}", e);
                failed += 1;
            }
        }
    }

    if failed > 0 {
        eprintln!("{} RIR(s) failed to download", failed);
        std::process::exit(1);
    }
}

fn fetch_one(
    client: &reqwest::blocking::Client,
    url: &str,
    out_dir: &Path,
    name: &str,
) -> Result<usize, String> {
    let body = client
        .get(url)
        .send()
        .map_err(|e| e.to_string())?
        .error_for_status()
        .map_err(|e| e.to_string())?
        .bytes()
        .map_err(|e| e.to_string())?;

    let dest = out_dir.join(format!("delegated-{}-latest", name));
    let tmp = out_dir.join(format!(".delegated-{}-latest.tmp", name));

    std::fs::write(&tmp, &body).map_err(|e| format!("write {}: {}", tmp.display(), e))?;
    std::fs::rename(&tmp, &dest)
        .map_err(|e| format!("rename to {}: {}", dest.display(), e))?;

    Ok(body.len())
}

fn parse_args() -> PathBuf {
    let mut args = std::env::args().skip(1);
    while let Some(arg) = args.next() {
        match arg.as_str() {
            "--help" | "-h" => {
                print_usage();
                std::process::exit(0);
            }
            "--out" | "-o" => match args.next() {
                Some(val) => return PathBuf::from(val),
                None => {
                    eprintln!("error: {} requires a directory argument", arg);
                    std::process::exit(1);
                }
            },
            s if s.starts_with("--out=") => {
                return PathBuf::from(&s["--out=".len()..]);
            }
            s => {
                eprintln!("error: unknown argument: {}", s);
                print_usage();
                std::process::exit(1);
            }
        }
    }

    eprintln!("error: --out <dir> is required");
    print_usage();
    std::process::exit(1);
}

fn print_usage() {
    eprintln!(
        "Usage: ip-rir-fetch --out <dir>
       ip-rir-fetch -o <dir>

Downloads delegated stats files from all 5 RIRs (APNIC, ARIN, RIPE NCC,
LACNIC, AFRINIC) into <dir>. Files are written atomically via a temp file.

Build: cargo build --features fetch --bin ip-rir-fetch"
    );
}