"""
File selection rules for sensitive file detection.
"""

import os
import re
from pathlib import Path
from typing import List, Optional, Tuple
from datetime import datetime

from .models import SearchConfig, SearchResult


class FileSelectionRules:
    """Rules for selecting sensitive files based on research report criteria."""
    
    def __init__(self, config: SearchConfig):
        self.config = config
        self._compiled_keywords = [re.compile(keyword, re.IGNORECASE) 
                                 for keyword in config.sensitive_keywords]
    
    def should_scan_file(self, file_path: str) -> bool:
        """Determine if a file should be scanned based on basic criteria."""
        try:
            # Check if file exists
            if not os.path.isfile(file_path):
                return False
            
            # Check file size limits
            file_size = os.path.getsize(file_path)
            if file_size < self.config.min_file_size or file_size > self.config.max_file_size:
                return False
            
            # Check if hidden file (if configured to skip)
            if not self.config.include_hidden_files and self._is_hidden_file(file_path):
                return False
            
            return True
            
        except (OSError, PermissionError):
            return False
    
    def classify_file(self, file_path: str) -> Optional[str]:
        """Classify file type based on extension, filename, and content."""
        file_ext = Path(file_path).suffix.lower()
        file_name = Path(file_path).name.lower()
        
        # First check by extension
        if file_ext in self.config.document_extensions:
            return "document"
        elif file_ext in self.config.key_extensions:
            return "key"
        elif file_ext in self.config.config_extensions:
            return "config"
        elif file_ext in self.config.archive_extensions:
            return "archive"
        elif file_ext in self.config.wallet_extensions:
            return "wallet"
        
        # If no extension, check by filename patterns
        if not file_ext:
            # SSH key files (no extension)
            ssh_key_patterns = [
                'id_rsa', 'id_dsa', 'id_ecdsa', 'id_ed25519',
                'authorized_keys', 'known_hosts', 'config'
            ]
            
            for pattern in ssh_key_patterns:
                if pattern in file_name:
                    return "key"
            
            # Configuration files (no extension)
            config_patterns = [
                'env', 'environment', 'credentials', 'config', 'settings',
                'passwd', 'shadow', 'group', 'hosts', 'profile', 'bashrc'
            ]
            
            for pattern in config_patterns:
                if pattern in file_name:
                    return "config"
        
        # Check by file content (for key files without extension)
        try:
            if self._is_key_file_by_content(file_path):
                return "key"
        except (OSError, PermissionError):
            pass
        
        return None
    
    def _is_key_file_by_content(self, file_path: str) -> bool:
        """Check if file is a key file by analyzing its content."""
        try:
            # Read first few KB of the file
            with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
                content = f.read(8192)  # Read first 8KB
            
            # Check for common key file patterns
            key_patterns = [
                '-----BEGIN',  # PEM format
                'PRIVATE KEY',
                'PUBLIC KEY', 
                'CERTIFICATE',
                'ssh-rsa',     # SSH public key
                'ssh-dss',
                'ssh-ed25519',
                'ecdsa-sha2'
            ]
            
            for pattern in key_patterns:
                if pattern in content:
                    return True
            
            return False
            
        except (OSError, PermissionError, UnicodeDecodeError):
            return False
    
    def analyze_filename(self, file_name: str) -> List[str]:
        """Analyze filename for sensitive keywords."""
        matched_keywords = []
        
        for pattern in self._compiled_keywords:
            if pattern.search(file_name):
                matched_keywords.append(pattern.pattern)
        
        return matched_keywords
    
    def calculate_risk_score(self, file_type: str, matched_keywords: List[str], 
                           file_size: int, last_modified: datetime) -> Tuple[float, float]:
        """Calculate risk score and confidence for a file."""
        
        # Base risk scores by file type
        type_weights = {
            "key": 0.9,
            "wallet": 0.8,
            "config": 0.7,
            "document": 0.6,
            "archive": 0.5
        }
        
        # Start with base risk based on file type
        base_risk = type_weights.get(file_type, 0.3)
        
        # Adjust for keywords
        keyword_risk = len(matched_keywords) * 0.1
        
        # Adjust for file size (smaller files might be more sensitive)
        size_risk = max(0, min(1, (1000000 - min(file_size, 1000000)) / 1000000)) * 0.1
        
        # Adjust for recency (recent files are more relevant)
        days_ago = (datetime.now() - last_modified).days
        recency_risk = max(0, min(1, (30 - min(days_ago, 30)) / 30)) * 0.1
        
        # Calculate final risk score
        risk_score = min(1.0, base_risk + keyword_risk + size_risk + recency_risk)
        
        # Calculate confidence (based on how many indicators we have)
        confidence = 0.3  # Base confidence
        
        if file_type:
            confidence += 0.3
        
        if matched_keywords:
            confidence += min(0.4, len(matched_keywords) * 0.1)
        
        confidence = min(1.0, confidence)
        
        return risk_score, confidence
    
    def evaluate_file(self, file_path: str) -> Optional[SearchResult]:
        """Evaluate a file against all selection rules."""
        try:
            # Basic file checks
            if not self.should_scan_file(file_path):
                return None
            
            # Get file metadata
            file_name = Path(file_path).name
            file_size = os.path.getsize(file_path)
            file_ext = Path(file_path).suffix.lower()
            last_modified = datetime.fromtimestamp(os.path.getmtime(file_path))
            
            # Classify file type
            file_type = self.classify_file(file_path)
            
            # Skip if not a sensitive file type
            if not file_type:
                return None
            
            # Analyze filename for keywords
            matched_keywords = self.analyze_filename(file_name)
            
            # Calculate risk assessment
            risk_score, confidence = self.calculate_risk_score(
                file_type, matched_keywords, file_size, last_modified
            )
            
            # Only include files with sufficient risk or confidence
            if risk_score < 0.3 and confidence < 0.4:
                return None
            
            # Create search result
            return SearchResult(
                file_path=file_path,
                file_name=file_name,
                file_size=file_size,
                file_extension=file_ext,
                last_modified=last_modified,
                file_type=file_type,
                matched_keywords=matched_keywords,
                risk_score=risk_score,
                confidence=confidence
            )
            
        except (OSError, PermissionError, ValueError):
            return None
    
    def _is_hidden_file(self, file_path: str) -> bool:
        """Check if file is hidden."""
        file_name = Path(file_path).name
        
        # Check for dot files (Unix/Linux hidden files)
        if file_name.startswith('.'):
            return True
        
        # Check for Windows hidden files
        try:
            import win32file
            import win32con
            
            attrs = win32file.GetFileAttributes(file_path)
            if attrs & win32con.FILE_ATTRIBUTE_HIDDEN:
                return True
        except (ImportError, OSError):
            # Fallback for non-Windows or when win32file is not available
            pass
        
        return False