"""
Configuration management for LandPPT AI features
"""

import os
from typing import Optional, Dict, Any, ClassVar
from pydantic import Field
from pydantic_settings import BaseSettings
from dotenv import load_dotenv

# Load environment variables with error handling
try:
    load_dotenv()
except (PermissionError, FileNotFoundError) as e:
    # Silently continue if .env file is not accessible
    # This allows the application to work with system environment variables
    pass
except Exception as e:
    # Log other errors but continue
    import logging
    logging.getLogger(__name__).warning(f"Could not load .env file: {e}")

class AIConfig(BaseSettings):
    """AI configuration settings"""
    
    # OpenAI Configuration
    openai_api_key: Optional[str] = Field(default=None, env="OPENAI_API_KEY")
    openai_base_url: str = Field(default="https://api.openai.com/v1", env="OPENAI_BASE_URL")
    openai_model: str = Field(default="gpt-3.5-turbo", env="OPENAI_MODEL")
    
    # Anthropic Configuration
    anthropic_api_key: Optional[str] = Field(default=None, env="ANTHROPIC_API_KEY")
    anthropic_model: str = Field(default="claude-3-haiku-20240307", env="ANTHROPIC_MODEL")

    # Google Gemini Configuration
    google_api_key: Optional[str] = Field(default=None, env="GOOGLE_API_KEY")
    google_base_url: str = Field(default="https://generativelanguage.googleapis.com", env="GOOGLE_BASE_URL")
    google_model: str = Field(default="gemini-1.5-flash", env="GOOGLE_MODEL")
    
    # Azure OpenAI Configuration
    azure_openai_api_key: Optional[str] = Field(default=None, env="AZURE_OPENAI_API_KEY")
    azure_openai_endpoint: Optional[str] = Field(default=None, env="AZURE_OPENAI_ENDPOINT")
    azure_openai_api_version: str = Field(default="2024-02-15-preview", env="AZURE_OPENAI_API_VERSION")
    azure_openai_deployment_name: Optional[str] = Field(default=None, env="AZURE_OPENAI_DEPLOYMENT_NAME")
    
    # Ollama Configuration
    ollama_base_url: str = Field(default="http://localhost:11434", env="OLLAMA_BASE_URL")
    ollama_model: str = Field(default="llama2", env="OLLAMA_MODEL")
    
    # 302.AI Configuration
    ai_302ai_api_key: Optional[str] = Field(default=None, env="302AI_API_KEY", alias="302ai_api_key")
    ai_302ai_base_url: str = Field(default="https://api.302.ai/v1", env="302AI_BASE_URL", alias="302ai_base_url")
    ai_302ai_model: str = Field(default="gpt-4o", env="302AI_MODEL", alias="302ai_model")
    
    # Hugging Face Configuration
    huggingface_api_token: Optional[str] = Field(default=None, env="HUGGINGFACE_API_TOKEN")

    # Tavily API Configuration (for research functionality)
    tavily_api_key: Optional[str] = Field(default=None, env="TAVILY_API_KEY")
    tavily_max_results: int = Field(default=10, env="TAVILY_MAX_RESULTS")
    tavily_search_depth: str = Field(default="advanced", env="TAVILY_SEARCH_DEPTH")
    tavily_include_domains: Optional[str] = Field(default=None, env="TAVILY_INCLUDE_DOMAINS")
    tavily_exclude_domains: Optional[str] = Field(default=None, env="TAVILY_EXCLUDE_DOMAINS")

    # SearXNG Configuration (for research functionality)
    searxng_host: Optional[str] = Field(default=None, env="SEARXNG_HOST")
    searxng_max_results: int = Field(default=10, env="SEARXNG_MAX_RESULTS")
    searxng_language: str = Field(default="auto", env="SEARXNG_LANGUAGE")
    searxng_timeout: int = Field(default=30, env="SEARXNG_TIMEOUT")

    # Research Configuration
    research_provider: str = Field(default="tavily", env="RESEARCH_PROVIDER")  # tavily, searxng, both
    research_enable_content_extraction: bool = Field(default=True, env="RESEARCH_ENABLE_CONTENT_EXTRACTION")
    research_max_content_length: int = Field(default=5000, env="RESEARCH_MAX_CONTENT_LENGTH")
    research_extraction_timeout: int = Field(default=30, env="RESEARCH_EXTRACTION_TIMEOUT")

    # Apryse SDK Configuration (for PPTX export functionality)
    apryse_license_key: Optional[str] = Field(default=None, env="APRYSE_LICENSE_KEY")

    # Provider Selection
    default_ai_provider: str = Field(default="openai", env="DEFAULT_AI_PROVIDER")
    
    # Model Role Configuration
    default_model_provider: Optional[str] = Field(default=None, env="DEFAULT_MODEL_PROVIDER")
    default_model_name: Optional[str] = Field(default=None, env="DEFAULT_MODEL_NAME")
    outline_model_provider: Optional[str] = Field(default=None, env="OUTLINE_MODEL_PROVIDER")
    outline_model_name: Optional[str] = Field(default=None, env="OUTLINE_MODEL_NAME")
    creative_model_provider: Optional[str] = Field(default=None, env="CREATIVE_MODEL_PROVIDER")
    creative_model_name: Optional[str] = Field(default=None, env="CREATIVE_MODEL_NAME")
    image_prompt_model_provider: Optional[str] = Field(default=None, env="IMAGE_PROMPT_MODEL_PROVIDER")
    image_prompt_model_name: Optional[str] = Field(default=None, env="IMAGE_PROMPT_MODEL_NAME")
    slide_generation_model_provider: Optional[str] = Field(default=None, env="SLIDE_GENERATION_MODEL_PROVIDER")
    slide_generation_model_name: Optional[str] = Field(default=None, env="SLIDE_GENERATION_MODEL_NAME")
    editor_assistant_model_provider: Optional[str] = Field(default=None, env="EDITOR_ASSISTANT_MODEL_PROVIDER")
    editor_assistant_model_name: Optional[str] = Field(default=None, env="EDITOR_ASSISTANT_MODEL_NAME")
    template_generation_model_provider: Optional[str] = Field(default=None, env="TEMPLATE_GENERATION_MODEL_PROVIDER")
    template_generation_model_name: Optional[str] = Field(default=None, env="TEMPLATE_GENERATION_MODEL_NAME")
    speech_script_model_provider: Optional[str] = Field(default=None, env="SPEECH_SCRIPT_MODEL_PROVIDER")
    speech_script_model_name: Optional[str] = Field(default=None, env="SPEECH_SCRIPT_MODEL_NAME")

    # Generation Parameters
    max_tokens: int = Field(default=16384, env="MAX_TOKENS")
    temperature: float = Field(default=0.7, env="TEMPERATURE")
    top_p: float = Field(default=1.0, env="TOP_P")
    
    # Parallel Generation Configuration
    enable_parallel_generation: bool = Field(default=False, env="ENABLE_PARALLEL_GENERATION")
    parallel_slides_count: int = Field(default=3, env="PARALLEL_SLIDES_COUNT")
    
    # Feature Flags
    enable_network_mode: bool = Field(default=True, env="ENABLE_NETWORK_MODE")
    enable_local_models: bool = Field(default=False, env="ENABLE_LOCAL_MODELS")
    enable_streaming: bool = Field(default=True, env="ENABLE_STREAMING")
    
    # Logging
    log_level: str = Field(default="INFO", env="LOG_LEVEL")
    log_ai_requests: bool = Field(default=False, env="LOG_AI_REQUESTS")
    
    model_config = {
        "env_file": ".env",
        "case_sensitive": False,
        "extra": "ignore"
    }



    MODEL_ROLE_FIELDS: ClassVar[dict[str, tuple[str, str]]] = {
        "default": ("default_model_provider", "default_model_name"),
        "outline": ("outline_model_provider", "outline_model_name"),
        "creative": ("creative_model_provider", "creative_model_name"),
        "image_prompt": ("image_prompt_model_provider", "image_prompt_model_name"),
        "slide_generation": ("slide_generation_model_provider", "slide_generation_model_name"),
        "editor": ("editor_assistant_model_provider", "editor_assistant_model_name"),
        "template": ("template_generation_model_provider", "template_generation_model_name"),
        "speech_script": ("speech_script_model_provider", "speech_script_model_name"),
    }

    MODEL_ROLE_LABELS: ClassVar[dict[str, str]] = {
        "default": "默认模型",
        "outline": "大纲生成 / 要点增强模型",
        "creative": "创意指导模型",
        "image_prompt": "配图与提示词模型",
        "slide_generation": "幻灯片生成模型",
        "editor": "AI编辑助手模型",
        "template": "AI模板生成模型",
        "speech_script": "演讲稿生成模型",
    }



    @staticmethod
    def _normalize_optional_str(value: Optional[str]) -> Optional[str]:
        if value is None:
            return None
        if not isinstance(value, str):
            value = str(value)
        value = value.strip()
        return value or None

    def _normalize_provider(self, provider: Optional[str]) -> Optional[str]:
        normalized = self._normalize_optional_str(provider)
        return normalized.lower() if normalized else None

    def _get_default_model_for_provider(self, provider: Optional[str]) -> Optional[str]:
        provider_key = self._normalize_provider(provider)
        if provider_key == "openai":
            return self._normalize_optional_str(self.openai_model)
        if provider_key == "anthropic":
            return self._normalize_optional_str(self.anthropic_model)
        if provider_key in ("google", "gemini"):
            return self._normalize_optional_str(self.google_model)
        if provider_key == "302ai":
            return self._normalize_optional_str(self.ai_302ai_model)
        if provider_key == "ollama":
            return self._normalize_optional_str(self.ollama_model)
        if provider_key == "azure_openai":
            return self._normalize_optional_str(getattr(self, "azure_openai_deployment_name", None))
        return self._normalize_optional_str(self.openai_model)

    def get_model_config_for_role(self, role: str, provider_override: Optional[str] = None) -> Dict[str, Optional[str]]:
        role_key = (role or "default").lower()
        if role_key not in self.MODEL_ROLE_FIELDS:
            raise ValueError(f"Unknown model role: {role}")

        provider_field, model_field = self.MODEL_ROLE_FIELDS[role_key]
        configured_provider = self._normalize_provider(getattr(self, provider_field, None))
        configured_model = self._normalize_optional_str(getattr(self, model_field, None))
        override_provider = self._normalize_provider(provider_override)

        effective_provider = override_provider or configured_provider or self._normalize_provider(self.default_ai_provider) or "openai"

        if override_provider:
            if override_provider == configured_provider and configured_model:
                effective_model = configured_model
            else:
                effective_model = self._get_default_model_for_provider(override_provider)
        else:
            effective_model = configured_model or self._get_default_model_for_provider(effective_provider)

        return {
            "role": role_key,
            "provider": effective_provider,
            "model": effective_model
        }

    def get_all_model_roles(self) -> Dict[str, Dict[str, Optional[str]]]:
        roles = {}
        for role_key, (provider_field, model_field) in self.MODEL_ROLE_FIELDS.items():
            roles[role_key] = {
                "provider": self._normalize_optional_str(getattr(self, provider_field, None)),
                "model": self._normalize_optional_str(getattr(self, model_field, None)),
                "label": self.MODEL_ROLE_LABELS.get(role_key)
            }
        return roles


    def get_provider_config(self, provider: Optional[str] = None) -> Dict[str, Any]:
        """Get configuration for a specific AI provider"""
        provider = provider or self.default_ai_provider

        # Built-in providers
        configs = {
            "openai": {
                "api_key": self.openai_api_key,
                "base_url": self.openai_base_url,
                "model": self.openai_model,
                "max_tokens": self.max_tokens,
                "temperature": self.temperature,
                "top_p": self.top_p,
            },
            "anthropic": {
                "api_key": self.anthropic_api_key,
                "model": self.anthropic_model,
                "max_tokens": self.max_tokens,
                "temperature": self.temperature,
                "top_p": self.top_p,
            },
            "google": {
                "api_key": self.google_api_key,
                "base_url": self.google_base_url,
                "model": self.google_model,
                "max_tokens": self.max_tokens,
                "temperature": self.temperature,
                "top_p": self.top_p,
            },
            "gemini": {  # Alias for google
                "api_key": self.google_api_key,
                "base_url": self.google_base_url,
                "model": self.google_model,
                "max_tokens": self.max_tokens,
                "temperature": self.temperature,
                "top_p": self.top_p,
            },
            "azure_openai": {
                "api_key": self.azure_openai_api_key,
                "endpoint": self.azure_openai_endpoint,
                "api_version": self.azure_openai_api_version,
                "deployment_name": self.azure_openai_deployment_name,
                "max_tokens": self.max_tokens,
                "temperature": self.temperature,
                "top_p": self.top_p,
            },
            "ollama": {
                "base_url": self.ollama_base_url,
                "model": self.ollama_model,
                "max_tokens": self.max_tokens,
                "temperature": self.temperature,
                "top_p": self.top_p,
            },
            "302ai": {
                "api_key": self.ai_302ai_api_key,
                "base_url": self.ai_302ai_base_url,
                "model": self.ai_302ai_model,
                "max_tokens": self.max_tokens,
                "temperature": self.temperature,
                "top_p": self.top_p,
            }
        }

        return configs.get(provider, configs.get("openai", {}))
    
    def is_provider_available(self, provider: str) -> bool:
        """Check if a provider is properly configured"""
        config = self.get_provider_config(provider)

        # Built-in providers
        if provider == "openai":
            return bool(config.get("api_key"))
        elif provider == "anthropic":
            return bool(config.get("api_key"))
        elif provider == "google" or provider == "gemini":
            return bool(config.get("api_key"))
        elif provider == "azure_openai":
            return bool(config.get("api_key") and config.get("endpoint"))
        elif provider == "ollama":
            return self.enable_local_models
        elif provider == "302ai":
            return bool(config.get("api_key"))

        return False
    
    def get_available_providers(self) -> list[str]:
        """Get list of available AI providers"""
        providers = []

        # Add built-in providers
        for provider in ["openai", "anthropic", "google", "gemini", "azure_openai", "ollama", "302ai"]:
            if self.is_provider_available(provider):
                providers.append(provider)

        return providers

# Global configuration instance
ai_config = AIConfig()

def reload_ai_config():
    """Reload AI configuration from environment variables"""
    global ai_config
    # Force reload environment variables with error handling
    from dotenv import load_dotenv
    import os
    env_file = os.path.join(os.getcwd(), '.env')
    try:
        load_dotenv(env_file, override=True)
    except (PermissionError, FileNotFoundError) as e:
        # Silently continue if .env file is not accessible
        pass
    except Exception as e:
        import logging
        logging.getLogger(__name__).warning(f"Could not reload .env file: {e}")

    # Force update the existing instance with new values from environment
    ai_config.openai_model = os.environ.get('OPENAI_MODEL', ai_config.openai_model)
    ai_config.openai_base_url = os.environ.get('OPENAI_BASE_URL', ai_config.openai_base_url)
    ai_config.openai_api_key = os.environ.get('OPENAI_API_KEY', ai_config.openai_api_key)
    ai_config.anthropic_api_key = os.environ.get('ANTHROPIC_API_KEY', ai_config.anthropic_api_key)
    ai_config.anthropic_model = os.environ.get('ANTHROPIC_MODEL', ai_config.anthropic_model)
    ai_config.google_api_key = os.environ.get('GOOGLE_API_KEY', ai_config.google_api_key)
    ai_config.google_base_url = os.environ.get('GOOGLE_BASE_URL', ai_config.google_base_url)
    ai_config.google_model = os.environ.get('GOOGLE_MODEL', ai_config.google_model)
    ai_config.ai_302ai_api_key = os.environ.get('302AI_API_KEY', ai_config.ai_302ai_api_key)
    ai_config.ai_302ai_base_url = os.environ.get('302AI_BASE_URL', ai_config.ai_302ai_base_url)
    ai_config.ai_302ai_model = os.environ.get('302AI_MODEL', ai_config.ai_302ai_model)
    ai_config.default_ai_provider = os.environ.get('DEFAULT_AI_PROVIDER', ai_config.default_ai_provider)
    model_provider_env = os.environ.get('DEFAULT_MODEL_PROVIDER')
    ai_config.default_model_provider = (ai_config._normalize_optional_str(model_provider_env)
                                        if model_provider_env is not None else ai_config.default_model_provider)
    model_name_env = os.environ.get('DEFAULT_MODEL_NAME')
    ai_config.default_model_name = (ai_config._normalize_optional_str(model_name_env)
                                    if model_name_env is not None else ai_config.default_model_name)

    outline_provider_env = os.environ.get('OUTLINE_MODEL_PROVIDER')
    ai_config.outline_model_provider = (ai_config._normalize_optional_str(outline_provider_env)
                                        if outline_provider_env is not None else ai_config.outline_model_provider)
    outline_model_env = os.environ.get('OUTLINE_MODEL_NAME')
    ai_config.outline_model_name = (ai_config._normalize_optional_str(outline_model_env)
                                    if outline_model_env is not None else ai_config.outline_model_name)

    creative_provider_env = os.environ.get('CREATIVE_MODEL_PROVIDER')
    ai_config.creative_model_provider = (ai_config._normalize_optional_str(creative_provider_env)
                                         if creative_provider_env is not None else ai_config.creative_model_provider)
    creative_model_env = os.environ.get('CREATIVE_MODEL_NAME')
    ai_config.creative_model_name = (ai_config._normalize_optional_str(creative_model_env)
                                     if creative_model_env is not None else ai_config.creative_model_name)

    image_prompt_provider_env = os.environ.get('IMAGE_PROMPT_MODEL_PROVIDER')
    ai_config.image_prompt_model_provider = (ai_config._normalize_optional_str(image_prompt_provider_env)
                                             if image_prompt_provider_env is not None else ai_config.image_prompt_model_provider)
    image_prompt_model_env = os.environ.get('IMAGE_PROMPT_MODEL_NAME')
    ai_config.image_prompt_model_name = (ai_config._normalize_optional_str(image_prompt_model_env)
                                         if image_prompt_model_env is not None else ai_config.image_prompt_model_name)

    slide_provider_env = os.environ.get('SLIDE_GENERATION_MODEL_PROVIDER')
    ai_config.slide_generation_model_provider = (ai_config._normalize_optional_str(slide_provider_env)
                                                 if slide_provider_env is not None else ai_config.slide_generation_model_provider)
    slide_model_env = os.environ.get('SLIDE_GENERATION_MODEL_NAME')
    ai_config.slide_generation_model_name = (ai_config._normalize_optional_str(slide_model_env)
                                             if slide_model_env is not None else ai_config.slide_generation_model_name)

    editor_provider_env = os.environ.get('EDITOR_ASSISTANT_MODEL_PROVIDER')
    ai_config.editor_assistant_model_provider = (ai_config._normalize_optional_str(editor_provider_env)
                                                 if editor_provider_env is not None else ai_config.editor_assistant_model_provider)
    editor_model_env = os.environ.get('EDITOR_ASSISTANT_MODEL_NAME')
    ai_config.editor_assistant_model_name = (ai_config._normalize_optional_str(editor_model_env)
                                             if editor_model_env is not None else ai_config.editor_assistant_model_name)

    template_provider_env = os.environ.get('TEMPLATE_GENERATION_MODEL_PROVIDER')
    ai_config.template_generation_model_provider = (ai_config._normalize_optional_str(template_provider_env)
                                                   if template_provider_env is not None else ai_config.template_generation_model_provider)
    template_model_env = os.environ.get('TEMPLATE_GENERATION_MODEL_NAME')
    ai_config.template_generation_model_name = (ai_config._normalize_optional_str(template_model_env)
                                               if template_model_env is not None else ai_config.template_generation_model_name)

    speech_provider_env = os.environ.get('SPEECH_SCRIPT_MODEL_PROVIDER')
    ai_config.speech_script_model_provider = (ai_config._normalize_optional_str(speech_provider_env)
                                              if speech_provider_env is not None else ai_config.speech_script_model_provider)
    speech_model_env = os.environ.get('SPEECH_SCRIPT_MODEL_NAME')
    ai_config.speech_script_model_name = (ai_config._normalize_optional_str(speech_model_env)
                                         if speech_model_env is not None else ai_config.speech_script_model_name)

    ai_config.max_tokens = int(os.environ.get('MAX_TOKENS', str(ai_config.max_tokens)))
    ai_config.temperature = float(os.environ.get('TEMPERATURE', str(ai_config.temperature)))
    ai_config.top_p = float(os.environ.get('TOP_P', str(ai_config.top_p)))
    
    # Update parallel generation configuration
    ai_config.enable_parallel_generation = os.environ.get('ENABLE_PARALLEL_GENERATION', str(ai_config.enable_parallel_generation)).lower() == 'true'
    ai_config.parallel_slides_count = int(os.environ.get('PARALLEL_SLIDES_COUNT', str(ai_config.parallel_slides_count)))

    # Update Tavily configuration
    ai_config.tavily_api_key = os.environ.get('TAVILY_API_KEY', ai_config.tavily_api_key)
    ai_config.tavily_max_results = int(os.environ.get('TAVILY_MAX_RESULTS', str(ai_config.tavily_max_results)))
    ai_config.tavily_search_depth = os.environ.get('TAVILY_SEARCH_DEPTH', ai_config.tavily_search_depth)
    ai_config.tavily_include_domains = os.environ.get('TAVILY_INCLUDE_DOMAINS', ai_config.tavily_include_domains)
    ai_config.tavily_exclude_domains = os.environ.get('TAVILY_EXCLUDE_DOMAINS', ai_config.tavily_exclude_domains)

    # Update SearXNG configuration
    ai_config.searxng_host = os.environ.get('SEARXNG_HOST', ai_config.searxng_host)
    ai_config.searxng_max_results = int(os.environ.get('SEARXNG_MAX_RESULTS', str(ai_config.searxng_max_results)))
    ai_config.searxng_language = os.environ.get('SEARXNG_LANGUAGE', ai_config.searxng_language)
    ai_config.searxng_timeout = int(os.environ.get('SEARXNG_TIMEOUT', str(ai_config.searxng_timeout)))

    # Update Research configuration
    ai_config.research_provider = os.environ.get('RESEARCH_PROVIDER', ai_config.research_provider)
    ai_config.research_enable_content_extraction = os.environ.get('RESEARCH_ENABLE_CONTENT_EXTRACTION', str(ai_config.research_enable_content_extraction)).lower() == 'true'
    ai_config.research_max_content_length = int(os.environ.get('RESEARCH_MAX_CONTENT_LENGTH', str(ai_config.research_max_content_length)))
    ai_config.research_extraction_timeout = int(os.environ.get('RESEARCH_EXTRACTION_TIMEOUT', str(ai_config.research_extraction_timeout)))

    ai_config.apryse_license_key = os.environ.get('APRYSE_LICENSE_KEY', ai_config.apryse_license_key)

class AppConfig(BaseSettings):
    """Application configuration"""
    
    # Server Configuration
    host: str = Field(default="0.0.0.0", env="HOST")
    port: int = Field(default=8000, env="PORT")
    debug: bool = Field(default=True, env="DEBUG")
    reload: bool = Field(default=True, env="RELOAD")
    
    # Database Configuration (for future use)
    database_url: str = Field(default="sqlite:///./landppt.db", env="DATABASE_URL")
    
    # Security Configuration
    secret_key: str = Field(default="your-secret-key-here", env="SECRET_KEY")
    access_token_expire_minutes: int = Field(default=30, env="ACCESS_TOKEN_EXPIRE_MINUTES")
    
    # File Upload Configuration
    max_file_size: int = Field(default=10 * 1024 * 1024, env="MAX_FILE_SIZE")  # 10MB
    upload_dir: str = Field(default="uploads", env="UPLOAD_DIR")
    
    # Cache Configuration
    cache_ttl: int = Field(default=3600, env="CACHE_TTL")  # 1 hour
    
    model_config = {
        "case_sensitive": False,
        "extra": "ignore"
    }

# Global app configuration instance
app_config = AppConfig()
