//! Framework-integrity boundary check command.
//!
//! Validates an implementation against the inherited manifest chain — the
//! framework's `framework_manifest.toml` plus any derived-layer manifest such
//! as a domain `site_manifest.toml` — reporting protected-path, required-trait
//! and high-severity forbidden-pattern violations. This is the executable gate
//! that stops a lower-level actor (agent or person) from breaking or
//! reinventing an upper level: protected paths absent from the impl cannot be
//! edited, and an out-of-bounds re-declaration trips a violation. Code quality
//! (unwrap, generic paths) is intentionally out of scope — that is clippy's and
//! the guidelines' job, not the architectural-integrity gate's.

use std::path::PathBuf;

use rustelo_core::{Result, RusteloError};
use rustelo_core_lib::integrity::{IntegrityValidator, ValidationResult, ViolationSeverity};

/// Run the integrity boundary check. Exits non-zero (via an error) when the
/// result fails the manifest's violation thresholds, so it works directly as a
/// pre-commit / CI gate.
pub async fn check(
    manifests: Vec<PathBuf>,
    impl_root: PathBuf,
    changed: Vec<PathBuf>,
    min_severity: String,
    json: bool,
) -> Result<()> {
    let min = parse_severity(&min_severity)?;

    let validator = IntegrityValidator::from_manifests(&manifests, &impl_root)
        .map_err(|e| RusteloError::Generic(format!("integrity init: {e}")))?;

    let scope = if changed.is_empty() {
        None
    } else {
        Some(changed.as_slice())
    };

    let result = validator
        .check_boundaries(scope, min)
        .map_err(|e| RusteloError::Generic(format!("integrity check: {e}")))?;

    if json {
        let out = serde_json::to_string_pretty(&result)
            .map_err(|e| RusteloError::Generic(e.to_string()))?;
        println!("{out}");
    } else {
        report_text(&result);
    }

    if result.passed {
        Ok(())
    } else {
        Err(RusteloError::Generic(format!(
            "integrity gate FAILED: {} violation(s) exceed the manifest thresholds",
            result.violations.len()
        )))
    }
}

fn parse_severity(s: &str) -> Result<ViolationSeverity> {
    match s.to_ascii_lowercase().as_str() {
        "critical" => Ok(ViolationSeverity::Critical),
        "high" => Ok(ViolationSeverity::High),
        "medium" => Ok(ViolationSeverity::Medium),
        "low" => Ok(ViolationSeverity::Low),
        other => Err(RusteloError::Generic(format!(
            "unknown severity `{other}` (expected critical|high|medium|low)"
        ))),
    }
}

fn report_text(result: &ValidationResult) {
    let scanned = result.summary.total_files_scanned;
    if result.violations.is_empty() {
        println!("integrity: no violations ({scanned} file(s) scanned) — PASS");
        return;
    }

    println!(
        "integrity: {} violation(s) over {scanned} file(s):",
        result.violations.len()
    );
    for v in &result.violations {
        let loc = match v.line_number {
            Some(n) => format!("{}:{}", v.file_path, n),
            None => v.file_path.clone(),
        };
        println!("  [{:?}] {loc} — {}", v.severity, v.description);
        println!("        ↳ {}", v.suggested_fix);
    }
    println!(
        "result: {} (compatibility score {:.2})",
        if result.passed { "PASS" } else { "FAIL" },
        result.compatibility_score
    );
}
