"""HealthMixin — system health check."""
from __future__ import annotations

import logging
from typing import Any, Dict, List

from .models import HealthReport

logger = logging.getLogger(__name__)


class HealthMixin:
    """Mixin providing the health() check."""

    async def health(self) -> HealthReport:
        """
        Return a system status report.

        Checks every module for liveness. Returns HealthReport with per-module
        status strings and a top-level ok flag.
        """
        errors: List[str] = list(self._init_errors)
        modules: Dict[str, str] = {}

        # Storage
        if self.storage is None:
            modules["storage"] = "unavailable"
        else:
            try:
                await self.storage.ping()
                modules["storage"] = "ok"
            except Exception as exc:
                modules["storage"] = f"error: {exc}"
                errors.append(f"storage health: {exc}")

        # All other modules — just check presence
        module_attrs = {
            "extractor": "extraction",
            "entity_resolver": "entity_resolution",
            "reconciler": "reconciliation",
            "cornerstone_guardian": "cornerstone",
            "token_budget_manager": "token_budget",
            "retrieval": "retrieval",
            "feedback_system": "feedback",
            "llm": "llm",
        }
        for attr, label in module_attrs.items():
            obj = getattr(self, attr, None)
            modules[label] = "ok" if obj is not None else "unavailable"

        # Check sqlite-vec availability (informational — does not affect ok status)
        if self.storage is not None:
            vec_loaded = getattr(self.storage, "_vec_loaded", False)
            modules["sqlite_vec_available"] = "ok" if vec_loaded else "unavailable"

        # Quality Gates (#368)
        if self.config.quality_gates_enabled and self._metrics is not None:
            try:
                from cortex.core.quality_gates import QualityGateEngine
                gate_engine = QualityGateEngine(
                    metrics=self._metrics,
                    config=self.config,
                )
                gate_results = await gate_engine.check_all()
                failed_gates = [r for r in gate_results if not r.passed]
                modules["quality_gates"] = "ok" if not failed_gates else "degraded"
                for r in failed_gates:
                    if r.gate.action == "block":
                        errors.append(f"quality_gate/{r.gate.name}: {r.message}")
                    else:
                        # warn/degrade don't affect overall ok
                        modules[f"gate/{r.gate.name}"] = r.message
            except Exception as exc:
                logger.warning("Quality gate evaluation failed: %s", exc)
                modules["quality_gates"] = f"error: {exc}"

        # Circadian dream cycle status (#772)
        _circadian_engine = getattr(self, "_circadian_engine", None)
        if _circadian_engine is not None:
            _last_run = getattr(_circadian_engine, "_dream_cycle_last_run", None)
            _last_ok = getattr(_circadian_engine, "_dream_cycle_last_ok", True)
            if _last_run:
                modules["dream_cycle"] = f"last_run={_last_run} ok={_last_ok}"
            else:
                modules["dream_cycle"] = "scheduled (no runs yet)"
        else:
            # Try loading from DB if no live engine
            try:
                if self.storage is not None:
                    _raw = await self.storage.get_config("last_dream_cycle")
                    if _raw:
                        import json as _json
                        _dc_data = _json.loads(_raw)
                        modules["dream_cycle"] = f"last_run={_dc_data.get('completed_at', 'unknown')}"
                    else:
                        modules["dream_cycle"] = "never_run"
            except Exception:
                modules["dream_cycle"] = "unknown"

        # Deep Dream quality scores (#772)
        _deep_dream_enabled = getattr(self.config, "deep_dream_enabled", False)
        if _deep_dream_enabled and self.storage is not None:
            try:
                _dd_rows = await self.storage._db.execute_fetchall(
                    """SELECT depth_score, discovery_score, emotional_resonance_score,
                              created_at, thread_id
                       FROM dreams WHERE owner_id = ?
                       ORDER BY created_at DESC LIMIT 1""",
                    (getattr(self.config, "owner_id", "default"),),
                )
                if _dd_rows:
                    _dd = _dd_rows[0]
                    _avg = round((_dd[0] + _dd[1] + _dd[2]) / 3, 2)
                    modules["dream_cycle"] += (
                        f" | deep_dream: depth={_dd[0]} discovery={_dd[1]} "
                        f"resonance={_dd[2]} avg={_avg} thread={_dd[4]}"
                    )
                else:
                    modules["dream_cycle"] += " | deep_dream: enabled (no dreams yet)"
            except Exception as _dd_exc:
                logger.debug("Deep dream health check skipped: %s", _dd_exc)

        ok = all(
            v == "ok" for k, v in modules.items()
            if k not in ("sqlite_vec_available", "quality_gates", "dream_cycle")
            and not k.startswith("gate/")
        ) and not errors

        return HealthReport(
            ok=ok,
            storage=modules.get("storage", "unknown"),
            modules=modules,
            errors=errors,
        )
