"""
engine.py — TRMEngine v1

Modelo recursivo mínimo:
  - consome uma amostra de Telemetria V5
  - passa por 3 micro-agentes
  - atualiza energia, valência, profundidade e estado cognitivo
  - gera snapshot opcional para o Hippocampus

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

from __future__ import annotations
from typing import Any, Dict, Optional
import time
from pathlib import Path

from neurotron.logbus import logbus
from .state import TRMState
from .agents import GuardianAgent, ExplorerAgent, ArchaeologistAgent
from .thought_agent import ThoughtAgent
from .events import make_trm_snapshot_payload


class TRMEngine:
    """
    Tiny Recursive Model v1 — integrada ao Cortex.

    Uso típico (no Cortex.rest):

        tele = self.tele.step()
        self.trm.step(tele)

    O próprio TRMEngine nunca levanta exceção para fora — falhas internas
    são logadas via logbus.debug() e ignoradas.
    """

    def __init__(self, cortex, initial_energy: float = 100.0):
        self.ctx = cortex
        self.state = TRMState(energy=initial_energy, mode="idle")
        self.last_step_ts: Optional[float] = None

        # micro-agentes
        self.guardian = GuardianAgent()
        self.explorer = ExplorerAgent()
        self.archaeologist = ArchaeologistAgent(self.ctx)
        self.thought_agent = ThoughtAgent(self.ctx)


        # histórico curto de estados do TRM (para futuro TRM v2)
        self._history = []

    # ------------------------------------------------------------------ #
    #  Helpers internos
    # ------------------------------------------------------------------ #

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

    def _compute_step_cost(self, st_before: TRMState, st_after: TRMState, tele: Dict[str, Any]) -> float:
        """
        Custo “energético” de um passo TRM simplificado.

        Δenergia básica:
          base_cost = 0.5
          + 0.2 * depth
          + 0.05 * len(events telemétricos)
        """
        events = (tele or {}).get("events", []) or []
        base_cost = 0.5
        cost = base_cost + 0.2 * float(st_after.depth or 1) + 0.05 * len(events)

        # Se o sistema já estiver quente, custo sobe ligeiramente
        if st_after.temp > 50:
            cost *= 1.2
        if st_after.cog_state in ("hot", "critical"):
            cost *= 1.1

        return cost

    def _apply_energy(self, st: TRMState, cost: float) -> TRMState:
        out = st.copy()
        out.energy = max(0.0, out.energy - cost)

        # Se energia muito baixa, força modo mínimo
        if out.energy < 10.0 and out.depth > 1:
            old = out.depth
            out.depth = 1
            self._dbg(f"energia baixa ({out.energy:.1f}) → depth {old}→1 (modo mínimo)")

        if out.energy < 5.0:
            out.mode = "idle"
        else:
            out.mode = "active"

        return out

    def _update_history(self, st: TRMState):
        self._history.append(st.to_dict())
        if len(self._history) > 64:
            self._history = self._history[-64:]

    # ------------------------------------------------------------------ #
    #  Passo TRM
    # ------------------------------------------------------------------ #

    def step(self, telemetry: Dict[str, Any]) -> TRMState:
        """
        Única função pública chamada pelo Cortex.

        Args:
            telemetry: dict retornado por TelemetryV5.step()

        Returns:
            novo TRMState (também guardado em self.state)
        """
        try:
            t0 = time.monotonic()
            st0 = self.state.copy()

            # jitter interno TRM (independente do jitter de Telemetria V5)
            if self.last_step_ts is None:
                self.last_step_ts = t0
            else:
                internal_jitter = t0 - self.last_step_ts
                self.last_step_ts = t0
                # jitter muito alto → TRM sente-se “desalinhado”
                if internal_jitter > 2.0:
                    st0.valence -= 0.1
                    self._dbg(f"jitter interno alto ({internal_jitter:.2f}s) → valence -0.1")

            # ----------------------------------------------------------
            #  Passagem pelos micro-agentes
            # ----------------------------------------------------------
            st1 = self.guardian.step(st0, telemetry)
            st2 = self.explorer.step(st1, telemetry)
            st3 = self.archaeologist.step(st2, telemetry)

            # pensamentos simbólicos
            try:
                self.thought_agent.step(st3, telemetry)
            except Exception as e:
                self._dbg(f"thought_agent erro: {e}")


            # ----------------------------------------------------------
            #  Custo energético + modo de operação
            # ----------------------------------------------------------
            cost = self._compute_step_cost(st0, st3, telemetry)
            st4 = self._apply_energy(st3, cost)

            self.state = st4
            self._update_history(st4)

            # ----------------------------------------------------------
            #  Exportar snapshot para Hippocampus (low-rate)
            # ----------------------------------------------------------
            # Não queremos spammar o log, por isso só de vez em quando:
            try:
                # tick interno TRM: a cada ~10 passos do Cortex,
                # o TRM snapshot é interessante.
                # Usamos o próprio comprimento do histórico como step counter.
                if len(self._history) % 10 == 0 and hasattr(self.ctx, "memory"):
                    payload = make_trm_snapshot_payload(st4, telemetry)
                    self.ctx.memory.remember("trm.snapshot", payload)
            except Exception as e:
                self._dbg(f"erro ao gravar snapshot no Hippocampus: {e}")

            # log discreto em modo debug
            self._dbg(
                f"step ok: mode={st4.mode} cog={st4.cog_state} "
                f"energy={st4.energy:.1f} depth={st4.depth} "
                f"valence={st4.valence:+.2f}"
            )

            return st4

        except Exception as e:
            # Nunca deixamos o TRM quebrar o Cortex
            self._dbg(f"exceção no TRM step: {e}")
            return self.state
