use reqwest;
use serde_json;

pub const PLATFORMS: &[&str] = &["vanilla", "paper", "fabric", "forge", "spigot", "sponge"];

const BASE_URL: &str = "https://raw.githubusercontent.com/liebki/MinecraftServerForkDownloads/refs/heads/main";

pub fn get_platforms() -> &'static [&'static str] {
    PLATFORMS
}

async fn fetch_repo_json(filename: &str) -> Result<serde_json::Value, String> {
    let url = format!("{}/{}", BASE_URL, filename);
    let text = reqwest::get(&url)
        .await
        .map_err(|e| format!("Failed to fetch {}: {}", filename, e))?
        .text()
        .await
        .map_err(|e| format!("Failed to read response: {}", e))?;
    serde_json::from_str(&text).map_err(|e| format!("Failed to parse JSON: {}", e))
}

fn sorted_version_keys(json: &serde_json::Value) -> Result<Vec<String>, String> {
    let mut versions: Vec<String> = json
        .as_object()
        .ok_or("JSON is not an object")?
        .keys()
        .cloned()
        .collect();
    versions.sort_by(|a, b| {
        let parse = |v: &str| -> Vec<u32> { v.split('.').filter_map(|n| n.parse().ok()).collect() };
        parse(b).cmp(&parse(a))
    });
    Ok(versions)
}

fn url_from_repo_json(json: &serde_json::Value, version: &str) -> Result<String, String> {
    json.get(version)
        .and_then(|u| u.as_str())
        .map(|s| s.to_string())
        .ok_or_else(|| format!("No download URL found for version '{}'", version))
}

pub async fn get_versions(platform: &str) -> Result<Vec<String>, String> {
    match platform {
        "vanilla" => {
            let json = fetch_repo_json("release_vanilla_downloads.json").await?;
            let available = json
                .get("server_available")
                .ok_or("Missing 'server_available' key")?;
            sorted_version_keys(available)
        }
        "paper" => {
            let json = fetch_repo_json("paper_downloads.json").await?;
            sorted_version_keys(&json)
        }
        "fabric" => {
            let json = fetch_repo_json("release_fabric_downloads.json").await?;
            sorted_version_keys(&json)
        }
        "forge" => {
            let json = fetch_repo_json("forge_downloads.json").await?;
            sorted_version_keys(&json)
        }
        "spigot" => {
            let json = fetch_repo_json("spigot_downloads.json").await?;
            sorted_version_keys(&json)
        }
        "sponge" => {
            let json = fetch_repo_json("sponge_downloads.json").await?;
            sorted_version_keys(&json)
        }
        _ => Err(format!("Unknown platform: '{}'", platform)),
    }
}

pub async fn download(platform: &str, version: &str) -> Result<String, String> {
    match platform {
        "vanilla" => {
            let json = fetch_repo_json("release_vanilla_downloads.json").await?;
            json.get("server_available")
                .and_then(|v| v.get(version))
                .and_then(|u| u.as_str())
                .map(|s| s.to_string())
                .ok_or_else(|| format!("No download URL found for vanilla '{}'", version))
        }
        "paper" => {
            let json = fetch_repo_json("paper_downloads.json").await?;
            url_from_repo_json(&json, version)
        }
        "fabric" => {
            let json = fetch_repo_json("release_fabric_downloads.json").await?;
            url_from_repo_json(&json, version)
        }
        "forge" => {
            let json = fetch_repo_json("forge_downloads.json").await?;
            url_from_repo_json(&json, version)
        }
        "spigot" => {
            let json = fetch_repo_json("spigot_downloads.json").await?;
            url_from_repo_json(&json, version)
        }
        "sponge" => {
            let json = fetch_repo_json("sponge_downloads.json").await?;
            url_from_repo_json(&json, version)
        }
        _ => Err(format!("Unknown platform: '{}'", platform)),
    }
}
