"""
AI Service - Unified interface for AI providers with caching
"""
import logging
import hashlib
import time
from typing import Optional, Dict, Any
from threading import Lock

from config import settings
from core import AIProcessingError
from .ai.provider_factory import AIProviderFactory, ai_provider_factory


class LRUCache:
    """Thread-safe LRU Cache implementation"""
    
    def __init__(self, max_size: int = 100, ttl: int = 3600):
        self.max_size = max_size
        self.ttl = ttl
        self._cache: Dict[str, tuple[str, float]] = {}
        self._order: list[str] = []
        self._lock = Lock()
    
    def _is_expired(self, timestamp: float) -> bool:
        return (time.time() - timestamp) > self.ttl
    
    def get(self, key: str) -> Optional[str]:
        with self._lock:
            if key not in self._cache:
                return None
            value, timestamp = self._cache[key]
            if self._is_expired(timestamp):
                del self._cache[key]
                self._order.remove(key)
                return None
            # Move to end (most recently used)
            self._order.remove(key)
            self._order.append(key)
            return value
    
    def set(self, key: str, value: str) -> None:
        with self._lock:
            if key in self._cache:
                self._order.remove(key)
            elif len(self._order) >= self.max_size:
                # Remove least recently used
                oldest = self._order.pop(0)
                del self._cache[oldest]
            self._cache[key] = (value, time.time())
            self._order.append(key)
    
    def stats(self) -> Dict[str, int]:
        with self._lock:
            return {
                "size": len(self._cache),
                "max_size": self.max_size,
                "hits": sum(1 for _, t in self._cache.values() if not self._is_expired(t))
            }


class RateLimiter:
    """Token bucket rate limiter"""
    
    def __init__(self, rate: float = 10, capacity: int = 20):
        self.rate = rate  # tokens per second
        self.capacity = capacity
        self.tokens = capacity
        self.last_update = time.time()
        self._lock = Lock()
    
    def acquire(self, tokens: int = 1) -> float:
        with self._lock:
            now = time.time()
            elapsed = now - self.last_update
            self.last_update = now
            self.tokens = min(self.capacity, self.tokens + elapsed * self.rate)
            
            if self.tokens >= tokens:
                self.tokens -= tokens
                return 0.0
            
            wait_time = (tokens - self.tokens) / self.rate
            self.tokens = 0
            return wait_time


class AIService:
    """Unified service for AI operations with caching and rate limiting"""
    
    def __init__(self):
        self.logger = logging.getLogger(__name__)
        self._factory: Optional[AIProviderFactory] = None
        self._prompt_cache = LRUCache(max_size=100, ttl=3600)  # 1 hour TTL
        self._rate_limiter = RateLimiter(rate=15, capacity=30)
        self._stats = {
            "total_requests": 0,
            "cache_hits": 0,
            "api_calls": 0
        }
    
    @property
    def factory(self) -> AIProviderFactory:
        """Lazy initialization of provider factory"""
        if self._factory is None:
            self._factory = ai_provider_factory
        return self._factory
    
    def _get_cache_key(self, prompt: str, operation: str) -> str:
        """Generate cache key from prompt and operation"""
        content = f"{operation}:{prompt[:500]}"  # Limit prompt length
        return hashlib.sha256(content.encode()).hexdigest()
    
    def generate_text(
        self,
        prompt: str,
        provider: Optional[str] = None,
        max_tokens: int = 4096
    ) -> str:
        """Generate text using AI provider with caching"""
        self._stats["total_requests"] += 1
        
        cache_key = self._get_cache_key(prompt, f"generate:{provider or 'default'}")
        
        # Check cache
        cached_result = self._prompt_cache.get(cache_key)
        if cached_result:
            self._stats["cache_hits"] += 1
            self.logger.debug(f"Cache hit for generate_text ({len(cached_result)} chars)")
            return cached_result
        
        # Apply rate limiting
        wait_time = self._rate_limiter.acquire()
        if wait_time > 0:
            time.sleep(wait_time)
        
        try:
            self._stats["api_calls"] += 1
            ai_provider = self.factory.get_provider(provider or 'gemini')
            result = ai_provider.generate(prompt, max_tokens=max_tokens)
            
            # Cache result
            self._prompt_cache.set(cache_key, result)
            
            return result
        except AIProcessingError as e:
            self.logger.error(f"AI generation failed: {e}")
            return f"Error: {str(e)}"
    
    def summarize(self, text: str, **kwargs) -> str:
        """Generate summary of text with caching"""
        self._stats["total_requests"] += 1
        
        cache_key = self._get_cache_key(text, "summarize")
        
        cached_result = self._prompt_cache.get(cache_key)
        if cached_result:
            self._stats["cache_hits"] += 1
            self.logger.debug(f"Cache hit for summarize ({len(cached_result)} chars)")
            return cached_result
        
        wait_time = self._rate_limiter.acquire()
        if wait_time > 0:
            time.sleep(wait_time)
        
        try:
            self._stats["api_calls"] += 1
            provider = self.factory.get_best_provider()
            result = provider.summarize(text, **kwargs)
            
            self._prompt_cache.set(cache_key, result)
            return result
        except AIProcessingError as e:
            self.logger.error(f"Summarization failed: {e}")
            return f"Error: {str(e)}"
    
    def correct_text(self, text: str, **kwargs) -> str:
        """Correct grammar and spelling with caching"""
        self._stats["total_requests"] += 1
        
        cache_key = self._get_cache_key(text, "correct")
        
        cached_result = self._prompt_cache.get(cache_key)
        if cached_result:
            self._stats["cache_hits"] += 1
            return cached_result
        
        wait_time = self._rate_limiter.acquire()
        if wait_time > 0:
            time.sleep(wait_time)
        
        try:
            self._stats["api_calls"] += 1
            provider = self.factory.get_best_provider()
            result = provider.correct_text(text, **kwargs)
            
            self._prompt_cache.set(cache_key, result)
            return result
        except AIProcessingError as e:
            self.logger.error(f"Text correction failed: {e}")
            return text
    
    def classify_content(self, text: str, **kwargs) -> Dict[str, Any]:
        """Classify content into categories with caching"""
        self._stats["total_requests"] += 1
        
        # For classification, use a shorter text for cache key
        short_text = text[:200]
        cache_key = self._get_cache_key(short_text, "classify")
        
        cached_result = self._prompt_cache.get(cache_key)
        if cached_result:
            self._stats["cache_hits"] += 1
            import json
            return json.loads(cached_result)
        
        wait_time = self._rate_limiter.acquire()
        if wait_time > 0:
            time.sleep(wait_time)
        
        try:
            self._stats["api_calls"] += 1
            provider = self.factory.get_best_provider()
            result = provider.classify_content(text, **kwargs)
            
            import json
            self._prompt_cache.set(cache_key, json.dumps(result))
            return result
        except AIProcessingError as e:
            self.logger.error(f"Classification failed: {e}")
            return {"category": "otras_clases", "confidence": 0.0}
    
    def get_stats(self) -> Dict[str, Any]:
        """Get service statistics"""
        cache_stats = self._prompt_cache.stats()
        hit_rate = (self._stats["cache_hits"] / self._stats["total_requests"] * 100) if self._stats["total_requests"] > 0 else 0
        
        return {
            **self._stats,
            "cache_size": cache_stats["size"],
            "cache_max_size": cache_stats["max_size"],
            "cache_hit_rate": round(hit_rate, 2),
            "rate_limiter": {
                "tokens": self._rate_limiter.tokens,
                "capacity": self._rate_limiter.capacity
            }
        }
    
    def clear_cache(self) -> None:
        """Clear the prompt cache"""
        self._prompt_cache = LRUCache(max_size=100, ttl=3600)
        self.logger.info("AI service cache cleared")


# Global instance
ai_service = AIService()
