"""
Custom log formatters for MarkiTect.

Provides structured formatting for development and production environments
with context-aware logging capabilities.
"""

import json
import logging
import traceback
from datetime import datetime, timezone
from typing import Dict, Any, Optional

from .context import get_current_log_context


class BaseFormatter(logging.Formatter):
    """Base formatter with common functionality."""

    def __init__(self, enable_context: bool = True, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.enable_context = enable_context

    def format(self, record: logging.LogRecord) -> str:
        """Format the log record with context information."""
        # Add context information if enabled
        if self.enable_context:
            self._add_context_to_record(record)

        # Add standard fields
        self._add_standard_fields(record)

        return super().format(record)

    def _add_context_to_record(self, record: logging.LogRecord) -> None:
        """Add context information to log record."""
        context = get_current_log_context()
        if context:
            record.correlation_id = context.correlation_id
            record.operation_id = context.operation_id
            record.operation_type = context.operation_type
            record.user_id = context.user_id
            record.request_id = context.request_id

            # Add custom fields
            for key, value in context.custom_fields.items():
                setattr(record, f'ctx_{key}', value)
        else:
            record.correlation_id = None
            record.operation_id = None
            record.operation_type = None
            record.user_id = None
            record.request_id = None

    def _add_standard_fields(self, record: logging.LogRecord) -> None:
        """Add standard fields to log record."""
        record.timestamp = datetime.now(timezone.utc).isoformat() + 'Z'
        record.logger_name = record.name
        record.level_name = record.levelname
        record.thread_name = record.threadName
        record.process_id = record.process

        # Add exception information if present
        if record.exc_info and record.exc_info != (None, None, None):
            if isinstance(record.exc_info, tuple) and len(record.exc_info) == 3:
                record.exception_type = record.exc_info[0].__name__ if record.exc_info[0] else None
                record.exception_message = str(record.exc_info[1]) if record.exc_info[1] else None
                record.stack_trace = traceback.format_exception(*record.exc_info)
            else:
                # Handle case where exc_info is True but we need to get current exception
                import sys
                exc_info = sys.exc_info()
                if exc_info[0] is not None:
                    record.exception_type = exc_info[0].__name__
                    record.exception_message = str(exc_info[1])
                    record.stack_trace = traceback.format_exception(*exc_info)
                else:
                    record.exception_type = None
                    record.exception_message = None
                    record.stack_trace = None
        else:
            record.exception_type = None
            record.exception_message = None
            record.stack_trace = None


class DevelopmentFormatter(BaseFormatter):
    """
    Human-readable formatter for development environment.

    Provides colored output and structured information for easy debugging.
    """

    # Color codes for different log levels
    COLORS = {
        'DEBUG': '\033[36m',     # Cyan
        'INFO': '\033[32m',      # Green
        'WARNING': '\033[33m',   # Yellow
        'ERROR': '\033[31m',     # Red
        'CRITICAL': '\033[41m',  # Red background
        'RESET': '\033[0m'       # Reset
    }

    def __init__(self, enable_context: bool = True, enable_colors: bool = True, *args, **kwargs):
        super().__init__(enable_context, *args, **kwargs)
        self.enable_colors = enable_colors and self._supports_color()

    def format(self, record: logging.LogRecord) -> str:
        """Format record for development environment."""
        super().format(record)

        # Build the message
        parts = []

        # Timestamp
        timestamp = datetime.fromtimestamp(record.created).strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
        parts.append(f"[{timestamp}]")

        # Log level with color
        level = record.levelname
        if self.enable_colors and level in self.COLORS:
            level = f"{self.COLORS[level]}{level:<8}{self.COLORS['RESET']}"
        else:
            level = f"{level:<8}"
        parts.append(level)

        # Logger name (shortened)
        logger_name = self._shorten_logger_name(record.name)
        parts.append(f"[{logger_name}]")

        # Context information
        if self.enable_context and hasattr(record, 'correlation_id') and record.correlation_id:
            context_parts = []
            if record.correlation_id:
                context_parts.append(f"cid:{record.correlation_id[:8]}")
            if record.operation_id:
                context_parts.append(f"op:{record.operation_id}")
            if context_parts:
                parts.append(f"({' '.join(context_parts)})")

        # Main message
        parts.append(record.getMessage())

        # Exception information
        if record.exc_info:
            parts.append(f"\n{self.formatException(record.exc_info)}")

        # Performance information
        if hasattr(record, 'duration_ms'):
            parts.append(f"[{record.duration_ms:.2f}ms]")

        return " ".join(parts)

    def _shorten_logger_name(self, name: str) -> str:
        """Shorten logger name for compact display."""
        parts = name.split('.')
        if len(parts) <= 2:
            return name

        # Keep first and last part, abbreviate middle parts
        first = parts[0]
        last = parts[-1]
        middle = '.'.join(part[0] for part in parts[1:-1])

        return f"{first}.{middle}.{last}" if middle else f"{first}.{last}"

    def _supports_color(self) -> bool:
        """Check if terminal supports color output."""
        import os
        import sys

        # Check if we're in a terminal
        if not hasattr(sys.stdout, 'isatty') or not sys.stdout.isatty():
            return False

        # Check environment variables
        if os.getenv('NO_COLOR'):
            return False

        if os.getenv('FORCE_COLOR'):
            return True

        # Check terminal type
        term = os.getenv('TERM', '')
        return 'color' in term or term in ('xterm', 'xterm-256color', 'screen')


class ProductionFormatter(BaseFormatter):
    """
    Structured formatter for production environment.

    Provides JSON-like structured output optimized for log aggregation systems.
    """

    def format(self, record: logging.LogRecord) -> str:
        """Format record for production environment."""
        super().format(record)

        # Build structured log entry
        log_entry = {
            'timestamp': record.timestamp,
            'level': record.levelname,
            'logger': record.name,
            'message': record.getMessage(),
            'thread': record.thread_name,
            'process': record.process_id
        }

        # Add context information
        if self.enable_context:
            context_info = {}
            if hasattr(record, 'correlation_id') and record.correlation_id:
                context_info['correlation_id'] = record.correlation_id
            if hasattr(record, 'operation_id') and record.operation_id:
                context_info['operation_id'] = record.operation_id
            if hasattr(record, 'operation_type') and record.operation_type:
                context_info['operation_type'] = record.operation_type
            if hasattr(record, 'user_id') and record.user_id:
                context_info['user_id'] = record.user_id
            if hasattr(record, 'request_id') and record.request_id:
                context_info['request_id'] = record.request_id

            if context_info:
                log_entry['context'] = context_info

            # Add custom context fields
            custom_fields = {}
            for attr_name in dir(record):
                if attr_name.startswith('ctx_'):
                    field_name = attr_name[4:]  # Remove 'ctx_' prefix
                    custom_fields[field_name] = getattr(record, attr_name)

            if custom_fields:
                log_entry['custom'] = custom_fields

        # Add exception information
        if record.exc_info:
            log_entry['exception'] = {
                'type': record.exception_type,
                'message': record.exception_message,
                'traceback': record.stack_trace
            }

        # Add performance information
        if hasattr(record, 'duration_ms'):
            log_entry['performance'] = {
                'duration_ms': record.duration_ms
            }

        # Add location information
        log_entry['source'] = {
            'file': record.pathname,
            'line': record.lineno,
            'function': record.funcName
        }

        return self._format_structured_entry(log_entry)

    def _format_structured_entry(self, entry: Dict[str, Any]) -> str:
        """Format structured entry as string."""
        # Use compact JSON format
        return json.dumps(entry, separators=(',', ':'), ensure_ascii=False, default=str)


class PerformanceFormatter(BaseFormatter):
    """
    Specialized formatter for performance logging.

    Optimized for capturing timing, metrics, and performance data.
    """

    def format(self, record: logging.LogRecord) -> str:
        """Format record for performance logging."""
        super().format(record)

        # Performance-focused format
        parts = [
            record.timestamp,
            record.levelname,
            record.name
        ]

        # Context for performance tracking
        if self.enable_context and hasattr(record, 'operation_id') and record.operation_id:
            parts.append(f"op:{record.operation_id}")

        # Main message
        parts.append(record.getMessage())

        # Performance metrics
        metrics = []
        if hasattr(record, 'duration_ms'):
            metrics.append(f"duration:{record.duration_ms:.2f}ms")
        if hasattr(record, 'memory_mb'):
            metrics.append(f"memory:{record.memory_mb:.2f}MB")
        if hasattr(record, 'cpu_percent'):
            metrics.append(f"cpu:{record.cpu_percent:.1f}%")

        if metrics:
            parts.append(f"[{', '.join(metrics)}]")

        return " | ".join(parts)