"""
JSEye Caching System - Make re-runs INSANELY fast
"""

import json
import hashlib
import time
from pathlib import Path
from typing import Dict, Any, Optional, List

from .logger import log_progress

class JSEyeCache:
    """
    🔥 CACHE EVERYTHING FOR INSANE SPEED
    
    Caches:
    - JS file hashes (detect changes)
    - Analysis results (regex, AST, secrets)
    - Tool outputs (gau, waybackurls, etc.)
    - Download results
    """
    
    def __init__(self, output_dir: Path):
        self.cache_dir = Path.home() / ".jseye" / "cache"
        self.cache_dir.mkdir(parents=True, exist_ok=True)
        
        # Cache files
        self.js_hashes_file = self.cache_dir / "js_hashes.json"
        self.analysis_cache_file = self.cache_dir / "analysis_results.json"
        self.tool_cache_file = self.cache_dir / "tool_outputs.json"
        self.download_cache_file = self.cache_dir / "downloads.json"
        
        # Load existing caches
        self.js_hashes = self.load_cache(self.js_hashes_file)
        self.analysis_cache = self.load_cache(self.analysis_cache_file)
        self.tool_cache = self.load_cache(self.tool_cache_file)
        self.download_cache = self.load_cache(self.download_cache_file)
        
        # Stats
        self.cache_hits = 0
        self.cache_misses = 0
    
    def load_cache(self, cache_file: Path) -> Dict[str, Any]:
        """Load cache from file"""
        try:
            if cache_file.exists():
                with open(cache_file, 'r') as f:
                    return json.load(f)
        except Exception as e:
            log_progress(f"Warning: Could not load cache {cache_file.name}: {e}")
        
        return {}
    
    def save_cache(self, cache_data: Dict[str, Any], cache_file: Path):
        """Save cache to file"""
        try:
            with open(cache_file, 'w') as f:
                json.dump(cache_data, f, indent=2)
        except Exception as e:
            log_progress(f"Warning: Could not save cache {cache_file.name}: {e}")
    
    def get_file_hash(self, file_path: str) -> str:
        """Get SHA256 hash of file content"""
        try:
            with open(file_path, 'rb') as f:
                content = f.read()
            return hashlib.sha256(content).hexdigest()
        except Exception:
            return ""
    
    def get_url_hash(self, url: str) -> str:
        """Get hash of URL for caching"""
        return hashlib.sha256(url.encode()).hexdigest()[:16]
    
    def is_js_file_cached(self, file_path: str) -> bool:
        """Check if JS file analysis is cached and up-to-date"""
        if not Path(file_path).exists():
            return False
        
        current_hash = self.get_file_hash(file_path)
        cached_hash = self.js_hashes.get(file_path)
        
        return current_hash == cached_hash and current_hash != ""
    
    def cache_js_file_hash(self, file_path: str):
        """Cache JS file hash"""
        file_hash = self.get_file_hash(file_path)
        if file_hash:
            self.js_hashes[file_path] = file_hash
            self.save_cache(self.js_hashes, self.js_hashes_file)
    
    def get_analysis_cache(self, file_path: str, analysis_type: str) -> Optional[Dict[str, Any]]:
        """Get cached analysis results"""
        if not self.is_js_file_cached(file_path):
            self.cache_misses += 1
            return None
        
        cache_key = f"{file_path}:{analysis_type}"
        cached_result = self.analysis_cache.get(cache_key)
        
        if cached_result:
            self.cache_hits += 1
            log_progress(f"[C] Cache HIT: {analysis_type} for {Path(file_path).name}")
            return cached_result
        
        self.cache_misses += 1
        return None
    
    def set_analysis_cache(self, file_path: str, analysis_type: str, results: Dict[str, Any]):
        """Cache analysis results"""
        cache_key = f"{file_path}:{analysis_type}"
        
        # Add timestamp
        results['cached_at'] = time.time()
        
        self.analysis_cache[cache_key] = results
        self.save_cache(self.analysis_cache, self.analysis_cache_file)
        
        # Also cache the file hash
        self.cache_js_file_hash(file_path)
    
    def get_tool_cache(self, tool_name: str, domain: str) -> Optional[List[str]]:
        """Get cached tool output"""
        cache_key = f"{tool_name}:{domain}"
        cached_result = self.tool_cache.get(cache_key)
        
        if cached_result:
            # Check if cache is not too old (24 hours)
            cached_time = cached_result.get('timestamp', 0)
            if time.time() - cached_time < 86400:  # 24 hours
                self.cache_hits += 1
                log_progress(f"[C] Tool cache HIT: {tool_name} for {domain}")
                return cached_result.get('urls', [])
        
        self.cache_misses += 1
        return None
    
    def set_tool_cache(self, tool_name: str, domain: str, urls: List[str]):
        """Cache tool output"""
        cache_key = f"{tool_name}:{domain}"
        
        self.tool_cache[cache_key] = {
            'urls': urls,
            'timestamp': time.time()
        }
        self.save_cache(self.tool_cache, self.tool_cache_file)
    
    def get_download_cache(self, url: str) -> Optional[Dict[str, Any]]:
        """Get cached download result"""
        url_hash = self.get_url_hash(url)
        cached_result = self.download_cache.get(url_hash)
        
        if cached_result:
            # Check if cached file still exists
            cached_path = cached_result.get('filepath')
            if cached_path and Path(cached_path).exists():
                self.cache_hits += 1
                log_progress(f"[C] Download cache HIT: {url}")
                return cached_result
        
        self.cache_misses += 1
        return None
    
    def set_download_cache(self, url: str, download_result: Dict[str, Any]):
        """Cache download result"""
        url_hash = self.get_url_hash(url)
        
        # Add timestamp
        download_result['cached_at'] = time.time()
        
        self.download_cache[url_hash] = download_result
        self.save_cache(self.download_cache, self.download_cache_file)
    
    def clear_cache(self, cache_type: str = "all"):
        """Clear specific or all caches"""
        if cache_type in ["all", "js"]:
            self.js_hashes.clear()
            self.save_cache(self.js_hashes, self.js_hashes_file)
        
        if cache_type in ["all", "analysis"]:
            self.analysis_cache.clear()
            self.save_cache(self.analysis_cache, self.analysis_cache_file)
        
        if cache_type in ["all", "tools"]:
            self.tool_cache.clear()
            self.save_cache(self.tool_cache, self.tool_cache_file)
        
        if cache_type in ["all", "downloads"]:
            self.download_cache.clear()
            self.save_cache(self.download_cache, self.download_cache_file)
        
        log_progress(f"[-] Cleared {cache_type} cache")
    
    def get_cache_stats(self) -> Dict[str, Any]:
        """Get cache statistics"""
        total_requests = self.cache_hits + self.cache_misses
        hit_rate = (self.cache_hits / total_requests * 100) if total_requests > 0 else 0
        
        return {
            'cache_hits': self.cache_hits,
            'cache_misses': self.cache_misses,
            'hit_rate_percent': round(hit_rate, 1),
            'cached_js_files': len(self.js_hashes),
            'cached_analyses': len(self.analysis_cache),
            'cached_tool_outputs': len(self.tool_cache),
            'cached_downloads': len(self.download_cache)
        }
    
    def cleanup_old_cache(self, max_age_days: int = 7):
        """Clean up old cache entries"""
        current_time = time.time()
        max_age_seconds = max_age_days * 24 * 3600
        
        # Clean analysis cache
        old_keys = []
        for key, value in self.analysis_cache.items():
            if isinstance(value, dict) and 'cached_at' in value:
                if current_time - value['cached_at'] > max_age_seconds:
                    old_keys.append(key)
        
        for key in old_keys:
            del self.analysis_cache[key]
        
        if old_keys:
            self.save_cache(self.analysis_cache, self.analysis_cache_file)
            log_progress(f"🧹 Cleaned {len(old_keys)} old analysis cache entries")
        
        # Clean tool cache
        old_keys = []
        for key, value in self.tool_cache.items():
            if isinstance(value, dict) and 'timestamp' in value:
                if current_time - value['timestamp'] > max_age_seconds:
                    old_keys.append(key)
        
        for key in old_keys:
            del self.tool_cache[key]
        
        if old_keys:
            self.save_cache(self.tool_cache, self.tool_cache_file)
            log_progress(f"🧹 Cleaned {len(old_keys)} old tool cache entries")