"""
Prometheus metrics middleware for FastAPI.

Exposes /metrics endpoint in Prometheus text format.
Tracks HTTP requests, translations, and file uploads.
"""

import time
import logging
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import Response
from prometheus_client import Counter, Histogram, generate_latest, CONTENT_TYPE_LATEST

logger = logging.getLogger(__name__)

# ---- Metrics definitions ----

http_requests_total = Counter(
    "http_requests_total",
    "Total HTTP requests",
    ["method", "path", "status"],
)

translation_total = Counter(
    "translation_total",
    "Total translations processed",
    ["provider", "file_type", "status"],
)

translation_duration_seconds = Histogram(
    "translation_duration_seconds",
    "Translation processing duration in seconds",
    ["provider", "file_type"],
    buckets=(0.5, 1, 2, 5, 10, 30, 60, 120, 300),
)

file_size_bytes = Histogram(
    "file_size_bytes",
    "Uploaded file size in bytes",
    ["file_type"],
    buckets=(100_000, 500_000, 1_000_000, 5_000_000, 10_000_000, 25_000_000, 50_000_000),
)

# ---- Quality pipeline metrics (L0 + L1) ----

quality_l0_checks_total = Counter(
    "quality_l0_checks_total",
    "Total L0 (script+length+pattern) quality checks",
    ["result", "file_type"],  # result: pass | fail | error
)

quality_l1_judge_total = Counter(
    "quality_l1_judge_total",
    "Total L1 (LLM judge) verdicts",
    ["verdict", "model"],  # verdict: pass | fail | skip | error
)

quality_l1_judge_duration_seconds = Histogram(
    "quality_l1_judge_duration_seconds",
    "L1 LLM judge call duration in seconds",
    ["model"],
    buckets=(0.1, 0.25, 0.5, 1, 2, 5, 10, 30),
)

quality_l1_judge_cost_usd = Histogram(
    "quality_l1_judge_cost_usd",
    "L1 LLM judge estimated cost in USD",
    ["model"],
    buckets=(0.0001, 0.0005, 0.001, 0.005, 0.01, 0.05, 0.1),
)

# ---- L2 Pro premium judge (Track A4) ----

quality_l2_judge_total = Counter(
    "quality_l2_judge_total",
    "Total L2 (Pro premium judge, 8-dim) verdicts",
    ["verdict", "model", "tier"],  # verdict: pass | fail | skip | error
)

quality_l2_judge_duration_seconds = Histogram(
    "quality_l2_judge_duration_seconds",
    "L2 Pro judge call duration in seconds",
    ["model"],
    buckets=(0.5, 1, 2, 5, 10, 20, 30, 60),
)

quality_l2_judge_cost_usd = Histogram(
    "quality_l2_judge_cost_usd",
    "L2 Pro judge estimated cost in USD",
    ["model"],
    buckets=(0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0),
)

# ---- Retry metrics ----

translation_retry_total = Counter(
    "translation_retry_total",
    "Total translation retries triggered by quality issues",
    ["reason", "tier"],  # reason: l0_fail | l1_fail | format_loss
)

# ---- Format preservation metrics ----

format_elements_lost_total = Counter(
    "format_elements_lost_total",
    "Format elements (hyperlinks, footnotes, charts, ...) lost during translation",
    ["format", "element_type"],  # format: docx | xlsx | pptx | pdf
)

# Paths to skip from metrics (noisy health checks)
_SKIP_PATHS = {"/health", "/ready", "/metrics", "/favicon.ico"}


def record_translation(provider: str, file_type: str, duration: float, status: str = "success"):
    translation_total.labels(provider=provider, file_type=file_type, status=status).inc()
    translation_duration_seconds.labels(provider=provider, file_type=file_type).observe(duration)


def record_file_size(file_type: str, size_bytes: int):
    file_size_bytes.labels(file_type=file_type).observe(size_bytes)


# ---- Quality helpers ----

def record_l0_result(passed: bool, file_type: str = "unknown"):
    """Record an L0 quality check outcome.

    Args:
        passed: True if the L0 check passed.
        file_type: docx | xlsx | pptx | pdf | unknown
    """
    result = "pass" if passed else "fail"
    quality_l0_checks_total.labels(result=result, file_type=file_type).inc()


def record_l0_error(file_type: str = "unknown"):
    """Record an L0 internal error (so we can spot broken checks)."""
    quality_l0_checks_total.labels(result="error", file_type=file_type).inc()


def record_l1_verdict(
    verdict: str,
    model: str = "unknown",
    duration_seconds: float = None,
    cost_usd: float = None,
):
    """Record an L1 judge verdict.

    Args:
        verdict: pass | fail | skip | error
        model: model name (e.g. deepseek-chat)
        duration_seconds: optional, observed in histogram
        cost_usd: optional, observed in histogram
    """
    quality_l1_judge_total.labels(verdict=verdict, model=model).inc()
    if duration_seconds is not None:
        quality_l1_judge_duration_seconds.labels(model=model).observe(duration_seconds)
    if cost_usd is not None:
        quality_l1_judge_cost_usd.labels(model=model).observe(cost_usd)


def record_l2_verdict(
    verdict: str,
    model: str = "unknown",
    tier: str = "pro",
    duration_seconds: float = None,
    cost_usd: float = None,
):
    """Record an L2 Pro judge verdict.

    Args:
        verdict: pass | fail | skip | error
        model: model name (e.g. gpt-4o)
        tier: pro | business | enterprise
        duration_seconds: optional, observed in histogram
        cost_usd: optional, observed in histogram
    """
    quality_l2_judge_total.labels(verdict=verdict, model=model, tier=tier).inc()
    if duration_seconds is not None:
        quality_l2_judge_duration_seconds.labels(model=model).observe(duration_seconds)
    if cost_usd is not None:
        quality_l2_judge_cost_usd.labels(model=model).observe(cost_usd)


def record_translation_retry(reason: str, tier: str = "free"):
    """Record a translation retry triggered by a quality issue.

    Args:
        reason: l0_fail | l1_fail | format_loss | user_request
        tier: free | pro | enterprise
    """
    translation_retry_total.labels(reason=reason, tier=tier).inc()


def record_format_loss(format: str, element_type: str):
    """Record a format element that was lost during translation.

    Args:
        format: docx | xlsx | pptx | pdf
        element_type: hyperlink | footnote | chart | diagram | comment | image
    """
    format_elements_lost_total.labels(format=format, element_type=element_type).inc()


class PrometheusMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        if request.url.path in _SKIP_PATHS:
            return await call_next(request)

        start = time.time()
        response: Response = await call_next(request)
        duration = time.time() - start

        path = request.url.path
        # Group dynamic paths to avoid label explosion
        if path.startswith("/api/v1/translations/"):
            path = "/api/v1/translations/{id}"
        elif path.startswith("/api/v1/download/"):
            path = "/api/v1/download/{id}"

        http_requests_total.labels(
            method=request.method,
            path=path,
            status=str(response.status_code),
        ).inc()

        return response


def get_metrics() -> Response:
    body = generate_latest()
    return Response(content=body, media_type=CONTENT_TYPE_LATEST)
