from __future__ import annotations


if __package__ in {None, ""}:  # pragma: no cover - permite ejecución como script
    import sys
    from pathlib import Path

    _current_file = Path(__file__).resolve()
    for _parent in (_current_file.parent, *_current_file.parents):
        if (_parent / "pyproject.toml").exists() or (_parent / ".git").exists():
            _project_root = _parent
            break
    else:
        _project_root = _current_file.parents[2]
    _src_root = _project_root / "src"
    for _path in (_project_root, _src_root):
        _path_str = str(_path)
        if _path_str not in sys.path:
            sys.path.insert(0, _path_str)
    from agix._script_utils import bootstrap_script_environment

    bootstrap_script_environment("__main__", str(_current_file))

from typing import Any, Dict, Sequence, Set
import os
import secrets
import warnings

from fastapi import (
    Depends,
    FastAPI,
    Header,
    HTTPException,
    WebSocket,
    WebSocketDisconnect,
    status,
)
from fastapi.responses import JSONResponse
from pydantic import BaseModel, Field
from agix.qualia.constants import QUALIA_WS_PATH
from agix.qualia.qualia_engine import QualiaEngine
from agix.memory.experiential import GestorDeMemoria
import uvicorn


class RegisterPayload(BaseModel):
    name: str
    metadata: Dict[str, Any] = Field(default_factory=dict)


class EventPayload(BaseModel):
    event: str


class QualiaPayload(BaseModel):
    input: list[float] = Field(default_factory=list)
    internal: list[float] = Field(default_factory=list)


def _load_api_token() -> str:
    token = os.getenv("AGIX_API_TOKEN")
    if token:
        return token

    generated = secrets.token_urlsafe(32)
    warnings.warn(
        "AGIX_API_TOKEN no está definido. Se ha generado un token temporal: "
        f"{generated}. Establece la variable de entorno AGIX_API_TOKEN con un valor"
        " seguro antes de iniciar el hub para evitar este comportamiento.",
        RuntimeWarning,
        stacklevel=2,
    )
    return generated


API_TOKEN = _load_api_token()


def verify_token(authorization: str = Header(...)) -> None:
    scheme, _, token = authorization.partition(" ")
    if scheme.lower() != "bearer" or token != API_TOKEN:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")


class QualiaHub:
    """Orquestador central de módulos AGIX."""

    def __init__(self) -> None:
        self.modules: Dict[str, Dict[str, Any]] = {}
        self.app = FastAPI(title="Qualia Hub")
        try:
            self.engine = QualiaEngine(GestorDeMemoria())
        except Exception:
            self.engine = QualiaEngine(GestorDeMemoria(), backend="jax")
        self.current_state: Any | None = None
        self.clients: Set[WebSocket] = set()
        self._setup_routes()

    # ------------------------------------------------------------------
    def _setup_routes(self) -> None:
        @self.app.post("/register")
        def _register(payload: RegisterPayload, _: None = Depends(verify_token)):
            name = payload.name
            metadata = payload.metadata
            if name:
                self.register_module(name, metadata)
            return JSONResponse({"status": "ok"})

        @self.app.get("/modules")
        def _modules(_: None = Depends(verify_token)):
            return JSONResponse({"modules": self.modules})

        @self.app.post("/event")
        def _event(payload: EventPayload, _: None = Depends(verify_token)):
            event = payload.event
            self.broadcast_event(event)
            return JSONResponse({"status": "ok"})

        @self.app.post("/qualia")
        async def _process(payload: QualiaPayload, _: None = Depends(verify_token)):
            data = payload.input
            state = payload.internal
            qualia = self.process_input(data, state)
            for client in list(self.clients):
                await client.send_json({"qualia": self._as_list(qualia)})
            return JSONResponse({"qualia": self._as_list(qualia)})

        @self.app.get("/qualia")
        def _get_state(_: None = Depends(verify_token)):
            return JSONResponse({"qualia": self._as_list(self.current_state)})

        @self.app.websocket(QUALIA_WS_PATH)
        async def _ws(ws: WebSocket):
            token = ws.headers.get("Authorization", "")
            scheme, _, token_value = token.partition(" ")
            if scheme.lower() != "bearer" or token_value != API_TOKEN:
                await ws.close(code=1008)
                return
            await ws.accept()
            self.clients.add(ws)
            if self.current_state is not None:
                await ws.send_json({"qualia": self._as_list(self.current_state)})
            try:
                while True:
                    await ws.receive_text()
            except WebSocketDisconnect:
                self.clients.remove(ws)

    # ------------------------------------------------------------------
    def register_module(self, name: str, metadata: Dict[str, Any]) -> None:
        """Registra un módulo junto a sus metadatos."""
        self.modules[name] = metadata

    def get_modules(self) -> Dict[str, Dict[str, Any]]:
        """Devuelve la tabla de módulos registrados."""
        return self.modules

    def broadcast_event(self, event: str) -> None:
        """Difunde un evento a los módulos registrados (solo log)."""
        print(f"Evento difundido: {event}")

    def process_input(
        self, input_data: Sequence[float], internal_state: Sequence[float]
    ) -> Any:
        """Genera y almacena un estado de cualia a partir de los datos."""
        self.current_state = self.engine.generate_state(input_data, internal_state)
        return self.current_state

    def apply_to_behavior(self, agix_agent: Any) -> None:
        """Ajusta el comportamiento del agente según el estado de cualia."""
        if self.current_state is None:
            return
        try:
            mean_val = float(self.engine._lib.mean(self.current_state))
        except Exception:
            mean_val = 0.0
        agix_agent.internal_state["qualia_intensity"] = mean_val
        if hasattr(agix_agent, "feel"):
            try:
                agix_agent.feel("qualia", abs(mean_val), "curiosidad")
            except Exception:
                pass

    # ------------------------------------------------------------------
    def _as_list(self, tensor: Any | None) -> list[float]:
        """Convierte el tensor a lista para ser serializado."""
        if tensor is None:
            return []
        try:
            data = tensor.tolist()  # type: ignore[assignment]
        except Exception:
            data = tensor
        if isinstance(data, list):
            return [float(x) for x in data]
        try:
            return [float(data)]  # type: ignore[arg-type]
        except Exception:
            return []

    def run(self, host: str = "127.0.0.1", port: int = 9000, emotion: bool = False) -> None:
        """Arranca el servidor HTTP de QualiaHub o del EmotionHub."""
        if emotion:
            from .emotion_hub import EmotionHub

            EmotionHub().run(host=host, port=port)
            return
        uvicorn.run(self.app, host=host, port=port)
