//! Unreal Engine C++ extractor.
//!
//! Detects security and architecture issues in Unreal Engine C++ code, such as:
//! - Exposed Exec functions (can be called from console by users/cheaters)
//! - Unprotected replication variables
//! - Hardcoded asset paths (fragile and hard to maintain)

#![allow(clippy::too_many_arguments)]

use regex::Regex;
use stemedb_core::types::ObjectValue;

use super::Extractor;
use crate::types::{ExtractedClaim, Language};

/// Extractor for Unreal Engine C++ patterns.
pub struct UnrealCppExtractor {
    /// UFUNCTION(Exec) detection
    exec_function: Regex,
    /// UPROPERTY(Replicated) without condition
    unconditional_replication: Regex,
    /// Hardcoded asset paths (TEXT("/Game/...") or TEXT("/Engine/..."))
    hardcoded_asset_path: Regex,
}

impl Default for UnrealCppExtractor {
    fn default() -> Self {
        Self::new()
    }
}

impl UnrealCppExtractor {
    /// Create a new Unreal C++ extractor with compiled regexes.
    ///
    /// # Panics
    /// Panics if regex patterns are invalid.
    #[allow(clippy::expect_used)]
    pub fn new() -> Self {
        Self {
            exec_function: Regex::new(r"UFUNCTION\s*\((?:[^)]*,\s*)?Exec(?:,\s*[^)]*)?\)")
                .expect("valid regex"),
            unconditional_replication: Regex::new(
                r"UPROPERTY\s*\((?:[^)]*,\s*)?Replicated(?:\s*)\)",
            )
            .expect("valid regex"),
            hardcoded_asset_path: Regex::new(r#"TEXT\s*\(\s*['"]/(Game|Engine)/[^'"]+['"]\s*\)"#)
                .expect("valid regex"),
        }
    }

    fn check_pattern(
        &self,
        content: &str,
        pattern: &Regex,
        path_segments: &[String],
        file: &str,
        category: &str,
        leaf: &str,
        desc_template: &str,
    ) -> Vec<ExtractedClaim> {
        let mut claims = Vec::new();

        for (line_idx, line) in content.lines().enumerate() {
            if let Some(matched) = pattern.find(line) {
                let mut concept_path = path_segments.to_vec();
                concept_path.push("unreal".to_string());
                concept_path.push(category.to_string());
                concept_path.push(leaf.to_string());

                claims.push(ExtractedClaim {
                    concept_path: format!("code://{}", concept_path.join("/")),
                    predicate: "exposed".to_string(), // Default predicate
                    value: ObjectValue::Boolean(true),
                    file: file.to_string(),
                    line: line_idx + 1,
                    matched_text: matched.as_str().to_string(),
                    confidence: 0.9,
                    description: desc_template.to_string(),
                });
            }
        }

        claims
    }
}

impl Extractor for UnrealCppExtractor {
    fn name(&self) -> &str {
        "unreal_cpp"
    }

    fn languages(&self) -> &[Language] {
        &[Language::Cpp]
    }

    fn extract(
        &self,
        path_segments: &[String],
        content: &str,
        language: Language,
        file: &str,
    ) -> Vec<ExtractedClaim> {
        if language != Language::Cpp {
            return vec![];
        }

        let mut claims = Vec::new();

        // Check for Exec functions
        claims.extend(self.check_pattern(
            content,
            &self.exec_function,
            path_segments,
            file,
            "security",
            "exec_function",
            "UFUNCTION(Exec) exposes this function to the console",
        ));

        // Check for Unconditional Replication
        claims.extend(self.check_pattern(
            content,
            &self.unconditional_replication,
            path_segments,
            file,
            "security",
            "replication",
            "UPROPERTY(Replicated) used without condition",
        ));

        // Check for Hardcoded Asset Paths
        claims.extend(self.check_pattern(
            content,
            &self.hardcoded_asset_path,
            path_segments,
            file,
            "assets",
            "hardcoded_path",
            "Hardcoded asset path found in C++. Use SoftObjectPtr or UPROPERTY(Config) instead.",
        ));

        claims
    }
}

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

    #[test]
    fn test_exec_function_detection() {
        let extractor = UnrealCppExtractor::new();
        let content = r#"
            UCLASS()
            class AMyActor : public AActor {
                GENERATED_BODY()

                UFUNCTION(Exec)
                void CheatGiveMoney();
            };
        "#;

        let claims = extractor.extract(
            &["cpp".to_string()],
            content,
            Language::Cpp,
            "Source/MyGame/MyActor.h",
        );

        assert_eq!(claims.len(), 1);
        assert_eq!(claims[0].concept_path, "code://cpp/unreal/security/exec_function");
    }

    #[test]
    fn test_hardcoded_asset_path_detection() {
        let extractor = UnrealCppExtractor::new();
        let content = r#"
            static ConstructorHelpers::FObjectFinder<UTexture2D> Logo(TEXT("/Game/UI/Logo"));
            static ConstructorHelpers::FClassFinder<AActor> Pawn(TEXT("/Engine/BasicShapes/Cube"));
        "#;

        let claims = extractor.extract(
            &["cpp".to_string()],
            content,
            Language::Cpp,
            "Source/MyGame/MyActor.cpp",
        );

        assert_eq!(claims.len(), 2);
        assert_eq!(claims[0].concept_path, "code://cpp/unreal/assets/hardcoded_path");
        assert_eq!(claims[1].concept_path, "code://cpp/unreal/assets/hardcoded_path");
    }
}
