from __future__ import annotations
import json, os
from datetime import datetime, timezone
from rich.console import Console
from rich.table import Table
from pathlib import Path

from .neurotron_config import (
    NEUROTRON_DATASET_PATH, NEUROTRON_HISTORY_KEEP, NEUROTRON_DIAG_SCHEMA,
    HOMEOSTASIS_CPU_WARN, HOMEOSTASIS_CPU_ALERT,
    HOMEOSTASIS_MEM_WARN, HOMEOSTASIS_MEM_ALERT,
    HOMEOSTASIS_LOAD_WARN, HOMEOSTASIS_LOAD_ALERT,
)
from .perception import Perception

console = Console()

def _now_iso():
    return datetime.now(timezone.utc).isoformat()

class AutoDiagnostic:
    def __init__(self, runtime_dir: str, log_dir: str):
        self.runtime_dir = runtime_dir
        self.log_dir = log_dir
        self.data_dir = Path(NEUROTRON_DATASET_PATH)
        self.data_dir.mkdir(parents=True, exist_ok=True)
        self.last_file = self.data_dir / "last_diagnostic.json"

        self.perception = Perception()
        self.current = None
        self.previous = None

    def _load_previous(self):
        if not self.last_file.exists():
            return None
        try:
            with open(self.last_file, "r") as f:
                return json.load(f)
        except Exception:
            return None

    def _save_current(self, payload: dict):
        history = []
        if self.last_file.exists():
            try:
                with open(self.last_file, "r") as f:
                    prev = json.load(f)
                history = prev.get("history", [])
                history.append({
                    "timestamp": prev.get("timestamp"),
                    "cpu_percent": prev.get("cpu_percent"),
                    "mem_percent": prev.get("mem_percent"),
                    "loadavg": prev.get("loadavg"),
                    "state": prev.get("state", "UNKNOWN"),
                })
                history = history[-NEUROTRON_HISTORY_KEEP:]
            except Exception:
                history = []
        payload["history"] = history
        with open(self.last_file, "w") as f:
            json.dump(payload, f, indent=2)

    def _classify_state(self, cpu, mem, l1):
        # valores podem ser "?"
        try:
            cpu = float(cpu)
            mem = float(mem)
            l1  = float(l1)
        except Exception:
            return "UNKNOWN"

        # ALERT/CRITICAL
        if cpu >= HOMEOSTASIS_CPU_ALERT or mem >= HOMEOSTASIS_MEM_ALERT or l1 >= HOMEOSTASIS_LOAD_ALERT:
            return "CRITICAL"
        if cpu >= HOMEOSTASIS_CPU_WARN or mem >= HOMEOSTASIS_MEM_WARN or l1 >= HOMEOSTASIS_LOAD_WARN:
            return "ALERT"

        # OKs
        return "STABLE"

    def _delta(self, a, b):
        try:
            if isinstance(a, list) and isinstance(b, list) and len(a) == len(b):
                return [round(float(x) - float(y), 2) for x, y in zip(a, b)]
            return round(float(a) - float(b), 2)
        except Exception:
            return "?"
        
    def _render_mini_trend(self, values, width=24, charset="▁▂▃▄▅▆▇█"):
        if not values:
            return ""
        lo = min(values); hi = max(values)
        if not isinstance(lo, (int, float)) or not isinstance(hi, (int, float)):
            return ""
        span = (hi - lo) or 1.0
        levels = len(charset) - 1
        bars = []
        for v in values[-width:]:
            if not isinstance(v, (int, float)):
                bars.append("·")
                continue
            i = int(round((v - lo) / span * levels))
            bars.append(charset[i])
        return "".join(bars)

    def run_exam(self):
        console.print("\n[bold]🤖 Iniciando rotina de Auto-Diagnóstico Evolutivo...[/bold]\n")

        snap = self.perception.snapshot()
        cpu = snap.get("cpu_percent", "?")
        mem = snap.get("mem_percent", "?")
        load = snap.get("loadavg", ["?", "?", "?"])

        prev = self._load_previous()
        self.previous = prev

        # deltas
        cpu_prev = prev.get("cpu_percent") if prev else "?"
        mem_prev = prev.get("mem_percent") if prev else "?"
        load_prev = prev.get("loadavg") if prev else ["?", "?", "?"]

        d_cpu = self._delta(cpu, cpu_prev)
        d_mem = self._delta(mem, mem_prev)
        d_load = self._delta(load, load_prev)

        # estado
        l1 = load[0] if isinstance(load, list) and load else "?"
        state = self._classify_state(cpu, mem, l1)

        # tabela
        table = Table(title="🩺 Exame Clínico Evolutivo", show_lines=True)
        table.add_column("Sinal Vital")
        table.add_column("Atual", justify="right")
        table.add_column("Δ", justify="center")
        table.add_column("Anterior", justify="right")

        def fmt(v):
            if isinstance(v, list):
                return str(v)
            return str(v)

        table.add_row("CPU (%)", fmt(cpu), fmt(d_cpu), fmt(cpu_prev))
        table.add_row("Memória (%)", fmt(mem), fmt(d_mem), fmt(mem_prev))
        table.add_row("Carga média (1/5/15)", fmt(load), "≈" if d_load == "?" else fmt(d_load), fmt(load_prev))
        console.print(table)

        payload = {
            "schema": NEUROTRON_DIAG_SCHEMA,
            "timestamp": _now_iso(),
            "cpu_percent": cpu,
            "mem_percent": mem,
            "loadavg": load,
            "state": state,
            "env": {
                "user": snap.get("env_user"),
                "term": snap.get("env_term"),
            },
        }
        self._save_current(payload)
        console.print(f"[green]✔ Histórico evolutivo atualizado em:[/green] \n{self.last_file}")

        # Atualiza telemetria contínua
        try:
            telemetry_file = Path(NEUROTRON_DATASET_PATH) / "telemetry.json"
            telemetry_file.parent.mkdir(parents=True, exist_ok=True)

            telemetry = []
            if telemetry_file.exists():
                telemetry = json.loads(telemetry_file.read_text() or "[]")

            telemetry.append({
                "timestamp": payload["timestamp"],
                "cpu": payload.get("cpu_percent"),
                "mem": payload.get("mem_percent"),
                "load": payload.get("loadavg"),
                "state": payload.get("state"),
            })

            telemetry = telemetry[-128:]  # manter últimas 128 amostras
            telemetry_file.write_text(json.dumps(telemetry, indent=2))
        except Exception as e:
            console.print(f"[yellow]⚠️ Falha ao atualizar telemetria:[/] {e}")


        return state, payload
