"""
Centralized logging configuration for MarkiTect.

Provides environment-based configuration, structured logging setup,
and integration with the existing configuration system.
"""

import os
import logging
import logging.config
import logging.handlers
from typing import Dict, Any, Optional
from dataclasses import dataclass
from enum import Enum

from .formatters import DevelopmentFormatter, ProductionFormatter


class LogLevel(Enum):
    """Supported log levels."""
    DEBUG = "DEBUG"
    INFO = "INFO"
    WARNING = "WARNING"
    ERROR = "ERROR"
    CRITICAL = "CRITICAL"


class LogFormat(Enum):
    """Supported log formats."""
    DEVELOPMENT = "development"
    PRODUCTION = "production"
    JSON = "json"


@dataclass
class LoggingConfig:
    """Logging configuration settings."""
    level: LogLevel = LogLevel.INFO
    format_type: LogFormat = LogFormat.DEVELOPMENT
    enable_console: bool = True
    enable_file: bool = False
    file_path: Optional[str] = None
    max_file_size: int = 10 * 1024 * 1024  # 10MB
    backup_count: int = 5
    enable_context: bool = True
    enable_performance: bool = False

    # Component-specific levels
    component_levels: Dict[str, LogLevel] = None

    def __post_init__(self):
        if self.component_levels is None:
            self.component_levels = {}


def get_logging_config() -> LoggingConfig:
    """
    Get logging configuration from environment variables.

    Environment Variables:
        MARKITECT_LOG_LEVEL: Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
        MARKITECT_LOG_FORMAT: Log format (development, production, json)
        MARKITECT_LOG_CONSOLE: Enable console logging (true/false)
        MARKITECT_LOG_FILE: Enable file logging (true/false)
        MARKITECT_LOG_FILE_PATH: File path for file logging
        MARKITECT_LOG_FILE_SIZE: Maximum file size in bytes
        MARKITECT_LOG_BACKUP_COUNT: Number of backup files to keep
        MARKITECT_LOG_CONTEXT: Enable context logging (true/false)
        MARKITECT_LOG_PERFORMANCE: Enable performance logging (true/false)

        # Component-specific levels
        MARKITECT_LOG_LEVEL_INFRASTRUCTURE: Log level for infrastructure components
        MARKITECT_LOG_LEVEL_DOMAIN: Log level for domain components
        MARKITECT_LOG_LEVEL_APPLICATION: Log level for application components
    """
    config = LoggingConfig()

    # Main log level
    level_str = os.getenv('MARKITECT_LOG_LEVEL', config.level.value)
    try:
        config.level = LogLevel(level_str.upper())
    except ValueError:
        config.level = LogLevel.INFO

    # Log format
    format_str = os.getenv('MARKITECT_LOG_FORMAT', config.format_type.value)
    try:
        config.format_type = LogFormat(format_str.lower())
    except ValueError:
        config.format_type = LogFormat.DEVELOPMENT

    # Console and file logging
    config.enable_console = _parse_bool(os.getenv('MARKITECT_LOG_CONSOLE', 'true'))
    config.enable_file = _parse_bool(os.getenv('MARKITECT_LOG_FILE', 'false'))
    config.file_path = os.getenv('MARKITECT_LOG_FILE_PATH')

    # File rotation settings
    try:
        config.max_file_size = int(os.getenv('MARKITECT_LOG_FILE_SIZE', str(config.max_file_size)))
    except ValueError:
        pass

    try:
        config.backup_count = int(os.getenv('MARKITECT_LOG_BACKUP_COUNT', str(config.backup_count)))
    except ValueError:
        pass

    # Context and performance
    config.enable_context = _parse_bool(os.getenv('MARKITECT_LOG_CONTEXT', 'true'))
    config.enable_performance = _parse_bool(os.getenv('MARKITECT_LOG_PERFORMANCE', 'false'))

    # Component-specific levels
    component_prefixes = ['INFRASTRUCTURE', 'DOMAIN', 'APPLICATION']
    for prefix in component_prefixes:
        env_var = f'MARKITECT_LOG_LEVEL_{prefix}'
        level_str = os.getenv(env_var)
        if level_str:
            try:
                config.component_levels[prefix.lower()] = LogLevel(level_str.upper())
            except ValueError:
                pass

    return config


def setup_logging(config: Optional[LoggingConfig] = None) -> None:
    """
    Set up logging configuration for the entire application.

    Args:
        config: Optional logging configuration. If None, loads from environment.
    """
    if config is None:
        config = get_logging_config()

    # Create logging dictionary configuration
    log_config = _create_logging_dict_config(config)

    # Apply the configuration
    logging.config.dictConfig(log_config)

    # Set component-specific levels
    _configure_component_loggers(config)

    # Log the configuration setup
    logger = logging.getLogger('infrastructure.logging.config')
    logger.info(f"Logging configured with level={config.level.value}, format={config.format_type.value}")


def _create_logging_dict_config(config: LoggingConfig) -> Dict[str, Any]:
    """Create logging dictionary configuration."""
    log_config = {
        'version': 1,
        'disable_existing_loggers': False,
        'formatters': {},
        'handlers': {},
        'loggers': {},
        'root': {
            'level': config.level.value,
            'handlers': []
        }
    }

    # Configure formatters
    if config.format_type in (LogFormat.DEVELOPMENT, LogFormat.PRODUCTION):
        formatter_class = DevelopmentFormatter if config.format_type == LogFormat.DEVELOPMENT else ProductionFormatter
        log_config['formatters']['standard'] = {
            '()': f'{formatter_class.__module__}.{formatter_class.__name__}',
            'enable_context': config.enable_context
        }
    else:  # JSON format
        log_config['formatters']['standard'] = {
            'format': '%(message)s',
            'class': 'pythonjsonlogger.jsonlogger.JsonFormatter'
        }

    # Configure console handler
    if config.enable_console:
        log_config['handlers']['console'] = {
            'class': 'logging.StreamHandler',
            'level': config.level.value,
            'formatter': 'standard',
            'stream': 'ext://sys.stdout'
        }
        log_config['root']['handlers'].append('console')

    # Configure file handler
    if config.enable_file and config.file_path:
        log_config['handlers']['file'] = {
            'class': 'logging.handlers.RotatingFileHandler',
            'level': config.level.value,
            'formatter': 'standard',
            'filename': config.file_path,
            'maxBytes': config.max_file_size,
            'backupCount': config.backup_count,
            'encoding': 'utf-8'
        }
        log_config['root']['handlers'].append('file')

    return log_config


def _configure_component_loggers(config: LoggingConfig) -> None:
    """Configure component-specific logger levels."""
    component_mappings = {
        'infrastructure': [
            'infrastructure',
            'infrastructure.repositories',
            'infrastructure.connection_manager',
            'infrastructure.config',
            'infrastructure.logging'
        ],
        'domain': [
            'domain',
            'domain.issues',
            'domain.projects',
            'domain.services'
        ],
        'application': [
            'application',
            'tddai',
            'markitect'
        ]
    }

    for component, level in config.component_levels.items():
        logger_names = component_mappings.get(component, [])
        for logger_name in logger_names:
            logger = logging.getLogger(logger_name)
            logger.setLevel(level.value)


def _parse_bool(value: str) -> bool:
    """Parse boolean value from string."""
    return value.lower() in ('true', '1', 'yes', 'on', 'enabled')


def validate_logging_config(config: LoggingConfig) -> tuple[bool, list[str]]:
    """
    Validate logging configuration.

    Returns:
        Tuple of (is_valid, error_messages)
    """
    errors = []

    # Validate file path if file logging is enabled
    if config.enable_file:
        if not config.file_path:
            errors.append("File logging enabled but no file path specified")
        else:
            # Check if directory exists and is writable
            import os
            from pathlib import Path

            file_path = Path(config.file_path)
            parent_dir = file_path.parent

            if not parent_dir.exists():
                try:
                    parent_dir.mkdir(parents=True, exist_ok=True)
                except OSError as e:
                    errors.append(f"Cannot create log directory {parent_dir}: {e}")

            if parent_dir.exists() and not os.access(parent_dir, os.W_OK):
                errors.append(f"Log directory {parent_dir} is not writable")

    # Validate file size and backup count
    if config.max_file_size <= 0:
        errors.append("Maximum file size must be positive")

    if config.backup_count < 0:
        errors.append("Backup count must be non-negative")

    # Validate at least one output is enabled
    if not config.enable_console and not config.enable_file:
        errors.append("At least one output (console or file) must be enabled")

    return len(errors) == 0, errors


# Default configurations for different environments
DEFAULT_DEVELOPMENT_CONFIG = LoggingConfig(
    level=LogLevel.DEBUG,
    format_type=LogFormat.DEVELOPMENT,
    enable_console=True,
    enable_file=False,
    enable_context=True,
    enable_performance=True
)

DEFAULT_PRODUCTION_CONFIG = LoggingConfig(
    level=LogLevel.INFO,
    format_type=LogFormat.PRODUCTION,
    enable_console=True,
    enable_file=True,
    file_path='logs/markitect.log',
    enable_context=True,
    enable_performance=False
)

DEFAULT_TESTING_CONFIG = LoggingConfig(
    level=LogLevel.WARNING,
    format_type=LogFormat.DEVELOPMENT,
    enable_console=False,
    enable_file=False,
    enable_context=False,
    enable_performance=False
)