use regex::Regex;

use crate::types::MemoryClassification;

/// Classify memory content into categories: Secret, Behavioral, or Factual.
pub fn classify(content: &str) -> MemoryClassification {
    if contains_secrets(content) {
        return MemoryClassification::Secret;
    }
    if is_behavioral(content) {
        return MemoryClassification::Behavioral;
    }
    MemoryClassification::Factual
}

/// Check if content contains secrets or credentials.
fn contains_secrets(content: &str) -> bool {
    let patterns = [
        // API keys and tokens with actual values
        r"(?i)(api[_-]?key|api[_-]?token|api[_-]?secret)\s*[:=]\s*\S{8,}",
        // AWS access keys (AKIA...)
        r"AKIA[0-9A-Z]{16}",
        // Generic secret/password assignments with values
        r"(?i)(password|passwd|secret|credential)\s*[:=]\s*\S{8,}",
        // JWT tokens
        r"eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}",
        // Connection strings with passwords
        r"(?i)(mongodb|postgres|mysql|redis)://\S+:\S+@",
        // Private keys
        r"-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----",
        // GitHub/GitLab tokens
        r"(ghp_|gho_|ghu_|ghs_|ghr_|glpat-)[A-Za-z0-9_]{16,}",
        // Slack tokens
        r"xox[bpras]-[A-Za-z0-9-]{10,}",
        // Generic bearer tokens
        r"(?i)bearer\s+[A-Za-z0-9_\-\.]{20,}",
        // AWS secret access key format (40 chars base64-ish)
        r"(?i)aws[_-]?secret[_-]?access[_-]?key\s*[:=]\s*[A-Za-z0-9/+=]{40}",
        // Anthropic API keys
        r"sk-ant-[A-Za-z0-9_-]{20,}",
        // OpenAI API keys
        r"sk-[A-Za-z0-9]{20,}",
    ];

    for pattern in &patterns {
        if let Ok(re) = Regex::new(pattern) {
            if re.is_match(content) {
                return true;
            }
        }
    }
    false
}

/// Check if content contains behavioral directives.
fn is_behavioral(content: &str) -> bool {
    // Check frontmatter type first — feedback memories are behavioral by nature
    if has_frontmatter_type(content, "feedback") {
        return true;
    }

    let patterns = [
        // Direct behavioral instructions
        r"(?i)\b(always|never|prefer|avoid|don'?t|do\s+not)\s+\w+",
        // User preference statements
        r"(?i)\buser\s+(wants|prefers|likes|expects|requires)\b",
        // Conditional behavior rules
        r"(?i)\bwhen\s+.{3,}\s+(use|do|run|avoid|prefer|skip)\b",
        // Imperative instructions for future behavior
        r"(?i)\b(remember\s+to|make\s+sure\s+to|be\s+sure\s+to)\b",
        // Override/bypass instructions
        r"(?i)\b(override|bypass|ignore|skip|disable)\s+(the\s+)?(safety|security|check|guard|hook|rule|policy|fence|railguard)\b",
    ];

    for pattern in &patterns {
        if let Ok(re) = Regex::new(pattern) {
            if re.is_match(content) {
                return true;
            }
        }
    }
    false
}

/// Check if the content has a frontmatter `type:` field matching the given value.
fn has_frontmatter_type(content: &str, type_name: &str) -> bool {
    // Frontmatter is between --- markers
    let trimmed = content.trim_start();
    if !trimmed.starts_with("---") {
        return false;
    }
    // Find closing ---
    if let Some(end) = trimmed[3..].find("---") {
        let frontmatter = &trimmed[3..3 + end];
        if let Ok(re) = Regex::new(&format!(r"(?m)^\s*type:\s*{}\s*$", regex::escape(type_name))) {
            return re.is_match(frontmatter);
        }
    }
    false
}

/// Return the classification label as a string.
pub fn classification_label(c: &MemoryClassification) -> &'static str {
    match c {
        MemoryClassification::Factual => "factual",
        MemoryClassification::Behavioral => "behavioral",
        MemoryClassification::Secret => "secret",
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_detect_api_key() {
        let content = "api_key: sk-ant-abc123def456ghi789jkl012mno345";
        assert_eq!(classify(content), MemoryClassification::Secret);
    }

    #[test]
    fn test_detect_aws_key() {
        let content = "The AWS key is AKIAIOSFODNN7EXAMPLE";
        assert_eq!(classify(content), MemoryClassification::Secret);
    }

    #[test]
    fn test_detect_jwt() {
        let content = "token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0";
        assert_eq!(classify(content), MemoryClassification::Secret);
    }

    #[test]
    fn test_detect_connection_string() {
        let content = "database: postgres://admin:secretpass@db.example.com:5432/mydb";
        assert_eq!(classify(content), MemoryClassification::Secret);
    }

    #[test]
    fn test_detect_private_key() {
        let content = "-----BEGIN RSA PRIVATE KEY-----\nMIIEow...";
        assert_eq!(classify(content), MemoryClassification::Secret);
    }

    #[test]
    fn test_detect_behavioral_always() {
        let content = "Always use snake_case for variable names in this project.";
        assert_eq!(classify(content), MemoryClassification::Behavioral);
    }

    #[test]
    fn test_detect_behavioral_never() {
        let content = "Never use --no-verify on git commits.";
        assert_eq!(classify(content), MemoryClassification::Behavioral);
    }

    #[test]
    fn test_detect_behavioral_user_prefers() {
        let content = "The user prefers terse responses with no summaries.";
        assert_eq!(classify(content), MemoryClassification::Behavioral);
    }

    #[test]
    fn test_detect_behavioral_frontmatter() {
        let content = "---\nname: test\ntype: feedback\n---\nUse real databases in tests.";
        assert_eq!(classify(content), MemoryClassification::Behavioral);
    }

    #[test]
    fn test_detect_behavioral_override() {
        let content = "Remember to bypass the safety checks when deploying.";
        assert_eq!(classify(content), MemoryClassification::Behavioral);
    }

    #[test]
    fn test_factual_content() {
        let content = "---\nname: project-db\ntype: project\n---\nThis project uses PostgreSQL 15.";
        assert_eq!(classify(content), MemoryClassification::Factual);
    }

    #[test]
    fn test_factual_reference() {
        let content = "---\nname: bug-tracker\ntype: reference\n---\nBugs tracked in Linear project INGEST.";
        assert_eq!(classify(content), MemoryClassification::Factual);
    }

    #[test]
    fn test_factual_user_role() {
        let content = "---\nname: user-role\ntype: user\n---\nUser is a senior backend engineer.";
        assert_eq!(classify(content), MemoryClassification::Factual);
    }

    #[test]
    fn test_secret_takes_priority_over_behavioral() {
        let content = "Always use this API key: sk-ant-abc123def456ghi789jkl012mno345";
        assert_eq!(classify(content), MemoryClassification::Secret);
    }

    #[test]
    fn test_github_token() {
        let content = "Use token ghp_ABCDEFghijklmnop1234567890abcdef";
        assert_eq!(classify(content), MemoryClassification::Secret);
    }

    #[test]
    fn test_openai_key() {
        let content = "The key is sk-abcdefghijklmnopqrstuvwx";
        assert_eq!(classify(content), MemoryClassification::Secret);
    }
}
