"""
agents.py — micro-agentes internos do TRM v1

Três agentes:
  🛡️ Guardião   — homeostase + proteção
  🧭 Explorador  — previsões simples + profundidade TRM
  📜 Arqueólogo  — correlação com eventos passados (Hippocampus)

Todos os logs vão para logbus.debug().
"""

from __future__ import annotations
from typing import Any, Dict, Iterable, List
from pathlib import Path
import json

from neurotron.logbus import logbus
from .state import TRMState
from .events import (
    TRM_EVENT_TEMP_RISING_FAST,
    TRM_EVENT_ENTER_STRESS_ZONE,
    TRM_EVENT_FS_WARNING,
    TRM_EVENT_LOOP_SUSPECT,
    TRM_EVENT_RECOVERING,
    summarize_telemetry_events,
)


class TRMAgentBase:
    name = "trm.agent"

    def _dbg(self, msg: str):
        logbus.debug(f"[{self.name}] {msg}")

    def step(self, state: TRMState, tele: Dict[str, Any]) -> TRMState:
        """Override nas subclasses."""
        return state


# ========================================================================
# 🛡️ Guardião — homeostase
# ========================================================================

class GuardianAgent(TRMAgentBase):
    name = "trm.guardian"

    def step(self, state: TRMState, tele: Dict[str, Any]) -> TRMState:
        st = state.copy()

        raw = tele.get("raw", {}) or {}
        temp = float(tele.get("temp", 0.0) or 0.0)
        jitter = float(tele.get("jitter", 0.0) or 0.0)
        fs = tele.get("fs", {}) or {}
        events = tele.get("events", []) or []

        cpu = float(raw.get("cpu") or 0.0)
        mem = float(raw.get("mem") or 0.0)
        load = float(raw.get("load") or 0.0)

        # Estado cognitivo base pela temperatura
        if temp < 25:
            st.cog_state = "stable"
        elif temp < 45:
            st.cog_state = "warm"
        elif temp < 65:
            st.cog_state = "hot"
        elif temp < 85:
            st.cog_state = "critical"
        else:
            st.cog_state = "recovery"

        st.temp = temp
        st.jitter = jitter

        # Ajuste de profundidade em função do stress
        if cpu > 90 or mem > 90 or load > 3.0:
            # stress forte: reduzir profundidade para poupar energia
            old = st.depth
            st.depth = max(1, st.depth - 1)
            if st.depth != old:
                self._dbg(f"stress alto (cpu={cpu}, mem={mem}, load={load}) → depth {old}→{st.depth}")

        elif 40 < cpu < 80 and 40 < mem < 80 and load < 2.0:
            # zona confortável: podemos aprofundar um pouco
            old = st.depth
            st.depth = min(5, st.depth + 1)
            if st.depth != old:
                self._dbg(f"zona confortável → depth {old}→{st.depth}")

        # FS warning → baixa valência
        if fs.get("read_only") or (fs.get("io_errors") or 0) > 0:
            st.valence -= 1.0
            self._dbg("fs_warning → valence -1")

        # eventos explícitos
        counts = summarize_telemetry_events(events)
        if counts.get(TRM_EVENT_ENTER_STRESS_ZONE, 0) > 0:
            st.valence -= 0.5
        if counts.get(TRM_EVENT_RECOVERING, 0) > 0:
            st.valence += 0.5

        # clamp valência
        if st.valence > 5.0:
            st.valence = 5.0
        if st.valence < -5.0:
            st.valence = -5.0

        return st


# ========================================================================
# 🧭 Explorador — previsões e “curiosidade”
# ========================================================================

class ExplorerAgent(TRMAgentBase):
    name = "trm.explorer"

    def __init__(self):
        self._last_cpu: float = 0.0
        self._last_mem: float = 0.0

    def step(self, state: TRMState, tele: Dict[str, Any]) -> TRMState:
        st = state.copy()

        raw = tele.get("raw", {}) or {}
        cpu = float(raw.get("cpu") or 0.0)
        mem = float(raw.get("mem") or 0.0)

        # tendências simples
        d_cpu = cpu - self._last_cpu
        d_mem = mem - self._last_mem

        self._last_cpu = cpu
        self._last_mem = mem

        # se CPU a subir rápido, assumir "mais trabalho interno"
        if d_cpu > 5:
            st.energy -= 0.5
            self._dbg(f"cpu tendência ↑ ({d_cpu:+.1f}) → energia -0.5")

        # se CPU a descer e mem estável → sistema está a recuperar
        if d_cpu < -5 and abs(d_mem) < 2:
            st.valence += 0.2
            self._dbg("recuperação leve detectada → valence +0.2")

        # se valência muito positiva → TRM aprofunda um pouco (exploração)
        if st.valence > 1.5 and st.energy > 20.0:
            old = st.depth
            st.depth = min(7, st.depth + 1)
            if st.depth != old:
                self._dbg(f"valence alta ({st.valence:.2f}) → aprofundar depth {old}→{st.depth}")

        return st


# ========================================================================
# 📜 Arqueólogo — memória & padrões passados
# ========================================================================

class ArchaeologistAgent(TRMAgentBase):
    name = "trm.archaeologist"

    def __init__(self, ctx):
        self.ctx = ctx
        self.events_file = ctx.memory.events_file if hasattr(ctx, "memory") else Path("/opt/kernel/neurotron/logs/events.jsonl")

    def _tail_events(self, max_lines: int = 64) -> List[Dict[str, Any]]:
        """
        Lê as últimas linhas de events.jsonl (se existir).
        Erros são ignorados silenciosamente.
        """
        path = self.events_file
        if not path.exists():
            return []

        try:
            # ler todo e pegar as últimas max_lines (ficheiro é pequeno)
            lines = path.read_text(encoding="utf-8").splitlines()
            lines = lines[-max_lines:]
        except Exception:
            return []

        out: List[Dict[str, Any]] = []
        for line in lines:
            line = line.strip()
            if not line:
                continue
            try:
                obj = json.loads(line)
                out.append(obj)
            except Exception:
                continue
        return out

    def step(self, state: TRMState, tele: Dict[str, Any]) -> TRMState:
        st = state.copy()

        events = self._tail_events()
        if not events:
            return st

        # Contar eventos “perigosos” recentes
        danger = 0
        recovery = 0

        for ev in events:
            kind = ev.get("kind") or ""
            if "telemetry.event.fs_warning" in kind:
                danger += 1
            if "telemetry.event.loop_suspect" in kind:
                danger += 1
            if "telemetry.event.recovering" in kind:
                recovery += 1

        st.last_events = danger + recovery

        if danger > 0:
            st.valence -= min(1.0, 0.1 * danger)
            self._dbg(f"encontrou {danger} eventos perigosos recentes → valence -{min(1.0, 0.1 * danger):.2f}")

        if recovery > 0:
            st.valence += min(1.0, 0.1 * recovery)
            self._dbg(f"encontrou {recovery} eventos de recuperação → valence +{min(1.0, 0.1 * recovery):.2f}")

        return st
