"""Phase 5C: Sentinel Agents — lightweight health checks feeding goal engine."""
import json
import time
from datetime import datetime, timezone, timedelta
from pathlib import Path
from pulse.core.paths import get_state_dir, get_brain_dir
from pulse.core import event_bus

SENTINEL_ALERTS = get_state_dir() / "sentinel_alerts.jsonl"
SENTINEL_DEDUP = get_state_dir() / "health" / "sentinel_dedup.json"


def _load_dedup() -> dict:
    if not SENTINEL_DEDUP.exists():
        return {}
    try:
        return json.loads(SENTINEL_DEDUP.read_text(encoding="utf-8"))
    except Exception:
        return {}


def _save_dedup(data: dict):
    SENTINEL_DEDUP.parent.mkdir(parents=True, exist_ok=True)
    SENTINEL_DEDUP.write_text(json.dumps(data, indent=2), encoding="utf-8")


def _should_suppress(sentinel_type: str, target: str) -> bool:
    """Suppress duplicate alerts within 24h."""
    dedup = _load_dedup()
    key = f"{sentinel_type}:{target}"
    if key in dedup:
        last = datetime.fromisoformat(dedup[key])
        if datetime.now(timezone.utc) - last < timedelta(hours=24):
            return True
    return False


def _record_alert(alert: dict):
    """Log alert + update dedup."""
    # Log to JSONL
    SENTINEL_ALERTS.parent.mkdir(parents=True, exist_ok=True)
    with open(SENTINEL_ALERTS, "a", encoding="utf-8") as f:
        f.write(json.dumps(alert) + "\n")
    # Update dedup
    dedup = _load_dedup()
    dedup[f"{alert['sentinel_type']}:{alert['target']}"] = datetime.now(timezone.utc).isoformat()
    _save_dedup(dedup)
    # Publish event
    event_bus.publish("sentinel.alert", "sentinels", alert)


def _sentinel_brain_staleness() -> list:
    """Check Hippocampus file ages."""
    alerts = []
    brain_dir = get_brain_dir()
    lessons_dir = brain_dir / "Lessons"
    if lessons_dir.exists():
        latest = max(
            (f.stat().st_mtime for f in lessons_dir.iterdir() if f.is_file()),
            default=0,
        )
        if latest > 0:
            age_hours = (time.time() - latest) / 3600
            if age_hours > 48:
                alerts.append({
                    "sentinel_type": "brain_staleness",
                    "timestamp": datetime.now(timezone.utc).isoformat(),
                    "severity": 0.6,
                    "message": f"No new lessons in {age_hours:.0f}h",
                    "suggested_action": "Run knowledge extraction or interactive session",
                    "target": "Hippocampus/Lessons",
                })
    return alerts


def _sentinel_prediction_accuracy() -> list:
    """Check prediction oracle accuracy."""
    alerts = []
    metrics_file = get_state_dir() / "prediction_metrics.json"
    if metrics_file.exists():
        try:
            metrics = json.loads(metrics_file.read_text(encoding="utf-8"))
            accuracy = metrics.get("accuracy", 1.0)
            if accuracy < 0.5:
                alerts.append({
                    "sentinel_type": "prediction_accuracy",
                    "timestamp": datetime.now(timezone.utc).isoformat(),
                    "severity": 0.7,
                    "message": f"Prediction accuracy {accuracy:.2f} < 0.5",
                    "suggested_action": "Review prediction pipeline and training data",
                    "target": "prediction_daemon",
                })
        except Exception:
            pass
    return alerts


def _sentinel_error_rate() -> list:
    """Check recent error rate from interoception."""
    alerts = []
    try:
        signals_file = get_state_dir() / "interoception" / "signals.json"
        if signals_file.exists():
            signals = json.loads(signals_file.read_text(encoding="utf-8"))
            error_rate = signals.get("error_rate", {}).get("value", 0.0)
            if error_rate > 0.15:
                alerts.append({
                    "sentinel_type": "error_rate",
                    "timestamp": datetime.now(timezone.utc).isoformat(),
                    "severity": 0.8,
                    "message": f"Error rate {error_rate:.2f} > 0.15 threshold",
                    "suggested_action": "Check system logs for recurring errors",
                    "target": "system_errors",
                })
    except Exception:
        pass
    return alerts


def _sentinel_consolidation_health() -> list:
    """Check consolidation recency."""
    alerts = []
    log_file = get_brain_dir() / "Evidence" / "Metrics" / "consolidation_log.json"
    if log_file.exists():
        try:
            log = json.loads(log_file.read_text(encoding="utf-8"))
            ts = log.get("timestamp", "")
            if ts:
                last_run = datetime.fromisoformat(ts)
                age_hours = (datetime.now(timezone.utc) - last_run).total_seconds() / 3600
                if age_hours > 12:  # Should run every 6h
                    alerts.append({
                        "sentinel_type": "consolidation_health",
                        "timestamp": datetime.now(timezone.utc).isoformat(),
                        "severity": 0.7,
                        "message": f"Consolidation last ran {age_hours:.0f}h ago (expected <6h)",
                        "suggested_action": "Check orchestrator and consolidation daemon",
                        "target": "consolidation_daemon",
                    })
        except Exception:
            pass
    return alerts


def run_sentinels(verbose=False) -> dict:
    """
    Run all sentinels. Returns summary of alerts found.
    Total runtime target: <60s (no LLM calls).
    """
    all_alerts = []
    sentinel_fns = [
        _sentinel_brain_staleness,
        _sentinel_prediction_accuracy,
        _sentinel_error_rate,
        _sentinel_consolidation_health,
    ]

    for fn in sentinel_fns:
        try:
            alerts = fn()
            for alert in alerts:
                if not _should_suppress(alert["sentinel_type"], alert["target"]):
                    _record_alert(alert)
                    all_alerts.append(alert)
                    if verbose:
                        print(f"  [SENTINEL] {alert['sentinel_type']}: {alert['message']}")
        except Exception:
            pass

    return {"alerts_found": len(all_alerts), "alerts": all_alerts}
