#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Metrics and Observability
Provides structured logging, metrics collection, and performance tracking
"""

import time
import logging
import json
from typing import Dict, Any, Optional, List
from datetime import datetime
from collections import defaultdict
from threading import Lock


class MetricsCollector:
    """Collects and tracks metrics for automation scripts."""
    
    def __init__(self, service_name: str, job_id: str):
        """Initialize metrics collector.
        
        Args:
            service_name: Name of the service
            job_id: Job identifier
        """
        self.service_name = service_name
        self.job_id = job_id
        self.metrics: Dict[str, Any] = {
            'start_time': datetime.now().isoformat(),
            'operations': [],
            'errors': [],
            'performance': {},
            'counters': defaultdict(int)
        }
        self.lock = Lock()
        self.logger = logging.getLogger(f"{service_name}.metrics")
    
    def record_operation(self, operation_name: str, duration: float, success: bool, 
                        metadata: Optional[Dict[str, Any]] = None):
        """Record an operation.
        
        Args:
            operation_name: Name of the operation
            duration: Duration in seconds
            success: Whether operation was successful
            metadata: Optional metadata dictionary
        """
        with self.lock:
            operation = {
                'name': operation_name,
                'duration': duration,
                'success': success,
                'timestamp': datetime.now().isoformat()
            }
            if metadata:
                operation['metadata'] = metadata
            
            self.metrics['operations'].append(operation)
            
            if success:
                self.metrics['counters'][f'{operation_name}_success'] += 1
            else:
                self.metrics['counters'][f'{operation_name}_failed'] += 1
    
    def record_error(self, error_type: str, error_message: str, context: Optional[Dict[str, Any]] = None):
        """Record an error.
        
        Args:
            error_type: Type of error
            error_message: Error message
            context: Optional context dictionary
        """
        with self.lock:
            error = {
                'type': error_type,
                'message': error_message,
                'timestamp': datetime.now().isoformat()
            }
            if context:
                error['context'] = context
            
            self.metrics['errors'].append(error)
            self.metrics['counters']['errors_total'] += 1
            self.metrics['counters'][f'errors_{error_type}'] += 1
    
    def update_counter(self, counter_name: str, increment: int = 1):
        """Update a counter.
        
        Args:
            counter_name: Name of the counter
            increment: Increment value
        """
        with self.lock:
            self.metrics['counters'][counter_name] += increment
    
    def set_performance_metric(self, metric_name: str, value: Any):
        """Set a performance metric.
        
        Args:
            metric_name: Name of the metric
            value: Metric value
        """
        with self.lock:
            self.metrics['performance'][metric_name] = value
    
    def get_summary(self) -> Dict[str, Any]:
        """Get metrics summary.
        
        Returns:
            Dictionary with metrics summary
        """
        with self.lock:
            end_time = datetime.now()
            start_time = datetime.fromisoformat(self.metrics['start_time'])
            duration = (end_time - start_time).total_seconds()
            
            # Calculate success rate
            total_operations = len(self.metrics['operations'])
            successful_operations = sum(1 for op in self.metrics['operations'] if op['success'])
            success_rate = (successful_operations / total_operations * 100) if total_operations > 0 else 0
            
            # Calculate average durations
            durations_by_operation = defaultdict(list)
            for op in self.metrics['operations']:
                durations_by_operation[op['name']].append(op['duration'])
            
            avg_durations = {
                name: sum(durations) / len(durations)
                for name, durations in durations_by_operation.items()
            }
            
            return {
                'service_name': self.service_name,
                'job_id': self.job_id,
                'duration': duration,
                'total_operations': total_operations,
                'successful_operations': successful_operations,
                'failed_operations': total_operations - successful_operations,
                'success_rate': success_rate,
                'total_errors': len(self.metrics['errors']),
                'counters': dict(self.metrics['counters']),
                'average_durations': avg_durations,
                'performance_metrics': self.metrics['performance']
            }
    
    def export_json(self) -> str:
        """Export metrics as JSON string.
        
        Returns:
            JSON string
        """
        with self.lock:
            summary = self.get_summary()
            return json.dumps(summary, indent=2)
    
    def log_summary(self):
        """Log metrics summary."""
        summary = self.get_summary()
        self.logger.info("=" * 80)
        self.logger.info("METRICS SUMMARY")
        self.logger.info("=" * 80)
        self.logger.info(f"Service: {summary['service_name']}")
        self.logger.info(f"Job ID: {summary['job_id']}")
        self.logger.info(f"Duration: {summary['duration']:.2f} seconds")
        self.logger.info(f"Total Operations: {summary['total_operations']}")
        self.logger.info(f"Successful: {summary['successful_operations']}")
        self.logger.info(f"Failed: {summary['failed_operations']}")
        self.logger.info(f"Success Rate: {summary['success_rate']:.2f}%")
        self.logger.info(f"Total Errors: {summary['total_errors']}")
        self.logger.info("=" * 80)


class StructuredLogger:
    """Provides structured logging with context."""
    
    def __init__(self, logger: logging.Logger, context: Optional[Dict[str, Any]] = None):
        """Initialize structured logger.
        
        Args:
            logger: Python logger instance
            context: Optional context dictionary
        """
        self.logger = logger
        self.context = context or {}
    
    def _add_context(self, message: str, extra: Optional[Dict[str, Any]] = None) -> str:
        """Add context to message.
        
        Args:
            message: Log message
            extra: Optional extra context
            
        Returns:
            Message with context
        """
        context_str = json.dumps({**self.context, **(extra or {})})
        return f"{message} | Context: {context_str}"
    
    def info(self, message: str, **kwargs):
        """Log info message."""
        self.logger.info(self._add_context(message, kwargs))
    
    def warning(self, message: str, **kwargs):
        """Log warning message."""
        self.logger.warning(self._add_context(message, kwargs))
    
    def error(self, message: str, **kwargs):
        """Log error message."""
        self.logger.error(self._add_context(message, kwargs))
    
    def debug(self, message: str, **kwargs):
        """Log debug message."""
        self.logger.debug(self._add_context(message, kwargs))


def track_execution_time(func):
    """Decorator to track function execution time.
    
    Returns:
        Decorated function
    """
    def wrapper(self, *args, **kwargs):
        if hasattr(self, 'metrics'):
            start = time.time()
            try:
                result = func(self, *args, **kwargs)
                duration = time.time() - start
                self.metrics.record_operation(
                    func.__name__,
                    duration,
                    True,
                    {'args_count': len(args), 'kwargs_count': len(kwargs)}
                )
                return result
            except Exception as e:
                duration = time.time() - start
                self.metrics.record_operation(
                    func.__name__,
                    duration,
                    False,
                    {'error': str(e)}
                )
                self.metrics.record_error(type(e).__name__, str(e))
                raise
        else:
            return func(self, *args, **kwargs)
    return wrapper

