use serde::{Deserialize, Serialize};
use std::path::Path;

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ValidationResult {
    pub file: String,
    pub status: ValidationStatus,
    pub message: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "lowercase")]
pub enum ValidationStatus {
    Ok,
    Warn,
    Error,
}

/// Validate a UO root directory.
/// Returns one ValidationResult per expected file.
pub fn validate_uo_root(path: &str) -> Vec<ValidationResult> {
    let root = Path::new(path);
    let mut results = Vec::new();

    // Required — one of each pair must exist
    let required_pairs: &[(&str, &str)] = &[
        ("art.mul", "artLegacyMUL.uop"),
        ("gumpart.mul", "gumpartLegacyMUL.uop"),
    ];

    for (a, b) in required_pairs {
        let a_exists = root.join(a).exists();
        let b_exists = root.join(b).exists();
        if a_exists || b_exists {
            let found = if a_exists { a } else { b };
            results.push(ValidationResult {
                file: format!("{} / {}", a, b),
                status: ValidationStatus::Ok,
                message: Some(format!("Found: {}", found)),
            });
        } else {
            results.push(ValidationResult {
                file: format!("{} / {}", a, b),
                status: ValidationStatus::Error,
                message: Some("Neither file found".to_string()),
            });
        }
    }

    // Strictly required singles
    for file in &["tiledata.mul", "hues.mul", "cliloc.enu", "unifont.mul"] {
        let exists = root.join(file).exists();
        results.push(ValidationResult {
            file: file.to_string(),
            status: if exists {
                ValidationStatus::Ok
            } else {
                ValidationStatus::Error
            },
            message: if exists {
                None
            } else {
                Some("File not found".to_string())
            },
        });
    }

    // Optional — warn if missing
    let optional: &[(&str, Option<&str>)] = &[
        ("anim.mul", None),
        ("multi.mul", None),
        ("map0.mul", None),
        ("radarcol.mul", None),
    ];

    for (file, alt) in optional {
        let exists = root.join(file).exists()
            || alt.map(|a| root.join(a).exists()).unwrap_or(false);
        results.push(ValidationResult {
            file: file.to_string(),
            status: if exists {
                ValidationStatus::Ok
            } else {
                ValidationStatus::Warn
            },
            message: if exists {
                None
            } else {
                Some("Optional file missing — some features unavailable".to_string())
            },
        });
    }

    results
}

/// Validate a ServUO Scripts path.
pub fn validate_scripts_path(path: &str) -> Vec<ValidationResult> {
    let p = Path::new(path);
    if !p.exists() {
        return vec![ValidationResult {
            file: path.to_string(),
            status: ValidationStatus::Error,
            message: Some("Path does not exist".to_string()),
        }];
    }

    // Check for at least one .cs file
    let has_cs = walkdir::WalkDir::new(p)
        .max_depth(5)
        .into_iter()
        .filter_map(|e| e.ok())
        .any(|e| e.path().extension().map(|x| x == "cs").unwrap_or(false));

    vec![ValidationResult {
        file: path.to_string(),
        status: if has_cs {
            ValidationStatus::Ok
        } else {
            ValidationStatus::Warn
        },
        message: if has_cs {
            None
        } else {
            Some("No .cs files found — is this the right folder?".to_string())
        },
    }]
}

/// Detect whether the UO root uses MUL or UOP format.
pub fn detect_asset_format(uo_root: &str) -> &'static str {
    if Path::new(uo_root).join("artLegacyMUL.uop").exists() {
        "uop"
    } else {
        "mul"
    }
}
