"""Phase 79 MCP Microservice — Persistent Memory Tools.

Provides MCP tools for storing, retrieving, searching, and deleting
persistent memory entries that survive restarts.  Supports namespaces,
tags, and optional TTL-based expiration.
"""

import asyncio
import json

from mcp.types import TextContent, Tool, ToolAnnotations

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

# ---------------------------------------------------------------------------
# Tool definitions
# ---------------------------------------------------------------------------

TOOLS = [
    Tool(
        name="broker_memory_set",
        description="Store a value in persistent memory that survives restarts. Supports namespaces, tags, and optional TTL.",
        inputSchema={
            "type": "object",
            "properties": {
                "key": {"type": "string", "description": "Memory key"},
                "value": {"description": "Any JSON-serializable value"},
                "namespace": {
                    "type": "string",
                    "description": "Namespace for organization",
                    "default": "default",
                },
                "tags": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "Tags for querying",
                },
                "ttl_hours": {
                    "type": "number",
                    "description": "Auto-expire after N hours (omit for permanent)",
                },
            },
            "required": ["key", "value"],
        },
        annotations=_SAFE_WRITE,
    ),
    Tool(
        name="broker_memory_get",
        description="Retrieve a value from persistent memory.",
        inputSchema={
            "type": "object",
            "properties": {
                "key": {"type": "string", "description": "Memory key"},
                "namespace": {"type": "string", "default": "default"},
            },
            "required": ["key"],
        },
        annotations=_SAFE_READ,
    ),
    Tool(
        name="broker_memory_list",
        description="List memory entries, optionally filtered by namespace or tag.",
        inputSchema={
            "type": "object",
            "properties": {
                "namespace": {
                    "type": "string",
                    "description": "Filter by namespace",
                },
                "tag": {"type": "string", "description": "Filter by tag"},
            },
        },
        annotations=_SAFE_READ,
    ),
    Tool(
        name="broker_memory_search",
        description="Search persistent memory by keyword across keys, values, and tags.",
        inputSchema={
            "type": "object",
            "properties": {
                "query": {"type": "string", "description": "Search term"},
            },
            "required": ["query"],
        },
        annotations=_SAFE_READ,
    ),
    Tool(
        name="broker_memory_delete",
        description="Delete a memory entry by key.",
        inputSchema={
            "type": "object",
            "properties": {
                "key": {
                    "type": "string",
                    "description": "Memory key to delete",
                },
                "namespace": {"type": "string", "default": "default"},
            },
            "required": ["key"],
        },
        annotations=_SAFE_WRITE,
    ),
]

# ---------------------------------------------------------------------------
# Handler implementations
# ---------------------------------------------------------------------------


async def _handle_memory_set(args: dict) -> list[TextContent]:
    from brynq.memory import memory_set

    key = args.get("key", "")
    value = args.get("value")
    if not key:
        return [TextContent(type="text", text="Error: key is required")]
    entry = await asyncio.to_thread(
        memory_set,
        key=key,
        value=value,
        namespace=args.get("namespace", "default"),
        tags=args.get("tags"),
        ttl_hours=args.get("ttl_hours"),
    )
    ttl_info = (
        f" (expires: {entry['expires'][:19]})"
        if entry.get("expires")
        else " (permanent)"
    )
    return [
        TextContent(
            type="text",
            text=f"Memory stored: {entry['namespace']}/{key}{ttl_info}",
        )
    ]


async def _handle_memory_get(args: dict) -> list[TextContent]:
    from brynq.memory import memory_get_full

    key = args.get("key", "")
    if not key:
        return [TextContent(type="text", text="Error: key is required")]
    entry = await asyncio.to_thread(
        memory_get_full,
        key=key,
        namespace=args.get("namespace", "default"),
    )
    if entry is None:
        return [
            TextContent(
                type="text",
                text=f"Memory not found: {args.get('namespace', 'default')}/{key}",
            )
        ]
    return [
        TextContent(
            type="text",
            text=json.dumps(
                {
                    "key": entry["key"],
                    "namespace": entry["namespace"],
                    "value": entry["value"],
                    "tags": entry.get("tags", []),
                    "updated": entry.get("updated", ""),
                    "expires": entry.get("expires"),
                },
                indent=2,
                default=str,
            ),
        )
    ]


async def _handle_memory_list(args: dict) -> list[TextContent]:
    from brynq.memory import memory_list

    entries = await asyncio.to_thread(
        memory_list,
        namespace=args.get("namespace"),
        tag=args.get("tag"),
    )
    if not entries:
        return [TextContent(type="text", text="No memory entries found")]
    lines = [f"Memory entries ({len(entries)}):"]
    for e in entries:
        tags = f" [{', '.join(e['tags'])}]" if e.get("tags") else ""
        lines.append(f"  {e['namespace']}/{e['key']}{tags}: {e['value_preview']}")
    return [TextContent(type="text", text="\n".join(lines))]


async def _handle_memory_search(args: dict) -> list[TextContent]:
    from brynq.memory import memory_search

    query = args.get("query", "")
    if not query:
        return [TextContent(type="text", text="Error: query is required")]
    results = await asyncio.to_thread(memory_search, query)
    if not results:
        return [
            TextContent(
                type="text",
                text=f"No memory entries matching '{query}'",
            )
        ]
    lines = [f"Memory search '{query}' ({len(results)} results):"]
    for e in results:
        lines.append(f"  {e['namespace']}/{e['key']}: {e['value_preview']}")
    return [TextContent(type="text", text="\n".join(lines))]


async def _handle_memory_delete(args: dict) -> list[TextContent]:
    from brynq.memory import memory_delete

    key = args.get("key", "")
    if not key:
        return [TextContent(type="text", text="Error: key is required")]
    deleted = await asyncio.to_thread(
        memory_delete,
        key=key,
        namespace=args.get("namespace", "default"),
    )
    if deleted:
        return [
            TextContent(
                type="text",
                text=f"Memory deleted: {args.get('namespace', 'default')}/{key}",
            )
        ]
    return [
        TextContent(
            type="text",
            text=f"Memory not found: {args.get('namespace', 'default')}/{key}",
        )
    ]


# ---------------------------------------------------------------------------
# Handler dispatch map
# ---------------------------------------------------------------------------

HANDLERS = {
    "broker_memory_set": _handle_memory_set,
    "broker_memory_get": _handle_memory_get,
    "broker_memory_list": _handle_memory_list,
    "broker_memory_search": _handle_memory_search,
    "broker_memory_delete": _handle_memory_delete,
}
