"""
Configuration Management for Connectors
Environment-based configuration with validation and defaults
"""

import os
import json
import yaml
from typing import Dict, Any, Optional, Union, List
from pathlib import Path
from dataclasses import dataclass, field
from .exceptions import ConfigurationError
from .logging_config import get_logger

logger = get_logger(__name__)


@dataclass
class ConnectionConfig:
    """Base configuration for connection settings"""
    
    # Basic connection settings
    url: str
    timeout: int = 30
    max_retries: int = 3
    verify_ssl: bool = True
    
    # Advanced settings
    retry_delay: float = 1.0
    retry_backoff_factor: float = 2.0
    connection_pool_size: int = 10
    max_pool_connections: int = 50
    
    # Rate limiting
    rate_limit_requests: Optional[int] = None
    rate_limit_period: Optional[int] = None
    
    # Caching
    enable_cache: bool = True
    cache_ttl: int = 300  # 5 minutes
    cache_max_size: int = 1000
    
    # Logging
    log_level: str = "INFO"
    log_requests: bool = False
    log_responses: bool = False
    
    def validate(self) -> None:
        """Validate configuration settings"""
        if not self.url:
            raise ConfigurationError("URL is required")
        
        if self.timeout <= 0:
            raise ConfigurationError("Timeout must be positive")
        
        if self.max_retries < 0:
            raise ConfigurationError("Max retries must be non-negative")
        
        if self.retry_delay < 0:
            raise ConfigurationError("Retry delay must be non-negative")
        
        if self.retry_backoff_factor < 1:
            raise ConfigurationError("Retry backoff factor must be >= 1")
        
        if self.connection_pool_size <= 0:
            raise ConfigurationError("Connection pool size must be positive")
        
        if self.cache_ttl < 0:
            raise ConfigurationError("Cache TTL must be non-negative")
        
        if self.cache_max_size <= 0:
            raise ConfigurationError("Cache max size must be positive")


@dataclass
class ConfluenceConfig(ConnectionConfig):
    """Confluence-specific configuration"""
    
    # Authentication
    token: Optional[str] = None
    username: Optional[str] = None
    password: Optional[str] = None
    cloud: bool = True
    
    # API settings
    api_version: str = "latest"
    expand_defaults: List[str] = field(default_factory=lambda: ["body.storage", "version", "space"])
    
    # Content settings
    max_content_size: int = 10 * 1024 * 1024  # 10MB
    supported_image_formats: List[str] = field(default_factory=lambda: ["png", "jpg", "jpeg", "gif", "bmp"])
    
    # OCR settings
    enable_ocr: bool = True
    tesseract_lang: str = "eng"
    ocr_confidence_threshold: float = 60.0
    
    def validate(self) -> None:
        """Validate Confluence-specific settings"""
        super().validate()
        
        # Check authentication
        if not self.token and not (self.username and self.password):
            raise ConfigurationError("Either token or username/password must be provided")
        
        if self.ocr_confidence_threshold < 0 or self.ocr_confidence_threshold > 100:
            raise ConfigurationError("OCR confidence threshold must be between 0 and 100")


@dataclass
class JiraConfig(ConnectionConfig):
    """Jira-specific configuration"""
    
    # Authentication
    token: Optional[str] = None
    username: Optional[str] = None
    password: Optional[str] = None
    api_token: Optional[str] = None
    
    # API settings
    api_version: str = "3"
    default_fields: List[str] = field(default_factory=lambda: ["summary", "status", "assignee"])
    
    # Issue settings
    max_search_results: int = 1000
    max_comments_per_issue: int = 100
    
    # Agile/Scrum settings
    enable_agile: bool = True
    default_board_type: str = "scrum"
    
    def validate(self) -> None:
        """Validate Jira-specific settings"""
        super().validate()
        
        # Check authentication
        auth_methods = [self.token, self.api_token, self.username and self.password]
        if not any(auth_methods):
            raise ConfigurationError("At least one authentication method must be provided")


class ConfigManager:
    """Centralized configuration management"""
    
    def __init__(self, config_file: Optional[str] = None):
        self.config_file = config_file
        self._config_cache: Dict[str, Any] = {}
        self._load_config()
    
    def _load_config(self) -> None:
        """Load configuration from file and environment"""
        # Load from file if specified
        if self.config_file:
            self._load_from_file()
        
        # Override with environment variables
        self._load_from_environment()
    
    def _load_from_file(self) -> None:
        """Load configuration from file"""
        config_path = Path(self.config_file)
        
        if not config_path.exists():
            logger.warning(f"Config file not found: {self.config_file}")
            return
        
        try:
            with open(config_path, 'r', encoding='utf-8') as f:
                if config_path.suffix.lower() in ['.yaml', '.yml']:
                    config_data = yaml.safe_load(f)
                elif config_path.suffix.lower() == '.json':
                    config_data = json.load(f)
                else:
                    raise ConfigurationError(f"Unsupported config file format: {config_path.suffix}")
            
            self._config_cache.update(config_data)
            logger.info(f"Loaded configuration from {self.config_file}")
            
        except Exception as e:
            raise ConfigurationError(f"Failed to load config file {self.config_file}: {e}")
    
    def _load_from_environment(self) -> None:
        """Load configuration from environment variables"""
        # Common settings
        self._set_from_env('log_level', 'CONNECTORS_LOG_LEVEL')
        self._set_from_env('timeout', 'CONNECTORS_TIMEOUT', int)
        self._set_from_env('max_retries', 'CONNECTORS_MAX_RETRIES', int)
        self._set_from_env('verify_ssl', 'CONNECTORS_VERIFY_SSL', bool)
        self._set_from_env('enable_cache', 'CONNECTORS_ENABLE_CACHE', bool)
        self._set_from_env('cache_ttl', 'CONNECTORS_CACHE_TTL', int)
        
        # Confluence settings
        self._set_from_env('confluence_url', 'CONFLUENCE_URL')
        self._set_from_env('confluence_token', 'CONFLUENCE_TOKEN')
        self._set_from_env('confluence_username', 'CONFLUENCE_USERNAME')
        self._set_from_env('confluence_password', 'CONFLUENCE_PASSWORD')
        self._set_from_env('confluence_cloud', 'CONFLUENCE_CLOUD', bool)
        
        # Jira settings
        self._set_from_env('jira_url', 'JIRA_URL')
        self._set_from_env('jira_token', 'JIRA_TOKEN')
        self._set_from_env('jira_username', 'JIRA_USERNAME')
        self._set_from_env('jira_password', 'JIRA_PASSWORD')
        self._set_from_env('jira_api_token', 'JIRA_API_TOKEN')
    
    def _set_from_env(
        self, 
        key: str, 
        env_var: str, 
        type_converter: type = str
    ) -> None:
        """Set configuration value from environment variable"""
        value = os.getenv(env_var)
        if value is not None:
            try:
                if type_converter == bool:
                    self._config_cache[key] = value.lower() in ('true', '1', 'yes', 'on')
                elif type_converter == int:
                    self._config_cache[key] = int(value)
                else:
                    self._config_cache[key] = value
            except ValueError as e:
                raise ConfigurationError(f"Invalid {env_var} value: {value}")
    
    def get_confluence_config(self) -> ConfluenceConfig:
        """Get Confluence configuration"""
        config_data = self._get_service_config('confluence')
        config = ConfluenceConfig(**config_data)
        config.validate()
        return config
    
    def get_jira_config(self) -> JiraConfig:
        """Get Jira configuration"""
        config_data = self._get_service_config('jira')
        config = JiraConfig(**config_data)
        config.validate()
        return config
    
    def _get_service_config(self, service: str) -> Dict[str, Any]:
        """Get configuration for a specific service"""
        # Start with common configuration
        config = {
            'url': self._config_cache.get(f'{service}_url', ''),
            'timeout': self._config_cache.get('timeout', 30),
            'max_retries': self._config_cache.get('max_retries', 3),
            'verify_ssl': self._config_cache.get('verify_ssl', True),
            'enable_cache': self._config_cache.get('enable_cache', True),
            'cache_ttl': self._config_cache.get('cache_ttl', 300),
            'log_level': self._config_cache.get('log_level', 'INFO'),
        }
        
        # Add service-specific configuration from file
        service_config = self._config_cache.get(service, {})
        config.update(service_config)
        
        # Add service-specific environment variables
        if service == 'confluence':
            config.update({
                'token': self._config_cache.get('confluence_token'),
                'username': self._config_cache.get('confluence_username'),
                'password': self._config_cache.get('confluence_password'),
                'cloud': self._config_cache.get('confluence_cloud', True),
            })
        elif service == 'jira':
            config.update({
                'token': self._config_cache.get('jira_token'),
                'username': self._config_cache.get('jira_username'),
                'password': self._config_cache.get('jira_password'),
                'api_token': self._config_cache.get('jira_api_token'),
            })
        
        return config
    
    def reload(self) -> None:
        """Reload configuration from file and environment"""
        self._config_cache.clear()
        self._load_config()
        logger.info("Configuration reloaded")


# Global configuration manager instance
_config_manager: Optional[ConfigManager] = None


def get_config_manager(config_file: Optional[str] = None) -> ConfigManager:
    """Get the global configuration manager instance"""
    global _config_manager
    if _config_manager is None:
        _config_manager = ConfigManager(config_file)
    return _config_manager


def load_config_from_file(file_path: str) -> Dict[str, Any]:
    """Load configuration from a specific file"""
    config_path = Path(file_path)
    
    if not config_path.exists():
        raise ConfigurationError(f"Config file not found: {file_path}")
    
    try:
        with open(config_path, 'r', encoding='utf-8') as f:
            if config_path.suffix.lower() in ['.yaml', '.yml']:
                return yaml.safe_load(f)
            elif config_path.suffix.lower() == '.json':
                return json.load(f)
            else:
                raise ConfigurationError(f"Unsupported config file format: {config_path.suffix}")
    except Exception as e:
        raise ConfigurationError(f"Failed to load config file {file_path}: {e}")


def create_sample_config(file_path: str, format_type: str = "yaml") -> None:
    """Create a sample configuration file"""
    sample_config = {
        'log_level': 'INFO',
        'timeout': 30,
        'max_retries': 3,
        'verify_ssl': True,
        'enable_cache': True,
        'cache_ttl': 300,
        
        'confluence': {
            'url': 'https://your-confluence.atlassian.net',
            'token': 'your-confluence-token',
            'cloud': True,
            'enable_ocr': True,
            'tesseract_lang': 'eng'
        },
        
        'jira': {
            'url': 'https://your-jira.atlassian.net',
            'token': 'your-jira-token',
            'api_version': '3',
            'enable_agile': True
        }
    }
    
    config_path = Path(file_path)
    config_path.parent.mkdir(parents=True, exist_ok=True)
    
    try:
        with open(config_path, 'w', encoding='utf-8') as f:
            if format_type.lower() == 'yaml':
                yaml.dump(sample_config, f, default_flow_style=False, indent=2)
            elif format_type.lower() == 'json':
                json.dump(sample_config, f, indent=2)
            else:
                raise ConfigurationError(f"Unsupported format: {format_type}")
        
        logger.info(f"Sample configuration created at {file_path}")
        
    except Exception as e:
        raise ConfigurationError(f"Failed to create sample config: {e}")
