//! CLI handler for the validate command.

use std::path::PathBuf;
use std::process::ExitCode;

use super::{
    AgentUxValidator, ConflictDetectionRule, NamingConsistencyRule, OutputFormat, Severity,
    ValidateConfig,
};
use crate::capability::parse_capability_file;

/// Collect YAML capability files from paths.
fn collect_capability_files(paths: &[PathBuf]) -> Vec<PathBuf> {
    let mut files = Vec::new();

    for path in paths {
        if path.is_file() {
            let ext = path.extension().and_then(|e| e.to_str()).unwrap_or("");
            if ext == "yaml" || ext == "yml" {
                files.push(path.clone());
            }
        } else if path.is_dir() {
            collect_yaml_recursive(path, &mut files);
        } else {
            eprintln!("Warning: skipping non-existent path: {}", path.display());
        }
    }

    files
}

/// Recursively collect YAML files from a directory.
fn collect_yaml_recursive(dir: &std::path::Path, files: &mut Vec<PathBuf>) {
    let Ok(entries) = std::fs::read_dir(dir) else {
        return;
    };

    for entry in entries.flatten() {
        let path = entry.path();
        if path.is_dir() {
            collect_yaml_recursive(&path, files);
        } else if path.is_file() {
            let ext = path.extension().and_then(|e| e.to_str()).unwrap_or("");
            if ext == "yaml" || ext == "yml" {
                files.push(path);
            }
        }
    }
}

/// Format validation results as human-readable text.
fn format_validate_text(
    file_results: &[(&str, &super::ValidationReport)],
    config: &ValidateConfig,
) -> String {
    use std::fmt::Write;

    let mut output = String::new();

    let pass_marker = if config.color {
        "\x1b[32mPASS\x1b[0m"
    } else {
        "PASS"
    };
    let warn_marker = if config.color {
        "\x1b[33mWARN\x1b[0m"
    } else {
        "WARN"
    };
    let fail_marker = if config.color {
        "\x1b[31mFAIL\x1b[0m"
    } else {
        "FAIL"
    };

    let mut total_pass = 0usize;
    let mut total_warn = 0usize;
    let mut total_fail = 0usize;

    for &(file_path, report) in file_results {
        let _ = writeln!(output, "\n--- {file_path} ---");

        for result in &report.results {
            // Apply severity filter
            if !config.min_severity.includes(result.severity) {
                continue;
            }

            let marker = if result.passed {
                total_pass += 1;
                pass_marker
            } else if result.severity == Severity::Fail {
                total_fail += 1;
                fail_marker
            } else {
                total_warn += 1;
                warn_marker
            };

            let _ = writeln!(
                output,
                "  [{marker}] [{}] {} - {}",
                result.rule_code, result.tool_name, result.rule_name
            );

            if !result.passed {
                for issue in &result.issues {
                    let _ = writeln!(output, "         {issue}");
                }
            }
        }
    }

    let _ = writeln!(output);
    let _ = writeln!(
        output,
        "Summary: {total_pass} passed, {total_warn} warnings, {total_fail} failures"
    );

    output
}

/// Run the validate command against one or more capability paths.
#[allow(clippy::too_many_lines)]
pub async fn run_validate_command(paths: &[PathBuf], config: &ValidateConfig) -> ExitCode {
    let files = collect_capability_files(paths);

    if files.is_empty() {
        eprintln!("No YAML capability files found in the given paths.");
        return ExitCode::from(2);
    }

    let validator = AgentUxValidator::new();
    let mut all_tools = Vec::new();
    let mut file_reports: Vec<(String, super::ValidationReport)> = Vec::new();
    let mut has_failures = false;
    let mut parse_errors = false;

    // Phase 1: Parse and validate each file
    for file in &files {
        let cap = match parse_capability_file(file).await {
            Ok(c) => c,
            Err(e) => {
                eprintln!("Parse error in {}: {e}", file.display());
                parse_errors = true;
                continue;
            }
        };

        let tool = cap.to_mcp_tool();
        let report = match validator.validate_tools(std::slice::from_ref(&tool)) {
            Ok(r) => r,
            Err(e) => {
                eprintln!("Validation error for {}: {e}", file.display());
                continue;
            }
        };

        if !report.failures().is_empty() {
            has_failures = true;
        }

        // Auto-fix if requested
        if config.auto_fix {
            let suggested = super::fix::CapabilityFixer::suggest_fixes(&report.results);
            if !suggested.is_empty()
                && let Ok(content) = std::fs::read_to_string(file)
                && let Some(patched) =
                    super::fix::CapabilityFixer::apply_fixes(&content, &suggested)
                && std::fs::write(file, patched).is_ok()
            {
                eprintln!(
                    "Auto-fixed {} issue(s) in {}",
                    suggested.len(),
                    file.display()
                );
            }
        }

        all_tools.push(tool);
        file_reports.push((file.display().to_string(), report));
    }

    // Phase 2: Cross-capability checks
    let conflict_results = ConflictDetectionRule::check_conflicts(&all_tools);
    let consistency_results = NamingConsistencyRule::check_consistency(&all_tools);

    let has_cross_failures = conflict_results
        .iter()
        .any(|r| !r.passed && r.severity == Severity::Fail)
        || consistency_results
            .iter()
            .any(|r| !r.passed && r.severity == Severity::Fail);

    if has_cross_failures {
        has_failures = true;
    }

    // Phase 3: Output
    match config.format {
        OutputFormat::Text => {
            let refs: Vec<(&str, &super::ValidationReport)> =
                file_reports.iter().map(|(p, r)| (p.as_str(), r)).collect();

            print!("{}", format_validate_text(&refs, config));

            // Print cross-capability results
            if !conflict_results.is_empty() || !consistency_results.is_empty() {
                println!("\n--- Cross-Capability Checks ---");
                for result in conflict_results.iter().chain(consistency_results.iter()) {
                    if !config.min_severity.includes(result.severity) {
                        continue;
                    }
                    let marker = if result.passed {
                        if config.color {
                            "\x1b[32mPASS\x1b[0m"
                        } else {
                            "PASS"
                        }
                    } else {
                        match result.severity {
                            Severity::Fail => {
                                if config.color {
                                    "\x1b[31mFAIL\x1b[0m"
                                } else {
                                    "FAIL"
                                }
                            }
                            _ => {
                                if config.color {
                                    "\x1b[33mWARN\x1b[0m"
                                } else {
                                    "WARN"
                                }
                            }
                        }
                    };
                    println!(
                        "  [{marker}] [{}] {} - {}",
                        result.rule_code, result.tool_name, result.rule_name
                    );
                    for issue in &result.issues {
                        println!("         {issue}");
                    }
                }
            }
        }

        OutputFormat::Json => {
            let json_output: Vec<serde_json::Value> = file_reports
                .iter()
                .map(|(path, report)| {
                    serde_json::json!({
                        "file": path,
                        "score": report.overall_score,
                        "grade": report.grade,
                        "results": report.results,
                        "summary": report.summary,
                    })
                })
                .collect();

            let full = serde_json::json!({
                "files": json_output,
                "cross_capability": {
                    "conflicts": conflict_results,
                    "consistency": consistency_results,
                },
            });

            println!(
                "{}",
                serde_json::to_string_pretty(&full).unwrap_or_default()
            );
        }

        OutputFormat::Sarif => {
            let file_result_refs: Vec<(&str, &[super::ValidationResult])> = file_reports
                .iter()
                .map(|(p, r)| (p.as_str(), r.results.as_slice()))
                .collect();

            let sarif = super::sarif::to_sarif_multi(&file_result_refs);
            println!(
                "{}",
                serde_json::to_string_pretty(&sarif).unwrap_or_default()
            );
        }
    }

    if parse_errors {
        ExitCode::from(2)
    } else if has_failures {
        ExitCode::FAILURE
    } else {
        ExitCode::SUCCESS
    }
}
