"""GraphProbe — health check for graph subsystems.

Probes all 8 graph systems + IKE and returns a structured health
report with per-system reachability, latency, and error details.

Usage::

    from databridge_core.sdk.core.graph_probe import GraphProbe

    probe = GraphProbe(ctx)
    health = probe.check()
    # health["overall"] → "healthy" | "degraded" | "unavailable"
    # health["systems"]["lineage_tracker"]["reachable"] → True
"""

from __future__ import annotations

import logging
import time
from typing import Any, Callable, Dict, Optional

logger = logging.getLogger(__name__)


class GraphProbe:
    """Health probe for SDK graph and IKE subsystems."""

    def __init__(self, ctx: Any):
        self._ctx = ctx

    def check(self) -> Dict[str, Any]:
        """Probe all graph systems and return structured health report.

        Returns:
            Dict with keys:
                ``overall``: ``"healthy"`` | ``"degraded"`` | ``"unavailable"``
                ``systems``: Per-system probe results
                ``trace_id``: From context for correlation
        """
        systems: Dict[str, Dict[str, Any]] = {}

        graphs = getattr(self._ctx, "graphs", None)
        ike = getattr(self._ctx, "ike", None)

        # 1. Lineage Tracker
        systems["lineage_tracker"] = self._probe_system(
            "lineage_tracker",
            lambda: graphs.lineage_available if graphs else False,
        )

        # 2. Evidence Tracker
        systems["evidence_tracker"] = self._probe_system(
            "evidence_tracker",
            lambda: graphs.evidence_available if graphs else False,
        )

        # 3. GraphRAG Vectors
        systems["graphrag_vectors"] = self._probe_system(
            "graphrag_vectors",
            lambda: graphs.vector_available if graphs else False,
        )

        # 4. KB Ingestion Pipeline
        systems["kb_ingestion_pipeline"] = self._probe_system(
            "kb_ingestion_pipeline",
            lambda: graphs.kb_available if graphs else False,
        )

        # 5. Entity Linker
        systems["entity_linker"] = self._probe_system(
            "entity_linker",
            lambda: graphs.linker_available if graphs else False,
        )

        # 6. Codex GraphRAG
        systems["codex_graphrag"] = self._probe_system(
            "codex_graphrag",
            lambda: graphs.codex_available if graphs else False,
        )

        # 7. IKE Graph
        systems["ike_graph"] = self._probe_system(
            "ike_graph",
            lambda: graphs.ike_graph_available if graphs else False,
        )

        # 8. Formula Dependency Graph
        systems["formula_dependency_graph"] = self._probe_system(
            "formula_dependency_graph",
            lambda: graphs.formula_graph_available if graphs else False,
        )

        # 9. IKE Bridge (separate from graph-based IKE)
        systems["ike_bridge"] = self._probe_system(
            "ike_bridge",
            lambda: ike.available if ike else False,
        )

        # Compute overall status
        reachable_count = sum(
            1 for s in systems.values() if s.get("reachable")
        )
        enabled_count = sum(
            1 for s in systems.values() if s.get("enabled")
        )

        if enabled_count == 0 or reachable_count == 0:
            overall = "unavailable"
        elif reachable_count == enabled_count:
            overall = "healthy"
        else:
            overall = "degraded"

        return {
            "overall": overall,
            "systems": systems,
            "reachable_count": reachable_count,
            "enabled_count": enabled_count,
            "trace_id": getattr(self._ctx, "trace_id", ""),
        }

    def _probe_system(
        self,
        name: str,
        check_fn: Callable[[], bool],
    ) -> Dict[str, Any]:
        """Probe a single system: enabled, reachable, latency_ms, error."""
        result: Dict[str, Any] = {
            "enabled": False,
            "reachable": False,
            "latency_ms": 0.0,
            "error": None,
        }
        try:
            t0 = time.monotonic()
            reachable = check_fn()
            elapsed = round((time.monotonic() - t0) * 1000, 2)
            result["enabled"] = True
            result["reachable"] = bool(reachable)
            result["latency_ms"] = elapsed
        except Exception as exc:
            result["enabled"] = True
            result["error"] = str(exc)
            logger.debug("GraphProbe %s error: %s", name, exc)
        return result
