"""Core code scanning logic for detecting suspicious patterns."""

import ast
import re
from dataclasses import dataclass
from enum import Enum
from pathlib import Path
from typing import List, Dict, Any, Optional
import tree_sitter_python as tspython
import tree_sitter_javascript as tsjavascript
import tree_sitter_typescript as tstypescript
from tree_sitter import Language, Parser, Node


class IssueType(Enum):
    """Types of issues detected by the scanner."""
    HALLUCINATED_IMPORT = "hallucinated_import"
    SUSPICIOUS_COMMENT = "suspicious_comment"
    TODO_PLACEHOLDER = "todo_placeholder"
    MAGIC_NUMBER = "magic_number"
    UNUSED_VARIABLE = "unused_variable"
    COMPLEX_FUNCTION = "complex_function"
    INCONSISTENT_STYLE = "inconsistent_style"
    POTENTIAL_SECURITY = "potential_security"


@dataclass
class Issue:
    """Represents a single code issue."""
    type: IssueType
    line: int
    column: int
    message: str
    severity: str  # "error", "warning", "info"
    suggestion: Optional[str] = None
    ai_generated_likelihood: float = 0.0  # 0.0 to 1.0


@dataclass
class ScanResult:
    """Results from scanning a file."""
    file_path: str
    issues: List[Issue]
    ai_confidence_score: float  # 0.0 (definitely human) to 1.0 (definitely AI)
    metrics: Dict[str, Any]
    lines_of_code: int
    
    @property
    def error_count(self) -> int:
        return sum(1 for i in self.issues if i.severity == "error")
    
    @property
    def warning_count(self) -> int:
        return sum(1 for i in self.issues if i.severity == "warning")


class CodeScanner:
    """Scans code files for suspicious patterns and AI-generated markers."""
    
    # Common AI hallucination patterns
    HALLUCINATED_IMPORTS = {
        "python": [
            "from sklearn.experimental",
            "import pandas.v2",
            "from tensorflow.keras.new",
            "import numpy.advanced",
            "from typing.protocols",  # Real but often misused
        ],
        "javascript": [
            "import { futureAPI }",
            "from 'react-experimental'",
            "require('node:experimental')",
        ],
        "typescript": [
            "import type { Experimental",
            "from '@types/future'",
        ]
    }
    
    # AI-generated comment patterns
    AI_COMMENT_PATTERNS = [
        r"(?i)# (?:TODO|FIXME|NOTE):\s*(?:implement|add|fix|update)\s+(?:this|here)",
        r"(?i)//\s*(?:TODO|FIXME):\s*(?:implement|add)\s+(?:actual|real)\s+(?:logic|implementation)",
        r"(?i)#\s*placeholder",
        r"(?i)//\s*generated by AI",
        r"(?i)#\s*AI (?:generated|created|wrote)",
        r"(?i)<!--\s*TODO:",
    ]
    
    # Suspicious variable/function names (too generic, AI-like)
    GENERIC_NAMES = {
        "temp", "tmp", "data", "result", "output", "input", "val", "var",
        "foo", "bar", "baz", "thing", "item", "obj", "arr", "list1", "list2"
    }
    
    def __init__(self):
        """Initialize the scanner with language parsers."""
        self.parsers = {
            "python": self._create_parser(Language(tspython.language())),
            "javascript": self._create_parser(Language(tsjavascript.language())),
            "typescript": self._create_parser(Language(tstypescript.language())),
        }
    
    def _create_parser(self, language: Language) -> Parser:
        """Create a tree-sitter parser for a language."""
        parser = Parser()
        parser.set_language(language)
        return parser
    
    def scan_file(self, file_path: Path) -> ScanResult:
        """Scan a single file and return results."""
        content = file_path.read_text(encoding="utf-8", errors="ignore")
        ext = file_path.suffix.lower()
        
        issues: List[Issue] = []
        
        # Determine language
        lang = self._detect_language(ext)
        
        if lang == "python":
            issues.extend(self._scan_python(content))
        elif lang in ["javascript", "typescript"]:
            issues.extend(self._scan_js_ts(content, lang))
        else:
            issues.extend(self._scan_generic(content))
        
        # Calculate AI confidence score
        metrics = self._calculate_metrics(content, issues)
        ai_score = self._calculate_ai_confidence(content, issues, metrics)
        
        return ScanResult(
            file_path=str(file_path),
            issues=issues,
            ai_confidence_score=ai_score,
            metrics=metrics,
            lines_of_code=len([l for l in content.split('\n') if l.strip()])
        )
    
    def _detect_language(self, ext: str) -> str:
        """Detect programming language from file extension."""
        lang_map = {
            ".py": "python",
            ".js": "javascript",
            ".jsx": "javascript",
            ".ts": "typescript",
            ".tsx": "typescript",
        }
        return lang_map.get(ext, "unknown")
    
    def _scan_python(self, content: str) -> List[Issue]:
        """Scan Python code for issues."""
        issues: List[Issue] = []
        
        try:
            tree = ast.parse(content)
            
            # Check imports
            for node in ast.walk(tree):
                if isinstance(node, ast.Import):
                    for alias in node.names:
                        issues.extend(self._check_import(alias.name, node.lineno, "python"))
                
                elif isinstance(node, ast.ImportFrom):
                    module = node.module or ""
                    for pattern in self.HALLUCINATED_IMPORTS["python"]:
                        if pattern in f"from {module}":
                            issues.append(Issue(
                                type=IssueType.HALLUCINATED_IMPORT,
                                line=node.lineno,
                                column=node.col_offset,
                                message=f"Suspicious import pattern: {module}",
                                severity="error",
                                ai_generated_likelihood=0.85
                            ))
                
                # Check for generic variable names
                elif isinstance(node, ast.Name):
                    if node.id.lower() in self.GENERIC_NAMES:
                        issues.append(Issue(
                            type=IssueType.INCONSISTENT_STYLE,
                            line=node.lineno,
                            column=node.col_offset,
                            message=f"Generic variable name: {node.id}",
                            severity="warning",
                            ai_generated_likelihood=0.6
                        ))
                
                # Check function complexity
                elif isinstance(node, ast.FunctionDef):
                    complexity = self._calculate_cyclomatic_complexity(node)
                    if complexity > 10:
                        issues.append(Issue(
                            type=IssueType.COMPLEX_FUNCTION,
                            line=node.lineno,
                            column=node.col_offset,
                            message=f"High complexity function '{node.name}' (complexity: {complexity})",
                            severity="warning",
                            ai_generated_likelihood=0.4
                        ))
        
        except SyntaxError as e:
            issues.append(Issue(
                type=IssueType.POTENTIAL_SECURITY,
                line=e.lineno or 0,
                column=0,
                message=f"Syntax error: {e.msg}",
                severity="error",
                ai_generated_likelihood=0.3
            ))
        
        # Check comments
        issues.extend(self._scan_comments(content))
        
        return issues
    
    def _scan_js_ts(self, content: str, lang: str) -> List[Issue]:
        """Scan JavaScript/TypeScript code."""
        issues: List[Issue] = []
        
        # Check imports using regex (tree-sitter would be more robust but regex is faster)
        import_patterns = [
            r"import\s+.*?\s+from\s+['\"]([^'\"]+)['\"]",
            r"require\(['\"]([^'\"]+)['\"]\)",
        ]
        
        for i, line in enumerate(content.split('\n'), 1):
            for pattern in import_patterns:
                matches = re.finditer(pattern, line)
                for match in matches:
                    module = match.group(1)
                    issues.extend(self._check_import(module, i, lang))
        
        # Check comments
        issues.extend(self._scan_comments(content))
        
        # Check for console.log (often left by AI)
        for i, line in enumerate(content.split('\n'), 1):
            if re.search(r'\bconsole\.log\b', line):
                issues.append(Issue(
                    type=IssueType.TODO_PLACEHOLDER,
                    line=i,
                    column=line.find('console.log'),
                    message="Debug console.log statement found",
                    severity="info",
                    ai_generated_likelihood=0.5
                ))
        
        return issues
    
    def _scan_generic(self, content: str) -> List[Issue]:
        """Scan any file for generic patterns."""
        issues: List[Issue] = []
        issues.extend(self._scan_comments(content))
        return issues
    
    def _scan_comments(self, content: str) -> List[Issue]:
        """Scan for suspicious comment patterns."""
        issues: List[Issue] = []
        
        for i, line in enumerate(content.split('\n'), 1):
            for pattern in self.AI_COMMENT_PATTERNS:
                if re.search(pattern, line):
                    issues.append(Issue(
                        type=IssueType.SUSPICIOUS_COMMENT,
                        line=i,
                        column=0,
                        message="AI-generated comment pattern detected",
                        severity="warning",
                        ai_generated_likelihood=0.75,
                        suggestion="Review and replace with specific implementation details"
                    ))
        
        return issues
    
    def _check_import(self, module: str, line: int, lang: str) -> List[Issue]:
        """Check if an import looks suspicious."""
        issues: List[Issue] = []
        
        hallucinated = self.HALLUCINATED_IMPORTS.get(lang, [])
        for pattern in hallucinated:
            if pattern.lower() in module.lower():
                issues.append(Issue(
                    type=IssueType.HALLUCINATED_IMPORT,
                    line=line,
                    column=0,
                    message=f"Potentially hallucinated import: {module}",
                    severity="error",
                    ai_generated_likelihood=0.9,
                    suggestion="Verify this module exists in your dependencies"
                ))
        
        return issues
    
    def _calculate_cyclomatic_complexity(self, node: ast.FunctionDef) -> int:
        """Calculate cyclomatic complexity of a function."""
        complexity = 1  # Base complexity
        
        for child in ast.walk(node):
            if isinstance(child, (ast.If, ast.While, ast.For, ast.ExceptHandler)):
                complexity += 1
            elif isinstance(child, ast.BoolOp):
                complexity += len(child.values) - 1
        
        return complexity
    
    def _calculate_metrics(self, content: str, issues: List[Issue]) -> Dict[str, Any]:
        """Calculate code metrics."""
        lines = content.split('\n')
        
        return {
            "total_lines": len(lines),
            "code_lines": len([l for l in lines if l.strip() and not l.strip().startswith('#')]),
            "comment_lines": len([l for l in lines if l.strip().startswith('#') or l.strip().startswith('//')]),
            "blank_lines": len([l for l in lines if not l.strip()]),
            "avg_line_length": sum(len(l) for l in lines) / max(len(lines), 1),
            "issue_count": len(issues),
            "error_count": sum(1 for i in issues if i.severity == "error"),
        }
    
    def _calculate_ai_confidence(
        self, content: str, issues: List[Issue], metrics: Dict[str, Any]
    ) -> float:
        """
        Calculate confidence that code was AI-generated.
        Returns 0.0 (definitely human) to 1.0 (definitely AI).
        """
        score = 0.0
        factors = []
        
        # Factor 1: High-confidence issues (hallucinated imports, AI comments)
        high_conf_issues = [i for i in issues if i.ai_generated_likelihood > 0.7]
        if high_conf_issues:
            factors.append(min(len(high_conf_issues) * 0.15, 0.6))
        
        # Factor 2: Too many TODO/placeholder comments
        todo_issues = [i for i in issues if i.type == IssueType.SUSPICIOUS_COMMENT]
        if len(todo_issues) > 2:
            factors.append(0.3)
        
        # Factor 3: Perfect formatting (AI often formats too well)
        avg_length = metrics["avg_line_length"]
        if 70 <= avg_length <= 85:  # Suspiciously consistent line length
            factors.append(0.2)
        
        # Factor 4: Generic variable names
        generic_issues = [i for i in issues if i.type == IssueType.INCONSISTENT_STYLE]
        if len(generic_issues) > 5:
            factors.append(0.25)
        
        # Factor 5: Check for AI signature patterns in content
        ai_signatures = [
            "here's a", "here is a", "i've created", "i've implemented",
            "this code", "this function", "this will", "you can use"
        ]
        content_lower = content.lower()
        if any(sig in content_lower for sig in ai_signatures):
            factors.append(0.3)
        
        # Combine factors (cap at 0.95, never 100% certain)
        if factors:
            score = min(sum(factors), 0.95)
        
        return round(score, 2)
