"""
Prometheus Metrics for The Iron Vault

Exposes operational metrics for monitoring filesystem operations,
request latencies, and error rates.

Metrics Exposed:
- fsal_requests_total: Counter of HTTP requests by endpoint/method/status
- fsal_request_duration_seconds: Histogram of request latencies
- fsal_operations_total: Counter of filesystem operations by type
- fsal_operation_errors_total: Counter of operation failures
- fsal_bytes_read_total: Counter of bytes read
- fsal_bytes_written_total: Counter of bytes written
- fsal_active_requests: Gauge of currently processing requests
"""

from __future__ import annotations

import time
from collections.abc import Callable
from functools import wraps

from prometheus_client import (
    CONTENT_TYPE_LATEST,
    REGISTRY,
    Counter,
    Gauge,
    Histogram,
    generate_latest,
)
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import Response

# ----------------------------- Metrics Definitions ---------------------------

# HTTP request metrics
REQUEST_COUNT = Counter(
    "fsal_requests_total",
    "Total HTTP requests",
    ["method", "endpoint", "status_code"],
)

REQUEST_LATENCY = Histogram(
    "fsal_request_duration_seconds",
    "HTTP request latency in seconds",
    ["method", "endpoint"],
    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
)

ACTIVE_REQUESTS = Gauge(
    "fsal_active_requests",
    "Number of requests currently being processed",
)

# Filesystem operation metrics
OPERATION_COUNT = Counter(
    "fsal_operations_total",
    "Total filesystem operations",
    ["operation"],  # read, write, list, delete, copy, move, mkdir, hash
)

OPERATION_ERRORS = Counter(
    "fsal_operation_errors_total",
    "Total filesystem operation errors",
    ["operation", "error_type"],
)

BYTES_READ = Counter(
    "fsal_bytes_read_total",
    "Total bytes read from filesystem",
)

BYTES_WRITTEN = Counter(
    "fsal_bytes_written_total",
    "Total bytes written to filesystem",
)

# Sandbox security metrics
SANDBOX_VIOLATIONS = Counter(
    "fsal_sandbox_violations_total",
    "Total sandbox violation attempts blocked",
    ["violation_type"],  # traversal, symlink, null_byte
)

# Rate limiting metrics
RATE_LIMIT_HITS = Counter(
    "fsal_rate_limit_hits_total",
    "Total requests rejected by rate limiter",
    ["endpoint"],
)


# ----------------------------- Middleware ------------------------------------


class MetricsMiddleware(BaseHTTPMiddleware):
    """Middleware to collect HTTP request metrics."""

    async def dispatch(self, request: Request, call_next: Callable) -> Response:
        # Skip metrics endpoint itself to avoid recursion
        if request.url.path == "/metrics":
            return await call_next(request)

        method = request.method
        # Normalize endpoint (remove path params for grouping)
        endpoint = self._normalize_endpoint(request.url.path)

        ACTIVE_REQUESTS.inc()
        start_time = time.perf_counter()

        try:
            response = await call_next(request)
            status_code = str(response.status_code)
        except Exception:
            status_code = "500"
            raise
        finally:
            duration = time.perf_counter() - start_time
            ACTIVE_REQUESTS.dec()

            REQUEST_COUNT.labels(
                method=method,
                endpoint=endpoint,
                status_code=status_code,
            ).inc()

            REQUEST_LATENCY.labels(
                method=method,
                endpoint=endpoint,
            ).observe(duration)

        return response

    def _normalize_endpoint(self, path: str) -> str:
        """Normalize path for metric labels (group similar endpoints)."""
        # Map API paths to normalized labels
        if path.startswith("/v1/fs/"):
            operation = path.split("/v1/fs/")[-1].split("/")[0]
            return f"/v1/fs/{operation}"
        return path


# ----------------------------- Helper Functions ------------------------------


def record_operation(operation: str) -> None:
    """Record a successful filesystem operation."""
    OPERATION_COUNT.labels(operation=operation).inc()


def record_operation_error(operation: str, error_type: str) -> None:
    """Record a filesystem operation error."""
    OPERATION_ERRORS.labels(operation=operation, error_type=error_type).inc()


def record_bytes_read(count: int) -> None:
    """Record bytes read from filesystem."""
    BYTES_READ.inc(count)


def record_bytes_written(count: int) -> None:
    """Record bytes written to filesystem."""
    BYTES_WRITTEN.inc(count)


def record_sandbox_violation(violation_type: str) -> None:
    """Record a blocked sandbox violation attempt."""
    SANDBOX_VIOLATIONS.labels(violation_type=violation_type).inc()


def record_rate_limit_hit(endpoint: str) -> None:
    """Record a rate-limited request."""
    RATE_LIMIT_HITS.labels(endpoint=endpoint).inc()


# ----------------------------- Metrics Endpoint ------------------------------


async def metrics_endpoint(request: Request) -> Response:
    """Prometheus metrics endpoint handler."""
    return Response(
        content=generate_latest(REGISTRY),
        media_type=CONTENT_TYPE_LATEST,
    )


# ----------------------------- Instrumentation Decorator ---------------------


def instrument_operation(operation: str):
    """
    Decorator to instrument filesystem operations with metrics.

    Usage:
        @instrument_operation("read")
        async def read_file(...):
            ...
    """

    def decorator(func: Callable) -> Callable:
        @wraps(func)
        async def wrapper(*args, **kwargs):
            try:
                result = await func(*args, **kwargs)
                record_operation(operation)
                return result
            except Exception as e:
                error_type = type(e).__name__
                record_operation_error(operation, error_type)
                raise

        return wrapper

    return decorator
