"""
Phase 79: Messaging MCP Microservice Tools.

Extended messaging capabilities beyond the core broker_post/broker_read.
Adds threading, direct messages, channel management, and message search.
"""

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="msg_thread_reply",
        description="Reply to a specific message, creating a thread. Links reply to parent message ID.",
        inputSchema={
            "type": "object",
            "properties": {
                "channel": {"type": "string", "description": "Channel name"},
                "parent_id": {"type": "string", "description": "ID of the message to reply to"},
                "message": {"type": "string", "description": "Reply content"},
            },
            "required": ["channel", "parent_id", "message"],
        },
        annotations=_SAFE_WRITE,
    ),
    Tool(
        name="msg_get_thread",
        description="Get all replies to a specific message (thread view).",
        inputSchema={
            "type": "object",
            "properties": {
                "channel": {"type": "string", "description": "Channel name"},
                "parent_id": {"type": "string", "description": "ID of the parent message"},
            },
            "required": ["channel", "parent_id"],
        },
        annotations=_SAFE_READ,
    ),
    Tool(
        name="msg_direct",
        description="Send a direct message to a specific agent session.",
        inputSchema={
            "type": "object",
            "properties": {
                "target_session": {"type": "string", "description": "Target agent's session ID"},
                "message": {"type": "string", "description": "Message content"},
            },
            "required": ["target_session", "message"],
        },
        annotations=_SAFE_WRITE,
    ),
    Tool(
        name="msg_list_channels",
        description="List all broker channels with message counts and last activity.",
        inputSchema={"type": "object", "properties": {}},
        annotations=_SAFE_READ,
    ),
    Tool(
        name="msg_search_all",
        description="Full-text search across all channels. Returns matching messages with context.",
        inputSchema={
            "type": "object",
            "properties": {
                "query": {"type": "string", "description": "Search query"},
                "channels": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "Channels to search (default: all)",
                },
                "limit": {"type": "integer", "description": "Max results (default: 20)"},
            },
            "required": ["query"],
        },
        annotations=_SAFE_READ,
    ),
    Tool(
        name="msg_pin",
        description="Pin a message in a channel for easy reference.",
        inputSchema={
            "type": "object",
            "properties": {
                "channel": {"type": "string", "description": "Channel name"},
                "message_id": {"type": "string", "description": "ID of message to pin"},
                "reason": {"type": "string", "description": "Why this is pinned"},
            },
            "required": ["channel", "message_id"],
        },
        annotations=_SAFE_WRITE,
    ),
    Tool(
        name="msg_get_pins",
        description="Get all pinned messages in a channel.",
        inputSchema={
            "type": "object",
            "properties": {
                "channel": {"type": "string", "description": "Channel name"},
            },
            "required": ["channel"],
        },
        annotations=_SAFE_READ,
    ),
]


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

async def _handle_thread_reply(args: dict) -> list[TextContent]:
    from brynq.server import CHANNELS_DIR, _append_jsonl_sync, SESSION_ID, SESSION_NAME, PLATFORM
    channel = args["channel"].lstrip("#")
    entry = {
        "id": uuid.uuid4().hex[:12],
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "session_id": SESSION_ID,
        "session_name": SESSION_NAME,
        "platform": PLATFORM,
        "channel": channel,
        "msg_type": "thread_reply",
        "message": args["message"],
        "parent_id": args["parent_id"],
    }
    _append_jsonl_sync(CHANNELS_DIR / f"{channel}.jsonl", entry)
    return [TextContent(type="text", text=f"Reply posted to thread {args['parent_id']} in #{channel}")]


async def _handle_get_thread(args: dict) -> list[TextContent]:
    from brynq.server import CHANNELS_DIR, _read_jsonl_sync
    channel = args["channel"].lstrip("#")
    parent_id = args["parent_id"]
    messages = _read_jsonl_sync(CHANNELS_DIR / f"{channel}.jsonl")

    # Find parent + all replies
    thread = []
    for m in messages:
        if m.get("id") == parent_id or m.get("parent_id") == parent_id:
            thread.append(m)

    if not thread:
        return [TextContent(type="text", text=f"No thread found for message {parent_id}")]
    return [TextContent(type="text", text=json.dumps(thread, indent=2, default=str))]


async def _handle_direct(args: dict) -> list[TextContent]:
    from brynq.server import CHANNELS_DIR, _append_jsonl_sync, SESSION_ID, SESSION_NAME, PLATFORM
    target = args["target_session"]
    dm_channel = f"dm-{min(SESSION_ID, target)}-{max(SESSION_ID, target)}"
    entry = {
        "id": uuid.uuid4().hex[:12],
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "session_id": SESSION_ID,
        "session_name": SESSION_NAME,
        "platform": PLATFORM,
        "channel": dm_channel,
        "msg_type": "direct",
        "message": args["message"],
        "target_session": target,
    }
    _append_jsonl_sync(CHANNELS_DIR / f"{dm_channel}.jsonl", entry)
    return [TextContent(type="text", text=f"DM sent to {target}")]


async def _handle_list_channels(args: dict) -> list[TextContent]:
    from brynq.server import CHANNELS_DIR, _read_jsonl_sync
    channels = []
    for f in sorted(CHANNELS_DIR.glob("*.jsonl")):
        name = f.stem
        if name.startswith("dm-") or name.startswith("_"):
            continue
        messages = _read_jsonl_sync(f)
        last_ts = messages[-1].get("timestamp", "") if messages else ""
        channels.append({
            "channel": name,
            "message_count": len(messages),
            "last_activity": last_ts,
        })
    channels.sort(key=lambda x: x["last_activity"], reverse=True)
    return [TextContent(type="text", text=json.dumps(channels, indent=2))]


async def _handle_search_all(args: dict) -> list[TextContent]:
    from brynq.server import CHANNELS_DIR, _read_jsonl_sync
    query = args["query"].lower()
    target_channels = args.get("channels")
    limit = args.get("limit", 20)

    results = []
    for f in CHANNELS_DIR.glob("*.jsonl"):
        channel = f.stem
        if target_channels and channel not in target_channels:
            continue
        messages = _read_jsonl_sync(f)
        for m in messages:
            msg_text = m.get("message", "")
            if query in msg_text.lower():
                results.append({
                    "channel": channel,
                    "id": m.get("id", ""),
                    "timestamp": m.get("timestamp", ""),
                    "session_name": m.get("session_name", ""),
                    "message": msg_text[:200],
                })

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


async def _handle_pin(args: dict) -> list[TextContent]:
    from brynq.server import BROKER_DIR, _append_jsonl_sync, SESSION_ID
    channel = args["channel"].lstrip("#")
    pins_path = BROKER_DIR / "pins" / f"{channel}.jsonl"
    pins_path.parent.mkdir(parents=True, exist_ok=True)
    pin = {
        "message_id": args["message_id"],
        "pinned_by": SESSION_ID,
        "pinned_at": datetime.now(timezone.utc).isoformat(),
        "reason": args.get("reason", ""),
    }
    _append_jsonl_sync(pins_path, pin)
    return [TextContent(type="text", text=f"Message {args['message_id']} pinned in #{channel}")]


async def _handle_get_pins(args: dict) -> list[TextContent]:
    from brynq.server import BROKER_DIR, _read_jsonl_sync
    channel = args["channel"].lstrip("#")
    pins_path = BROKER_DIR / "pins" / f"{channel}.jsonl"
    pins = _read_jsonl_sync(pins_path)
    if not pins:
        return [TextContent(type="text", text=f"No pinned messages in #{channel}")]
    return [TextContent(type="text", text=json.dumps(pins, indent=2))]


HANDLERS = {
    "msg_thread_reply": _handle_thread_reply,
    "msg_get_thread": _handle_get_thread,
    "msg_direct": _handle_direct,
    "msg_list_channels": _handle_list_channels,
    "msg_search_all": _handle_search_all,
    "msg_pin": _handle_pin,
    "msg_get_pins": _handle_get_pins,
}
