use anyhow::{anyhow, Result};
use std::path::Path;
use walkdir::WalkDir;

fn is_absolute_url(s: &str) -> bool {
    s.starts_with("http://") || s.starts_with("https://")
}

fn main() -> Result<()> {
    let root = std::env::args()
        .nth(1)
        .unwrap_or_else(|| "schemas".to_string());
    let root_path = Path::new(&root);

    if !root_path.exists() {
        return Err(anyhow!("schemas path not found: {}", root));
    }

    let mut errors: Vec<String> = Vec::new();

    for entry in WalkDir::new(root_path).into_iter().filter_map(|e| e.ok()) {
        let path = entry.path();
        if path.extension().and_then(|s| s.to_str()) != Some("json") {
            continue;
        }

        let content = std::fs::read_to_string(path)?;
        let json: serde_json::Value = match serde_json::from_str(&content) {
            Ok(v) => v,
            Err(e) => {
                errors.push(format!("{}: invalid JSON: {}", path.display(), e));
                continue;
            }
        };

        // Check $id
        if let Some(id) = json.get("$id").and_then(|v| v.as_str()) {
            if !is_absolute_url(id) {
                errors.push(format!(
                    "{}: $id must be absolute URL, got: {}",
                    path.display(),
                    id
                ));
            }
        } else {
            errors.push(format!("{}: missing $id", path.display()));
        }

        // Naive scan for $ref strings; ensure absolute when they look like URLs
        // Allow internal refs like #/definitions/...
        fn check_refs(value: &serde_json::Value, file: &str, errors: &mut Vec<String>) {
            match value {
                serde_json::Value::Object(map) => {
                    for (k, v) in map {
                        if k == "$ref" {
                            if let Some(s) = v.as_str() {
                                if s.starts_with('#') {
                                    // local ref OK
                                } else if !is_absolute_url(s) {
                                    errors.push(format!(
                                        "{}: $ref must be absolute URL or local '#': {}",
                                        file, s
                                    ));
                                }
                            }
                        }
                        check_refs(v, file, errors);
                    }
                }
                serde_json::Value::Array(arr) => {
                    for v in arr {
                        check_refs(v, file, errors);
                    }
                }
                _ => {}
            }
        }

        check_refs(&json, &path.display().to_string(), &mut errors);
    }

    if errors.is_empty() {
        println!("schema-check: OK");
        Ok(())
    } else {
        for e in &errors {
            eprintln!("{}", e);
        }
        Err(anyhow!("schema-check: found {} errors", errors.len()))
    }
}
