"""Prometheus metrics declarations for the TextIQ DE Airflow plugin.

All counters/histograms are registered against the default global registry.
Single-worker constraint: AIRFLOW__API__WORKERS=1 must be set; multi-worker
scraping (MultiProcessCollector) is explicitly out of scope.

NOTE: This module runs inside the Airflow api-server process.
Do NOT import from src.*.
"""

from prometheus_client import CONTENT_TYPE_LATEST, REGISTRY, Counter, Histogram, generate_latest
from starlette.responses import Response


# Airflow's plugins_manager scans every .py under /opt/airflow/plugins, so this
# module gets imported twice (once via the plugin __init__.py, once directly).
# These helpers make Counter/Histogram construction idempotent against the
# default registry.

def _counter(name: str, doc: str, labels: list[str]) -> Counter:
    existing = REGISTRY._names_to_collectors.get(name)
    if existing is not None:
        return existing
    return Counter(name, doc, labels)


def _histogram(name: str, doc: str, labels: list[str], buckets=None) -> Histogram:
    existing = REGISTRY._names_to_collectors.get(name)
    if existing is not None:
        return existing
    if buckets is None:
        return Histogram(name, doc, labels)
    return Histogram(name, doc, labels, buckets=buckets)


# ---------------------------------------------------------------------------
# Upload-flow counters (labels: outcome, tenant_id)
# ---------------------------------------------------------------------------

upload_total = _counter(
    "upload_total",
    "Number of document upload requests",
    ["outcome", "tenant_id"],
)

approve_total = _counter(
    "approve_total",
    "Number of document approval requests",
    ["outcome", "tenant_id"],
)

reject_total = _counter(
    "reject_total",
    "Number of document rejection requests",
    ["outcome", "tenant_id"],
)

delete_total = _counter(
    "delete_total",
    "Number of document deletion requests",
    ["outcome", "tenant_id"],
)

# ---------------------------------------------------------------------------
# Tag and domain-term counters (labels: outcome, operation, tenant_id)
# ---------------------------------------------------------------------------

tag_op_total = _counter(
    "tag_op_total",
    "Number of tag mutation operations",
    ["outcome", "operation", "tenant_id"],
)

domain_term_op_total = _counter(
    "domain_term_op_total",
    "Number of domain-term mutation operations",
    ["outcome", "operation", "tenant_id"],
)

# ---------------------------------------------------------------------------
# Histograms
# ---------------------------------------------------------------------------

_UPLOAD_BUCKETS = (0.1, 0.5, 1, 2, 5, 10, 30, 60, 120)

upload_duration_seconds = _histogram(
    "upload_duration_seconds",
    "Duration of document upload handler in seconds",
    ["outcome"],
    buckets=_UPLOAD_BUCKETS,
)

# HMAC verification uses default buckets (sub-second distribution expected)
hmac_verify_duration_seconds = _histogram(
    "hmac_verify_duration_seconds",
    "Duration of HMAC verification in seconds",
    ["result"],
)

tag_endpoint_duration_seconds = _histogram(
    "tag_endpoint_duration_seconds",
    "Duration of tag mutation endpoints in seconds",
    ["operation"],
)

domain_term_endpoint_duration_seconds = _histogram(
    "domain_term_endpoint_duration_seconds",
    "Duration of domain-term mutation endpoints in seconds",
    ["operation"],
)


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def outcome_for(status_code: int) -> str:
    """Map an HTTP status code to a bounded outcome label."""
    if 200 <= status_code < 300:
        return "success"
    if 400 <= status_code < 500:
        return "client_error"
    return "server_error"


def record_outcome(counter: Counter, status_code: int, **labels) -> str:
    """Increment counter with outcome derived from status_code plus extra labels.

    Returns the derived outcome string so callers can reuse it (e.g. for histograms).
    """
    outcome = outcome_for(status_code)
    counter.labels(outcome=outcome, **labels).inc()
    return outcome


def metrics_response() -> Response:
    """Return the current default-registry snapshot as a Prometheus text response."""
    return Response(generate_latest(), media_type=CONTENT_TYPE_LATEST)


def register_metrics_route(app) -> None:
    """Mount GET /metrics on the given FastAPI sub-app."""

    @app.get("/metrics", include_in_schema=False)
    def _metrics():
        return metrics_response()
