use crate::{
    invocation::{SkillContext, SkillInput},
    outcome::SkillOutcome,
    skill::{Skill, SkillId},
};
use once_cell::sync::Lazy;
use regex::Regex;
use serde_json::json;

// (regex, description, severity)
static PATTERNS: Lazy<Vec<(Regex, &'static str, &'static str)>> = Lazy::new(|| {
    let raw: &[(&str, &str, &str)] = &[
        (r"(?i)\brm\s+-rf\b", "destructive rm -rf", "critical"),
        (
            r"(?i)\brm\s+--no-preserve-root",
            "rm --no-preserve-root",
            "critical",
        ),
        (r"(?i):\s*\(\s*\)\s*\{.*\|.*&", "fork bomb", "critical"),
        (
            r"(?i)(dd\s+if=|>\s*/dev/[sh]d)",
            "disk overwrite",
            "critical",
        ),
        (r"\$\([^)]+\)", "command substitution $(…)", "high"),
        (r"`[^`]+`", "command substitution backtick", "high"),
        (r"(?i)&&\s*curl\s+", "chained curl download", "high"),
        (r"(?i)&&\s*wget\s+", "chained wget download", "high"),
        (r"(?i)\beval\b", "eval execution", "high"),
        (
            r"(?i)chmod\s+[0-7]*7[0-7][0-7]",
            "world-writable chmod",
            "high",
        ),
        (r"\.\./\.\./\.\./", "deep path traversal", "medium"),
        (r"\.\./\.\./", "path traversal", "medium"),
        (r"__import__\(", "python import injection", "medium"),
        (r"(?i)base64\s+--decode", "base64 decode pipe", "medium"),
        (
            r"(?i)\bcurl\b.*\|\s*(ba)?sh\b",
            "curl pipe to shell",
            "critical",
        ),
        (
            r"(?i)\bwget\b.*-O\s*-.*\|\s*sh",
            "wget pipe to shell",
            "critical",
        ),
    ];
    raw.iter()
        .filter_map(|(pat, desc, sev)| Regex::new(pat).ok().map(|re| (re, *desc, *sev)))
        .collect()
});

fn severity_rank(s: &str) -> u8 {
    match s {
        "critical" => 4,
        "high" => 3,
        "medium" => 2,
        "low" => 1,
        _ => 0,
    }
}

fn rank_to_str(r: u8) -> &'static str {
    match r {
        4 => "critical",
        3 => "high",
        2 => "medium",
        1 => "low",
        _ => "safe",
    }
}

pub struct SentinelSkill;

impl Skill for SentinelSkill {
    fn id(&self) -> SkillId {
        SkillId("sentinel")
    }
    fn display_name(&self) -> &str {
        "Sentinel"
    }

    fn input_schema(&self) -> serde_json::Value {
        json!({
            "type": "object",
            "properties": {
                "subject":      { "type": "string" },
                "subject_kind": { "type": "string", "enum": ["command", "path", "tool_call"] }
            },
            "required": ["subject", "subject_kind"]
        })
    }

    fn output_schema(&self) -> serde_json::Value {
        json!({
            "type": "object",
            "properties": {
                "risk_level":         { "type": "string" },
                "findings":           { "type": "array" },
                "recommended_action": { "type": "string" }
            }
        })
    }

    fn execute(&self, input: SkillInput, _ctx: &SkillContext) -> anyhow::Result<SkillOutcome> {
        let subject = input.get_str("subject").unwrap_or("");
        let mut findings = Vec::new();
        let mut max_rank: u8 = 0;

        for (re, description, severity) in PATTERNS.iter() {
            if re.is_match(subject) {
                findings.push(json!({
                    "description": description,
                    "severity":    severity,
                    "matched":     re.find(subject).map(|m| m.as_str()).unwrap_or(""),
                }));
                let rank = severity_rank(severity);
                if rank > max_rank {
                    max_rank = rank;
                }
            }
        }

        let risk_level = rank_to_str(max_rank);
        let recommended_action = match risk_level {
            "critical" => "block",
            "high" => "warn",
            "medium" => "warn",
            _ => "allow",
        };

        Ok(SkillOutcome::success(
            "sentinel",
            json!({
                "risk_level":         risk_level,
                "findings":           findings,
                "recommended_action": recommended_action,
            }),
        ))
    }
}
