"""Google Gemini provider implementation.

Google's Gemini models offer a free tier and excellent quality/price ratio.
Models are loaded dynamically from the API - no hardcoded model names.
"""
from typing import Optional, List
import json
import urllib.request
import urllib.error
from .base import AIProvider, AIProviderError


class GeminiProvider(AIProvider):
    """Google Gemini provider using the Generative Language API."""
    
    NAME = "gemini"
    REQUIRES_API_KEY = True
    API_BASE = "https://generativelanguage.googleapis.com/v1beta/models"
    
    # Patterns to exclude from model list (experimental, preview, specialized)
    EXCLUDED_PATTERNS = [
        'preview', 'exp', 'experimental', 'computer-use', 
        'deep-research', 'image', 'embedding', 'aqa', 'tts',
        'learnlm', 'imagen', 'veo'
    ]
    
    # Deprecated models that may still appear in API but return 404
    DEPRECATED_MODELS = [
        'gemini-2.0-flash',
        'gemini-1.0-pro',
        'gemini-pro',
    ]
    
    def list_models(self) -> List[str]:
        """List available Gemini models that support generateContent.
        
        Filters to only stable text generation models, excluding:
        - Preview/experimental models
        - Image generation models
        - Embedding models
        - Specialized models (computer-use, deep-research, etc.)
        
        Returns:
            List of model IDs available for text generation.
        """
        if not self.api_key:
            return []
        
        try:
            url = f"{self.API_BASE}?key={self.api_key}"
            req = urllib.request.Request(url, method='GET', headers={'User-Agent': 'ProxMenux/1.0'})
            
            with urllib.request.urlopen(req, timeout=10) as resp:
                data = json.loads(resp.read().decode('utf-8'))
            
            models = []
            for model in data.get('models', []):
                model_name = model.get('name', '')
                # Extract just the model ID (e.g., "models/gemini-pro" -> "gemini-pro")
                if model_name.startswith('models/'):
                    model_id = model_name[7:]
                else:
                    model_id = model_name
                
                # Only include models that support generateContent
                supported_methods = model.get('supportedGenerationMethods', [])
                if 'generateContent' not in supported_methods:
                    continue
                
                # Exclude experimental, preview, and specialized models
                model_lower = model_id.lower()
                if any(pattern in model_lower for pattern in self.EXCLUDED_PATTERNS):
                    continue
                
                # Exclude deprecated models that return 404
                if model_id in self.DEPRECATED_MODELS:
                    continue
                
                models.append(model_id)
            
            # Sort with recommended models first (flash-lite, flash, pro)
            def sort_key(m):
                m_lower = m.lower()
                if 'flash-lite' in m_lower:
                    return (0, m)  # Best for notifications (fast, cheap)
                if 'flash' in m_lower:
                    return (1, m)
                if 'pro' in m_lower:
                    return (2, m)
                return (3, m)
            
            return sorted(models, key=sort_key)
        except Exception as e:
            print(f"[GeminiProvider] Failed to list models: {e}")
            return []
    
    def generate(self, system_prompt: str, user_message: str,
                 max_tokens: int = 200) -> Optional[str]:
        """Generate a response using Google's Gemini API.
        
        Note: Gemini uses a different API format. System instructions
        go in a separate systemInstruction field.
        
        Args:
            system_prompt: System instructions
            user_message: User message to process
            max_tokens: Maximum response length
            
        Returns:
            Generated text or None if failed
            
        Raises:
            AIProviderError: If API key is missing or request fails
        """
        if not self.api_key:
            raise AIProviderError("API key required for Gemini")
        
        url = f"{self.API_BASE}/{self.model}:generateContent?key={self.api_key}"
        
        # Gemini uses a specific format with contents array
        payload = {
            'systemInstruction': {
                'parts': [{'text': system_prompt}]
            },
            'contents': [
                {
                    'role': 'user',
                    'parts': [{'text': user_message}]
                }
            ],
            'generationConfig': {
                'maxOutputTokens': max_tokens,
                'temperature': 0.3,
            }
        }
        
        headers = {
            'Content-Type': 'application/json',
        }
        
        result = self._make_request(url, payload, headers)
        
        try:
            # Gemini returns candidates array with content parts
            candidates = result.get('candidates', [])
            if not candidates:
                # Check for blocked content or other issues
                prompt_feedback = result.get('promptFeedback', {})
                block_reason = prompt_feedback.get('blockReason', '')
                if block_reason:
                    raise AIProviderError(f"Content blocked by Gemini: {block_reason}")
                raise AIProviderError("No candidates in response - model may be overloaded")
            
            # Check if response was blocked
            finish_reason = candidates[0].get('finishReason', '')
            if finish_reason == 'SAFETY':
                safety_ratings = candidates[0].get('safetyRatings', [])
                blocked_categories = [r.get('category', 'UNKNOWN') for r in safety_ratings 
                                     if r.get('blocked', False)]
                raise AIProviderError(f"Response blocked by safety filter: {blocked_categories}")
            
            content = candidates[0].get('content', {})
            parts = content.get('parts', [])
            if parts:
                text = parts[0].get('text', '').strip()
                if text:
                    return text
            
            # No text content - check if it's a known issue
            if finish_reason == 'MAX_TOKENS':
                # MAX_TOKENS with no content could mean prompt too long OR model overload
                raise AIProviderError("No response generated (MAX_TOKENS). Model may be overloaded - try again.")
            elif finish_reason == 'STOP':
                # Normal stop but no content - unusual
                raise AIProviderError("Model returned empty response")
            else:
                raise AIProviderError(f"No response from model (reason: {finish_reason}). Try again later.")
        except AIProviderError:
            raise
        except (KeyError, IndexError) as e:
            raise AIProviderError(f"Unexpected response format: {e}")
