"""
Hermes Router — Chat-WebSocket, Voice-Endpoints, Setup-Status.

Endpunkte:
  WS   /api/hermes/chat        — Streaming Chat mit dem Hermes Agent
  POST /api/hermes/transcribe  — Audio → Text (Whisper)
  POST /api/hermes/tts         — Text → Audio WAV (Piper)
  GET  /api/hermes/status      — Komponentenstatus (Whisper, Piper, SSH)
  GET  /api/hermes/pubkey      — Bosgame SSH-Public-Key fuer Windows-Setup
"""

import asyncio
import hashlib
import json
import subprocess
import tempfile
from pathlib import Path
from typing import Optional

from fastapi import APIRouter, Depends, UploadFile, File, WebSocket, WebSocketDisconnect
from fastapi.responses import Response, JSONResponse

from auth import auth
from config import (
    PIPER_BIN, PIPER_VOICE, WHISPER_MODEL_SIZE,
    HERMES_WINDOWS_HOST, HERMES_SSH_KEY,
)
from hermes_agent import run_agent

router = APIRouter(prefix="/api")

# ---------------------------------------------------------------------------
# Whisper (lazy-loaded, einmalig in RAM)
# ---------------------------------------------------------------------------

_whisper_model = None
_whisper_loading = False


def _get_whisper():
    global _whisper_model, _whisper_loading
    if _whisper_model is not None:
        return _whisper_model
    if _whisper_loading:
        return None
    _whisper_loading = True
    try:
        from faster_whisper import WhisperModel
        _whisper_model = WhisperModel(WHISPER_MODEL_SIZE, device="cpu", compute_type="int8")
    except Exception:
        _whisper_model = None
    finally:
        _whisper_loading = False
    return _whisper_model


# ---------------------------------------------------------------------------
# TTS-Cache (in-memory, max 64 Eintraege)
# ---------------------------------------------------------------------------

_tts_cache: dict[str, bytes] = {}


def _tts(text: str) -> Optional[bytes]:
    key = hashlib.md5(text.encode()).hexdigest()
    if key in _tts_cache:
        return _tts_cache[key]
    if not PIPER_BIN.exists() or not PIPER_VOICE.exists():
        return None
    with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f:
        wav_path = f.name
    try:
        result = subprocess.run(
            [str(PIPER_BIN), "--model", str(PIPER_VOICE), "--output_file", wav_path],
            input=text, capture_output=True, text=True, timeout=30
        )
        if result.returncode != 0:
            return None
        audio = Path(wav_path).read_bytes()
        if len(_tts_cache) >= 64:
            _tts_cache.pop(next(iter(_tts_cache)))
        _tts_cache[key] = audio
        return audio
    except Exception:
        return None
    finally:
        Path(wav_path).unlink(missing_ok=True)


# ---------------------------------------------------------------------------
# Endpoints
# ---------------------------------------------------------------------------

@router.websocket("/hermes/chat")
async def hermes_chat(websocket: WebSocket):
    """Streaming Chat mit dem Hermes Agent via WebSocket."""
    # Manuelle Token-Auth (WS kann keine HTTP-Header senden)
    from auth import TOKEN
    token_param = websocket.query_params.get("token", "")
    if TOKEN and token_param != TOKEN:
        await websocket.close(code=4001)
        return

    await websocket.accept()
    try:
        while True:
            raw = await websocket.receive_text()
            try:
                payload = json.loads(raw)
                message = payload.get("message", "").strip()
            except Exception:
                message = raw.strip()

            if not message:
                continue

            async def send(obj: dict) -> None:
                await websocket.send_json(obj)

            await run_agent(message, send)

    except WebSocketDisconnect:
        pass
    except Exception as exc:
        try:
            await websocket.send_json({"type": "error", "content": str(exc)})
        except Exception:
            pass


@router.post("/hermes/transcribe", dependencies=[Depends(auth)])
async def transcribe_audio(audio: UploadFile = File(...)):
    """Audio-Datei (webm/ogg/wav) per Whisper transkribieren."""
    model = _get_whisper()
    if model is None:
        return JSONResponse(
            status_code=503,
            content={"error": "Whisper nicht verfuegbar. Bitte faster-whisper installieren."}
        )
    with tempfile.NamedTemporaryFile(suffix=".webm", delete=False) as f:
        tmp = Path(f.name)
        tmp.write_bytes(await audio.read())
    try:
        segments, _ = await asyncio.get_event_loop().run_in_executor(
            None, lambda: model.transcribe(str(tmp), language="de")
        )
        text = " ".join(s.text.strip() for s in segments).strip()
        return {"text": text}
    except Exception as exc:
        return JSONResponse(status_code=500, content={"error": str(exc)})
    finally:
        tmp.unlink(missing_ok=True)


@router.post("/hermes/tts", dependencies=[Depends(auth)])
async def text_to_speech(body: dict):
    """Text zu WAV-Audio per Piper TTS konvertieren."""
    text = (body.get("text") or "").strip()
    if not text:
        return JSONResponse(status_code=400, content={"error": "Kein Text angegeben."})
    audio = await asyncio.get_event_loop().run_in_executor(None, lambda: _tts(text))
    if audio is None:
        return JSONResponse(
            status_code=503,
            content={"error": "Piper TTS nicht verfuegbar. Bitte Piper Binary + Stimme installieren."}
        )
    return Response(content=audio, media_type="audio/wav")


@router.get("/hermes/status", dependencies=[Depends(auth)])
def hermes_status():
    """Status aller Hermes-Komponenten."""
    # Whisper
    try:
        from faster_whisper import WhisperModel  # noqa
        whisper = "ready" if _whisper_model else "installed"
    except ImportError:
        whisper = "not_installed"

    # Piper
    piper = "ready" if (PIPER_BIN.exists() and PIPER_VOICE.exists()) else "not_installed"

    # SSH
    ssh_configured = bool(HERMES_WINDOWS_HOST) and HERMES_SSH_KEY.exists()
    ssh_ok = False
    if ssh_configured:
        try:
            import paramiko
            c = paramiko.SSHClient()
            c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            c.connect(
                HERMES_WINDOWS_HOST,
                username=__import__("config").HERMES_WINDOWS_USER,
                key_filename=str(HERMES_SSH_KEY),
                timeout=3,
            )
            c.close()
            ssh_ok = True
        except Exception:
            ssh_ok = False

    return {
        "whisper":      whisper,
        "piper":        piper,
        "ssh_configured": ssh_configured,
        "ssh_ok":       ssh_ok,
        "windows_host": HERMES_WINDOWS_HOST or None,
    }


@router.get("/hermes/pubkey", dependencies=[Depends(auth)])
def hermes_pubkey():
    """Gibt den SSH-Public-Key des Hermes Agent zurueck (fuer Windows authorized_keys)."""
    pub = Path(str(HERMES_SSH_KEY) + ".pub")
    if not pub.exists():
        return JSONResponse(
            status_code=404,
            content={"error": "Kein SSH-Key gefunden. Bitte auf dem Bosgame generieren: "
                               "ssh-keygen -t ed25519 -C 'hermes-agent@bosgame' "
                               f"-f {HERMES_SSH_KEY} -N ''"}
        )
    return {"pubkey": pub.read_text().strip()}
