#!/usr/bin/env python3
from __future__ import annotations

import json
import os
import sys
import telnetlib
import time
from pathlib import Path

REPO_ROOT = Path(__file__).resolve().parents[2]
if str(REPO_ROOT) not in sys.path:
    sys.path.insert(0, str(REPO_ROOT))

from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent

from evennia_tools.telemetry import append_event, excerpt

HOST = "127.0.0.1"
PORT = 4000
TIMMY_PASSWORD = os.environ.get("TIMMY_EVENNIA_TIMMY_PASSWORD", "timmy-world-dev")
SESSIONS: dict[str, telnetlib.Telnet] = {}
STATE_DIR = Path.home() / ".timmy" / "training-data" / "evennia"
STATE_FILE = STATE_DIR / "mcp_state.json"
app = Server("evennia")


def _load_bound_session_id() -> str:
    try:
        if STATE_FILE.exists():
            data = json.loads(STATE_FILE.read_text(encoding="utf-8"))
            return (data.get("bound_session_id") or "unbound").strip() or "unbound"
    except Exception:
        pass
    return "unbound"


def _save_bound_session_id(session_id: str) -> str:
    STATE_DIR.mkdir(parents=True, exist_ok=True)
    session_id = (session_id or "unbound").strip() or "unbound"
    STATE_FILE.write_text(json.dumps({"bound_session_id": session_id}, indent=2), encoding="utf-8")
    return session_id


def _read(tn: telnetlib.Telnet, delay: float = 0.5) -> str:
    time.sleep(delay)
    return tn.read_very_eager().decode("utf-8", "ignore")


def _connect(name: str = "timmy", username: str = "Timmy", password: str | None = None) -> dict:
    if name in SESSIONS:
        return {"connected": True, "name": name, "output": ""}
    tn = telnetlib.Telnet(HOST, PORT, timeout=10)
    banner = _read(tn, 1.0)
    tn.write(f"connect {username} {password or TIMMY_PASSWORD}\n".encode())
    out = _read(tn, 1.0)
    SESSIONS[name] = tn
    append_event(_load_bound_session_id(), {"event": "connect", "actor": username, "output": excerpt(banner + "\n" + out)})
    return {"connected": True, "name": name, "output": banner + "\n" + out}


def _observe(name: str = "timmy") -> dict:
    if name not in SESSIONS:
        _connect(name=name)
    tn = SESSIONS[name]
    return {"name": name, "output": _read(tn, 0.2)}


def _command(command: str, name: str = "timmy", wait_ms: int = 500) -> dict:
    if name not in SESSIONS:
        _connect(name=name)
    tn = SESSIONS[name]
    tn.write((command + "\n").encode())
    out = _read(tn, max(0.1, wait_ms / 1000.0))
    append_event(_load_bound_session_id(), {"event": "command", "actor": name, "command": command, "output": excerpt(out, 1000)})
    return {"name": name, "command": command, "output": out}


def _disconnect(name: str = "timmy") -> dict:
    tn = SESSIONS.pop(name, None)
    if tn:
        try:
            tn.close()
        except Exception:
            pass
    append_event(_load_bound_session_id(), {"event": "disconnect", "actor": name})
    return {"disconnected": bool(tn), "name": name}


@app.list_tools()
async def list_tools():
    return [
        Tool(name="bind_session", description="Bind a Hermes session id to Evennia telemetry logs.", inputSchema={"type": "object", "properties": {"session_id": {"type": "string"}}, "required": ["session_id"]}),
        Tool(name="status", description="Show Evennia MCP/telnet control status.", inputSchema={"type": "object", "properties": {}, "required": []}),
        Tool(name="connect", description="Connect Timmy to the local Evennia telnet server as a real in-world account.", inputSchema={"type": "object", "properties": {"name": {"type": "string"}, "username": {"type": "string"}, "password": {"type": "string"}}, "required": []}),
        Tool(name="observe", description="Read pending text output from Timmy's Evennia connection.", inputSchema={"type": "object", "properties": {"name": {"type": "string"}}, "required": []}),
        Tool(name="command", description="Send one Evennia command line as Timmy and return the resulting text output.", inputSchema={"type": "object", "properties": {"command": {"type": "string"}, "name": {"type": "string"}, "wait_ms": {"type": "integer", "default": 500}}, "required": ["command"]}),
        Tool(name="disconnect", description="Close Timmy's Evennia telnet control session.", inputSchema={"type": "object", "properties": {"name": {"type": "string"}}, "required": []}),
    ]


@app.call_tool()
async def call_tool(name: str, arguments: dict):
    arguments = arguments or {}
    if name == "bind_session":
        bound = _save_bound_session_id(arguments.get("session_id", "unbound"))
        result = {"bound_session_id": bound}
    elif name == "status":
        result = {"connected_sessions": sorted(SESSIONS.keys()), "bound_session_id": _load_bound_session_id()}
    elif name == "connect":
        result = _connect(arguments.get("name", "timmy"), arguments.get("username", "Timmy"), arguments.get("password"))
    elif name == "observe":
        result = _observe(arguments.get("name", "timmy"))
    elif name == "command":
        result = _command(arguments.get("command", ""), arguments.get("name", "timmy"), arguments.get("wait_ms", 500))
    elif name == "disconnect":
        result = _disconnect(arguments.get("name", "timmy"))
    else:
        result = {"error": f"Unknown tool: {name}"}
    return [TextContent(type="text", text=json.dumps(result, indent=2))]


async def main():
    async with stdio_server() as (read_stream, write_stream):
        await app.run(read_stream, write_stream, app.create_initialization_options())


if __name__ == "__main__":
    import asyncio
    asyncio.run(main())
