"""
Prometheus metrics middleware for the Pixel API.

Provides:
- HTTP request metrics (total requests, duration, status codes)
- Active task metrics
- Cache hit/miss metrics
- AI provider request metrics
- Custom business metrics
- System resource metrics
""" 
import time
import logging
import psutil
from typing import Callable
from fastapi import Request, Response
from prometheus_client import (
    Counter,
    Histogram,
    Gauge,
    generate_latest,
    CONTENT_TYPE_LATEST,
    REGISTRY
)

logger = logging.getLogger(__name__)

# --- HTTP Metrics ---

http_requests_total = Counter(
    'http_requests_total',
    'Total HTTP requests',
    ['method', 'endpoint', 'status']
)

http_request_duration_seconds = Histogram(
    'http_request_duration_seconds',
    'HTTP request duration in seconds',
    ['method', 'endpoint'],
    buckets=(0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1.0, 2.5, 5.0, 7.5, 10.0)
)

http_requests_in_progress = Gauge(
    'http_requests_in_progress',
    'Number of HTTP requests in progress',
    ['method', 'endpoint']
)

http_request_errors_total = Counter(
    'http_request_errors_total',
    'Total HTTP request errors',
    ['method', 'endpoint', 'error_type']
)

# --- Cache Metrics ---

cache_hits_total = Counter(
    'cache_hits_total',
    'Total cache hits',
    ['cache_type']
)

cache_misses_total = Counter(
    'cache_misses_total',
    'Total cache misses',
    ['cache_type']
)

cache_operations_duration_seconds = Histogram(
    'cache_operations_duration_seconds',
    'Cache operation duration in seconds',
    ['operation', 'cache_type'],
    buckets=(0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0)
)

# --- AI Provider Metrics ---

ai_provider_requests_total = Counter(
    'ai_provider_requests_total',
    'Total AI provider requests',
    ['provider', 'model', 'status']
)

ai_provider_request_duration_seconds = Histogram(
    'ai_provider_request_duration_seconds',
    'AI provider request duration in seconds',
    ['provider', 'model'],
    buckets=(0.5, 1.0, 2.0, 5.0, 10.0, 20.0, 30.0, 60.0, 120.0, 300.0)
)

ai_provider_errors_total = Counter(
    'ai_provider_errors_total',
    'Total AI provider errors',
    ['provider', 'model', 'error_type']
)

# --- Database Metrics ---

database_queries_total = Counter(
    'database_queries_total',
    'Total database queries',
    ['operation', 'table']
)

database_query_duration_seconds = Histogram(
    'database_query_duration_seconds',
    'Database query duration in seconds',
    ['operation', 'table'],
    buckets=(0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0)
)

database_connections_active = Gauge(
    'database_connections_active',
    'Number of active database connections'
)

database_connections_idle = Gauge(
    'database_connections_idle',
    'Number of idle database connections'
)

# --- System Resource Metrics ---

system_cpu_usage_percent = Gauge(
    'system_cpu_usage_percent',
    'System CPU usage percentage'
)

system_memory_usage_bytes = Gauge(
    'system_memory_usage_bytes',
    'System memory usage in bytes'
)

system_memory_available_bytes = Gauge(
    'system_memory_available_bytes',
    'System memory available in bytes'
)

system_disk_usage_bytes = Gauge(
    'system_disk_usage_bytes',
    'System disk usage in bytes',
    ['path']
)

system_disk_available_bytes = Gauge(
    'system_disk_available_bytes',
    'System disk available in bytes',
    ['path']
)

process_cpu_usage_percent = Gauge(
    'process_cpu_usage_percent',
    'Process CPU usage percentage'
)

process_memory_usage_bytes = Gauge(
    'process_memory_usage_bytes',
    'Process memory usage in bytes'
)

process_open_file_descriptors = Gauge(
    'process_open_file_descriptors',
    'Number of open file descriptors'
)

# --- Application Metrics ---

application_info = Gauge(
    'application_info',
    'Application information',
    ['version', 'environment']
)


# 性能 thresholds (in seconds)
SLOW_REQUEST_THRESHOLD = 1.0  # 日志 warning for requests > 1s
VERY_SLOW_REQUEST_THRESHOLD = 5.0  # 日志 error for requests > 5s


async def metrics_middleware(request: Request, call_next: Callable) -> Response:
    """ 中间件 to collect HTTP request metrics.
    
    Tracks:
    - Total requests by method, endpoint, and status
    - Request duration by method and endpoint
    - Requests in progress
    - Performance warnings for slow requests
    - Error tracking
    """
    # Skip metrics collection for the metrics endpoint itself
    if request.url.path == "/metrics":
        return await call_next(request)
    
    # 提取 endpoint pattern (remove IDs and dynamic parts)
    endpoint = _normalize_endpoint(request.url.path)
    method = request.method
    
    # Track requests in progress
    http_requests_in_progress.labels(method=method, endpoint=endpoint).inc()
    
    # 记录 start time
    start_time = time.time()
    
    try:
        # 进程 request
        response = await call_next(request)
        status = response.status_code
        
        # 记录 metrics
        duration = time.time() - start_time
        
        http_requests_total.labels(
            method=method,
            endpoint=endpoint,
            status=status
        ).inc()
        
        http_request_duration_seconds.labels(
            method=method,
            endpoint=endpoint
        ).observe(duration)
        
        # 性能 warning logging
        if duration > VERY_SLOW_REQUEST_THRESHOLD:
            logger.error(
                f"Very slow request detected: {method} {request.url.path}",
                extra={
                    'method': method,
                    'endpoint': endpoint,
                    'path': request.url.path,
                    'duration_seconds': round(duration, 3),
                    'status': status,
                    'threshold': 'very_slow',
                    'request_id': getattr(request.state, 'request_id', None)
                }
            )
        elif duration > SLOW_REQUEST_THRESHOLD:
            logger.warning(
                f"Slow request detected: {method} {request.url.path}",
                extra={
                    'method': method,
                    'endpoint': endpoint,
                    'path': request.url.path,
                    'duration_seconds': round(duration, 3),
                    'status': status,
                    'threshold': 'slow',
                    'request_id': getattr(request.state, 'request_id', None)
                }
            )
        
        return response
        
    except Exception as e:
        # 记录 error metrics
        error_type = type(e).__name__
        
        http_requests_total.labels(
            method=method,
            endpoint=endpoint,
            status=500
        ).inc()
        
        http_request_errors_total.labels(
            method=method,
            endpoint=endpoint,
            error_type=error_type
        ).inc()
        
        duration = time.time() - start_time
        http_request_duration_seconds.labels(
            method=method,
            endpoint=endpoint
        ).observe(duration)
        
        # 日志 error with duration
        logger.error(
            f"Request failed: {method} {request.url.path}",
            extra={
                'method': method,
                'endpoint': endpoint,
                'path': request.url.path,
                'duration_seconds': round(duration, 3),
                'error': str(e),
                'error_type': error_type,
                'request_id': getattr(request.state, 'request_id', None)
            },
            exc_info=True
        )
        
        raise
        
    finally:
        # Decrement in-progress counter
        http_requests_in_progress.labels(method=method, endpoint=endpoint).dec()


def _normalize_endpoint(path: str) -> str:
    """ 正常ize endpoint path by removing IDs and dynamic segments.
    
    Examples:
        /api/projects/123 -> /api/projects/{id}
        /api/tasks/abc-def-ghi -> /api/tasks/{id}
        /generations/image -> /generations/image
    
    Args:
        path: Request path
        
    Returns:
        Normalized endpoint path
    """
    parts = path.split('/')
    normalized_parts = []
    
    for part in parts:
        if not part:
            continue
        
        # Check if part looks like an ID (UUID, numeric, or long alphanumeric)
        if (
            len(part) > 8 and 
            ('-' in part or part.isdigit() or part.isalnum())
        ):
            normalized_parts.append('{id}')
        else:
            normalized_parts.append(part)
    
    return '/' + '/'.join(normalized_parts) if normalized_parts else '/'


def setup_metrics_middleware(app):
    """ 集合up metrics middleware for the FastAPI application.
    
    Args:
        app: FastAPI application instance
    """
    app.middleware("http")(metrics_middleware)
    logger.info("Metrics middleware registered")


# --- Metrics Helper Functions ---

def record_cache_hit(cache_type: str):
    """ 记录 a cache hit.""" 
    cache_hits_total.labels(cache_type=cache_type).inc()


def record_cache_miss(cache_type: str):
    """ 记录 a cache miss.""" 
    cache_misses_total.labels(cache_type=cache_type).inc()


def record_cache_operation(operation: str, cache_type: str, duration: float):
    """ 记录 cache operation duration.""" 
    cache_operations_duration_seconds.labels(
        operation=operation,
        cache_type=cache_type
    ).observe(duration)


def record_ai_provider_request(
    provider: str,
    model: str,
    status: str,
    duration: float,
    error_type: str = None
):
    """ 记录 AI provider request."""
    ai_provider_requests_total.labels(
        provider=provider,
        model=model,
        status=status
    ).inc()
    
    ai_provider_request_duration_seconds.labels(
        provider=provider,
        model=model
    ).observe(duration)
    
    if error_type:
        ai_provider_errors_total.labels(
            provider=provider,
            model=model,
            error_type=error_type
        ).inc()


def record_database_query(operation: str, table: str, duration: float):
    """ 记录 database query.""" 
    database_queries_total.labels(
        operation=operation,
        table=table
    ).inc()
    
    database_query_duration_seconds.labels(
        operation=operation,
        table=table
    ).observe(duration)


def set_application_info(version: str, environment: str):
    """ 集合 application information."""
    application_info.labels(
        version=version,
        environment=environment
    ).set(1)


def update_system_metrics():
    """ 更新 system resource metrics.
    
    Should be called periodically to collect current system state.
    """
    try:
        # CPU metrics
        cpu_percent = psutil.cpu_percent(interval=0.1)
        system_cpu_usage_percent.set(cpu_percent)
        
        # Memory metrics
        memory = psutil.virtual_memory()
        system_memory_usage_bytes.set(memory.used)
        system_memory_available_bytes.set(memory.available)
        
        # Disk metrics
        disk = psutil.disk_usage('/')
        system_disk_usage_bytes.labels(path='/').set(disk.used)
        system_disk_available_bytes.labels(path='/').set(disk.free)
        
        # 进程 metrics
        process = psutil.Process()
        process_cpu_usage_percent.set(process.cpu_percent(interval=0.1))
        process_memory_usage_bytes.set(process.memory_info().rss)
        
        # File descriptors (Unix-like systems only)
        try:
            process_open_file_descriptors.set(process.num_fds())
        except (AttributeError, NotImplementedError):
            # Not available on Windows
            pass
            
    except Exception as e:
        logger.error(f"Failed to update system metrics: {e}", exc_info=True)


def update_database_metrics():
    """ 更新 database connection pool metrics.
    
    Should be called periodically to collect current pool state.
    """
    try:
        from src.config.database import engine
        pool = engine.pool
        
        # 获取 pool statistics (only for QueuePool, not StaticPool)
        if hasattr(pool, 'checkedout'):
            database_connections_active.set(pool.checkedout())
            database_connections_idle.set(pool.size() - pool.checkedout())
        else:
            # StaticPool (SQLite) doesn't have these methods
            # 集合 to 0 to indicate not applicable
            database_connections_active.set(0)
            database_connections_idle.set(0)
        
    except Exception as e:
        logger.error(f"Failed to update database metrics: {e}", exc_info=True)


def get_metrics() -> bytes:
    """ 获取 current metrics in Prometheus format.
    
    Returns:
        Metrics data in Prometheus text format
    """ 
    return generate_latest(REGISTRY)


def get_metrics_content_type() -> str:
    """ 获取 the content type for Prometheus metrics.
    
    Returns:
        Content type string
    """ 
    return CONTENT_TYPE_LATEST
