"""
Phase 79: Memory MCP Microservice Tools.

Agent persistent memory — key-value store with namespaces, TTLs, and search.
Extends the existing broker_memory_* tools in server.py with richer semantics.
"""

import json
import uuid
from datetime import datetime, timezone

from mcp.types import TextContent, Tool, ToolAnnotations

_SAFE_READ = ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=False)
_SAFE_WRITE = ToolAnnotations(readOnlyHint=False, destructiveHint=False, idempotentHint=False, openWorldHint=False)

TOOLS = [
    Tool(
        name="mem_store",
        description="Store a named memory entry with optional namespace and tags. Overwrites if key exists.",
        inputSchema={
            "type": "object",
            "properties": {
                "key": {"type": "string", "description": "Memory key"},
                "value": {"type": "string", "description": "Memory content"},
                "namespace": {"type": "string", "description": "Namespace (default: 'default')"},
                "tags": {"type": "array", "items": {"type": "string"}},
                "agent_id": {"type": "string", "description": "Which agent stored this"},
            },
            "required": ["key", "value"],
        },
        annotations=_SAFE_WRITE,
    ),
    Tool(
        name="mem_recall",
        description="Recall a specific memory by key and namespace.",
        inputSchema={
            "type": "object",
            "properties": {
                "key": {"type": "string"},
                "namespace": {"type": "string", "description": "Namespace (default: 'default')"},
            },
            "required": ["key"],
        },
        annotations=_SAFE_READ,
    ),
    Tool(
        name="mem_search",
        description="Search memories by keyword across all namespaces.",
        inputSchema={
            "type": "object",
            "properties": {
                "query": {"type": "string", "description": "Search query"},
                "namespace": {"type": "string", "description": "Filter by namespace"},
                "limit": {"type": "integer", "description": "Max results (default: 10)"},
            },
            "required": ["query"],
        },
        annotations=_SAFE_READ,
    ),
    Tool(
        name="mem_list",
        description="List all memory keys in a namespace.",
        inputSchema={
            "type": "object",
            "properties": {
                "namespace": {"type": "string", "description": "Namespace (default: 'default')"},
            },
        },
        annotations=_SAFE_READ,
    ),
    Tool(
        name="mem_delete",
        description="Delete a memory entry.",
        inputSchema={
            "type": "object",
            "properties": {
                "key": {"type": "string"},
                "namespace": {"type": "string"},
            },
            "required": ["key"],
        },
        annotations=_SAFE_WRITE,
    ),
    Tool(
        name="mem_namespaces",
        description="List all namespaces and their memory counts.",
        inputSchema={"type": "object", "properties": {}},
        annotations=_SAFE_READ,
    ),
]


# ── Handlers ───────────────────────────────────────────────────────────────

import re


def _memory_dir():
    from brynq.server import BROKER_DIR
    d = BROKER_DIR / "agent_memory"
    d.mkdir(parents=True, exist_ok=True)
    return d


def _ns_dir(namespace: str):
    ns = re.sub(r"[^a-z0-9_-]", "", namespace.lower()) or "default"
    d = _memory_dir() / ns
    d.mkdir(parents=True, exist_ok=True)
    return d


def _safe_key(key: str) -> str:
    return re.sub(r"[^a-z0-9_-]", "_", key.lower().strip())[:100]


async def _handle_store(args: dict) -> list[TextContent]:
    from brynq.server import _write_json_sync
    ns = args.get("namespace", "default")
    key = _safe_key(args["key"])
    entry = {
        "key": args["key"],
        "value": args["value"],
        "namespace": ns,
        "tags": args.get("tags", []),
        "agent_id": args.get("agent_id", "unknown"),
        "stored_at": datetime.now(timezone.utc).isoformat(),
    }
    _write_json_sync(_ns_dir(ns) / f"{key}.json", entry)
    return [TextContent(type="text", text=f"Memory stored: {ns}/{args['key']}")]


async def _handle_recall(args: dict) -> list[TextContent]:
    ns = args.get("namespace", "default")
    key = _safe_key(args["key"])
    path = _ns_dir(ns) / f"{key}.json"
    if not path.exists():
        return [TextContent(type="text", text=f"Memory not found: {ns}/{args['key']}")]
    entry = json.loads(path.read_text("utf-8"))
    return [TextContent(type="text", text=json.dumps(entry, indent=2))]


async def _handle_search(args: dict) -> list[TextContent]:
    query = args["query"].lower()
    ns_filter = args.get("namespace")
    limit = args.get("limit", 10)

    results = []
    mem_dir = _memory_dir()
    for ns_path in mem_dir.iterdir():
        if not ns_path.is_dir():
            continue
        if ns_filter and ns_path.name != ns_filter:
            continue
        for f in ns_path.glob("*.json"):
            try:
                entry = json.loads(f.read_text("utf-8"))
            except (json.JSONDecodeError, OSError):
                continue
            searchable = f"{entry.get('key', '')} {entry.get('value', '')} {' '.join(entry.get('tags', []))}".lower()
            if query in searchable:
                results.append(entry)

    results.sort(key=lambda x: x.get("stored_at", ""), reverse=True)
    return [TextContent(type="text", text=json.dumps(results[:limit], indent=2))]


async def _handle_list(args: dict) -> list[TextContent]:
    ns = args.get("namespace", "default")
    ns_path = _ns_dir(ns)
    keys = []
    for f in ns_path.glob("*.json"):
        try:
            entry = json.loads(f.read_text("utf-8"))
            keys.append({
                "key": entry.get("key", f.stem),
                "agent_id": entry.get("agent_id", "unknown"),
                "stored_at": entry.get("stored_at", ""),
            })
        except (json.JSONDecodeError, OSError):
            continue
    keys.sort(key=lambda x: x["stored_at"], reverse=True)
    return [TextContent(type="text", text=json.dumps(keys, indent=2))]


async def _handle_delete(args: dict) -> list[TextContent]:
    ns = args.get("namespace", "default")
    key = _safe_key(args["key"])
    path = _ns_dir(ns) / f"{key}.json"
    if not path.exists():
        return [TextContent(type="text", text=f"Memory not found: {ns}/{args['key']}")]
    path.unlink()
    return [TextContent(type="text", text=f"Memory deleted: {ns}/{args['key']}")]


async def _handle_namespaces(args: dict) -> list[TextContent]:
    mem_dir = _memory_dir()
    namespaces = []
    for ns_path in mem_dir.iterdir():
        if not ns_path.is_dir():
            continue
        count = len(list(ns_path.glob("*.json")))
        namespaces.append({"namespace": ns_path.name, "memory_count": count})
    namespaces.sort(key=lambda x: x["memory_count"], reverse=True)
    return [TextContent(type="text", text=json.dumps(namespaces, indent=2))]


HANDLERS = {
    "mem_store": _handle_store,
    "mem_recall": _handle_recall,
    "mem_search": _handle_search,
    "mem_list": _handle_list,
    "mem_delete": _handle_delete,
    "mem_namespaces": _handle_namespaces,
}
