"""T07 GlitchTip — Sentry SDK integration.

Per v4.0 §T07. Self-hosted Sentry-compatible error tracking at
glitchtip.rugmunch.io (Sentry SDK pointed at our own instance).

Key principle: NEVER leak secrets. The before_send hook strips
authorization headers, X-API-Key, passwords, tokens, etc.

Per v3 unfuck rule #7: SDK init must be at module level, not in lifespan.
But the SDK itself uses lazy init — setup_sentry() is called once at startup.
"""
from __future__ import annotations

import logging
import os
from typing import Any

log = logging.getLogger(__name__)

# Config — defaults to local GlitchTip; override via env
DEFAULT_DSN = "http://rmi-glitchtip-web:8000/1"
DEFAULT_ENV = "production"
DEFAULT_SAMPLE_RATE = 0.1  # 10% of transactions traced

# Sensitive field names that must never be sent to error tracking
SENSITIVE_KEYS = {
    "authorization", "x-api-key", "x-payment", "x-agent-id",
    "password", "secret", "token", "cookie", "set-cookie",
    "api_key", "apikey", "access_token", "refresh_token",
    "private_key", "credit_card", "ssn",
}


def _scrub_secrets(data: Any) -> Any:
    """Recursively replace sensitive values with '[REDACTED]'.

    Walks dicts and lists, checking each key against SENSITIVE_KEYS.
    """
    if isinstance(data, dict):
        return {
            k: "[REDACTED]" if k.lower() in SENSITIVE_KEYS else _scrub_secrets(v)
            for k, v in data.items()
        }
    if isinstance(data, list):
        return [_scrub_secrets(x) for x in data]
    return data


def setup_sentry() -> bool:
    """Initialize the Sentry SDK pointed at our self-hosted GlitchTip.

    Returns True if initialized, False if DSN not configured or
    sentry_sdk is not installed (graceful — backend still works).
    """
    dsn = os.getenv("GLITCHTIP_DSN") or os.getenv("SENTRY_DSN")
    if not dsn:
        log.info("sentry_disabled no_dsn")
        return False

    try:
        import sentry_sdk
        from sentry_sdk.integrations.fastapi import FastApiIntegration
        from sentry_sdk.integrations.httpx import HttpxIntegration
        from sentry_sdk.integrations.redis import RedisIntegration
    except ImportError as exc:
        log.info(f"sentry_sdk_not_installed err={exc}")
        return False

    env = os.getenv("ENVIRONMENT", DEFAULT_ENV)
    traces_sample_rate = float(os.getenv("SENTRY_TRACES_SAMPLE_RATE", DEFAULT_SAMPLE_RATE))

    sentry_sdk.init(
        dsn=dsn,
        environment=env,
        traces_sample_rate=traces_sample_rate,
        send_default_pii=False,  # PII stays in our infra
        integrations=[
            FastApiIntegration(transaction_style="endpoint"),
            HttpxIntegration(),
            RedisIntegration(),
        ],
        before_send=_before_send,
        before_send_transaction=_before_send_transaction,
    )
    log.info(f"sentry_initialized dsn={dsn[:30]}... env={env} sample_rate={traces_sample_rate}")
    return True


def _before_send(event: dict, hint: dict) -> dict | None:
    """Strip secrets before sending to GlitchTip.

    Per v4.0 §T07: secrets scrubbed before send. This is a hard
    requirement — never log full request bodies with credentials.
    """
    try:
        if "request" in event:
            if "headers" in event["request"]:
                event["request"]["headers"] = _scrub_secrets(event["request"]["headers"])
            if "data" in event["request"]:
                event["request"]["data"] = _scrub_secrets(event["request"]["data"])
        if "extra" in event:
            event["extra"] = _scrub_secrets(event["extra"])
        if "user" in event:
            # Strip email/IP — keep only id
            event["user"] = {"id": event["user"].get("id", "anon")}
        return event
    except Exception as e:
        log.warning(f"sentry_scrub_fail err={e}")
        return event  # Better to log it than to drop it


def _before_send_transaction(event: dict, hint: dict) -> dict | None:
    """Drop transactions that are health checks / metrics scrapes (high volume, low value)."""
    url = event.get("transaction") or event.get("request", {}).get("url", "")
    if any(s in url for s in ("/health", "/ready", "/live", "/metrics", "/openapi.json")):
        return None
    return event


def capture_exception(error: BaseException, **extra: Any) -> None:
    """Manually capture an exception. For use in catch blocks where
    we want to log to Sentry but continue execution."""
    try:
        import sentry_sdk
        with sentry_sdk.push_scope() as scope:
            for k, v in extra.items():
                scope.set_extra(k, v)
            sentry_sdk.capture_exception(error)
    except Exception as e:
        log.warning(f"sentry_capture_fail err={e}")


def capture_message(message: str, level: str = "info", **extra: Any) -> None:
    """Manually capture a message. For non-exception events."""
    try:
        import sentry_sdk
        with sentry_sdk.push_scope() as scope:
            for k, v in extra.items():
                scope.set_extra(k, v)
            sentry_sdk.capture_message(message, level=level)
    except Exception as e:
        log.warning(f"sentry_message_fail err={e}")


def flush_sentry(timeout: float = 2.0) -> None:
    """Flush pending events. Call before shutdown."""
    try:
        import sentry_sdk
        sentry_sdk.flush(timeout=timeout)
    except Exception:
        pass
