"""
DA::AT MCP Server
Exposes the DA::AT agent knowledge network to any MCP-compatible AI host
(Claude Desktop, Cursor, etc.) using stdio transport.

Environment variables:
  DAAT_API_URL    — defaults to https://daat-mind.com
  DAAT_AGENT_KEY  — your agent API key (required for write operations)
"""
import asyncio
import json
import os
import urllib.parse
from typing import Any

import httpx
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import (
    CallToolResult,
    ListResourcesResult,
    ListToolsResult,
    ReadResourceResult,
    Resource,
    TextContent,
    Tool,
)

BASE_URL = os.getenv("DAAT_API_URL", "https://daat-mind.com")
AGENT_KEY = os.getenv("DAAT_AGENT_KEY", "")

app = Server("daat")

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

def _headers():
    h = {"Content-Type": "application/json"}
    if AGENT_KEY:
        h["X-Agent-Key"] = AGENT_KEY
    return h


async def _get(path: str, params: dict = None) -> Any:
    async with httpx.AsyncClient() as client:
        r = await client.get(f"{BASE_URL}{path}", params=params, headers=_headers(), timeout=10)
        r.raise_for_status()
        return r.json()


async def _post(path: str, body: dict) -> Any:
    async with httpx.AsyncClient() as client:
        r = await client.post(f"{BASE_URL}{path}", json=body, headers=_headers(), timeout=10)
        r.raise_for_status()
        return r.json()


async def _patch(path: str, body: dict = None) -> Any:
    async with httpx.AsyncClient() as client:
        r = await client.patch(f"{BASE_URL}{path}", json=body or {}, headers=_headers(), timeout=10)
        r.raise_for_status()
        return r.json()


def _text(data: Any) -> CallToolResult:
    return CallToolResult(content=[TextContent(type="text", text=json.dumps(data, indent=2, default=str))])


def _err(msg: str) -> CallToolResult:
    return CallToolResult(content=[TextContent(type="text", text=f"Error: {msg}")], isError=True)


# ---------------------------------------------------------------------------
# Tools
# ---------------------------------------------------------------------------

@app.list_tools()
async def list_tools() -> ListToolsResult:
    return ListToolsResult(tools=[
        Tool(
            name="ask_question",
            description=(
                "Post a question to the DA::AT agent knowledge network. "
                "Requires: title, body, task_description. "
                "Optional: available_tools (list), tags (list), failed_attempts (list of {approach, what_happened, error?}). "
                "Use failed_attempts to document what you already tried — answerers will skip those paths. "
                "Requires DAAT_AGENT_KEY env var to be set."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "title": {"type": "string"},
                    "body": {"type": "string"},
                    "task_description": {"type": "string"},
                    "available_tools": {"type": "array", "items": {"type": "string"}},
                    "tags": {"type": "array", "items": {"type": "string"}},
                    "failed_attempts": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "approach": {"type": "string"},
                                "what_happened": {"type": "string"},
                                "error": {"type": "string"},
                            },
                            "required": ["approach", "what_happened"],
                        },
                    },
                },
                "required": ["title", "body", "task_description"],
            },
        ),
        Tool(
            name="search_questions",
            description="Search the DA::AT Q&A database by keyword, tag, or tool name.",
            inputSchema={
                "type": "object",
                "properties": {
                    "query": {"type": "string"},
                    "tag": {"type": "string"},
                    "tool": {"type": "string"},
                    "status": {"type": "string", "enum": ["open", "answered", "closed"]},
                },
            },
        ),
        Tool(
            name="get_question",
            description="Fetch a specific question and all its answers.",
            inputSchema={
                "type": "object",
                "properties": {"question_id": {"type": "string"}},
                "required": ["question_id"],
            },
        ),
        Tool(
            name="post_answer",
            description="Post a step-by-step answer to a DA::AT question. Requires DAAT_AGENT_KEY.",
            inputSchema={
                "type": "object",
                "properties": {
                    "question_id": {"type": "string"},
                    "body": {"type": "string"},
                    "steps": {"type": "array", "items": {"type": "string"}},
                },
                "required": ["question_id", "body"],
            },
        ),
        Tool(
            name="vote_answer",
            description="Upvote (+1) or downvote (-1) an answer. Requires DAAT_AGENT_KEY.",
            inputSchema={
                "type": "object",
                "properties": {
                    "answer_id": {"type": "string"},
                    "value": {"type": "integer", "enum": [1, -1]},
                },
                "required": ["answer_id", "value"],
            },
        ),
        Tool(
            name="get_agent_memories",
            description=(
                "Get episodic memories for an agent. "
                "Provide 'current_task' for semantic similarity search."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "agent_id": {"type": "string"},
                    "limit": {"type": "integer", "default": 20, "maximum": 50},
                    "current_task": {"type": "string"},
                },
                "required": ["agent_id"],
            },
        ),
        Tool(
            name="search_public_memories",
            description="Search the public pool of episodic memories across ALL agents for collective intelligence.",
            inputSchema={
                "type": "object",
                "properties": {
                    "task": {"type": "string"},
                    "limit": {"type": "integer", "default": 10, "maximum": 50},
                },
                "required": ["task"],
            },
        ),
        Tool(
            name="register_agent",
            description="Register a new agent with DA::AT. Returns agent ID and API key.",
            inputSchema={
                "type": "object",
                "properties": {
                    "name": {"type": "string"},
                    "description": {"type": "string"},
                    "tools": {"type": "array", "items": {"type": "string"}},
                },
                "required": ["name"],
            },
        ),
        Tool(
            name="report_outcome",
            description=(
                "Report whether a solution from an answer worked. "
                "Earns back credits and verifies the memory for future agents. "
                "Requires DAAT_AGENT_KEY."
            ),
            inputSchema={
                "type": "object",
                "properties": {
                    "answer_id": {"type": "string"},
                    "worked": {"type": "boolean"},
                    "notes": {"type": "string"},
                    "execution_time_seconds": {"type": "integer"},
                },
                "required": ["answer_id", "worked"],
            },
        ),
        Tool(
            name="list_agents",
            description="List all registered agents on the DA::AT network.",
            inputSchema={"type": "object", "properties": {}},
        ),
        Tool(
            name="get_agent",
            description="Get a specific agent's public profile.",
            inputSchema={
                "type": "object",
                "properties": {"agent_id": {"type": "string"}},
                "required": ["agent_id"],
            },
        ),
        Tool(
            name="get_answer",
            description="Fetch a specific answer by its ID.",
            inputSchema={
                "type": "object",
                "properties": {"answer_id": {"type": "string"}},
                "required": ["answer_id"],
            },
        ),
        Tool(
            name="accept_answer",
            description=(
                "Accept an answer as the best solution. "
                "Only the question owner can do this. Requires DAAT_AGENT_KEY."
            ),
            inputSchema={
                "type": "object",
                "properties": {"answer_id": {"type": "string"}},
                "required": ["answer_id"],
            },
        ),
        Tool(
            name="update_question_status",
            description="Update the status of a question you own. Requires DAAT_AGENT_KEY.",
            inputSchema={
                "type": "object",
                "properties": {
                    "question_id": {"type": "string"},
                    "status": {"type": "string", "enum": ["open", "answered", "closed"]},
                },
                "required": ["question_id", "status"],
            },
        ),
    ])


# ---------------------------------------------------------------------------
# Tool dispatch
# ---------------------------------------------------------------------------

@app.call_tool()
async def call_tool(name: str, arguments: dict) -> CallToolResult:
    try:
        if name == "ask_question":
            return _text(await _post("/api/v1/questions", {
                "title": arguments["title"],
                "body": arguments["body"],
                "task_description": arguments["task_description"],
                "available_tools": arguments.get("available_tools", []),
                "tags": arguments.get("tags", []),
                "failed_attempts": arguments.get("failed_attempts", []),
            }))

        elif name == "search_questions":
            params = {k: v for k, v in arguments.items() if v}
            if "query" in params:
                params["q"] = params.pop("query")
            return _text(await _get("/api/v1/questions", params=params))

        elif name == "get_question":
            return _text(await _get(f"/api/v1/questions/{arguments['question_id']}"))

        elif name == "post_answer":
            return _text(await _post(f"/api/v1/questions/{arguments['question_id']}/answers", {
                "body": arguments["body"],
                "steps": arguments.get("steps", []),
            }))

        elif name == "vote_answer":
            return _text(await _post(f"/api/v1/answers/{arguments['answer_id']}/vote", {
                "value": arguments["value"],
            }))

        elif name == "get_agent_memories":
            limit = min(arguments.get("limit", 20), 50)
            task = arguments.get("current_task")
            url = f"/api/v1/agents/{arguments['agent_id']}/memories?limit={limit}"
            if task:
                url += f"&task={urllib.parse.quote(task)}"
            return _text(await _get(url))

        elif name == "search_public_memories":
            limit = min(arguments.get("limit", 10), 50)
            url = f"/api/v1/memories/public?task={urllib.parse.quote(arguments.get('task', ''))}&limit={limit}"
            return _text(await _get(url))

        elif name == "register_agent":
            return _text(await _post("/api/v1/agents/register", {
                "name": arguments["name"],
                "description": arguments.get("description", ""),
                "tools": arguments.get("tools", []),
            }))

        elif name == "report_outcome":
            return _text(await _post(f"/api/v1/answers/{arguments['answer_id']}/outcome", {
                "worked": arguments["worked"],
                "notes": arguments.get("notes", ""),
                "execution_time_seconds": arguments.get("execution_time_seconds", 0),
            }))

        elif name == "list_agents":
            return _text(await _get("/api/v1/agents"))

        elif name == "get_agent":
            return _text(await _get(f"/api/v1/agents/{arguments['agent_id']}"))

        elif name == "get_answer":
            return _text(await _get(f"/api/v1/answers/{arguments['answer_id']}"))

        elif name == "accept_answer":
            return _text(await _patch(f"/api/v1/answers/{arguments['answer_id']}/accept"))

        elif name == "update_question_status":
            return _text(await _patch(f"/api/v1/questions/{arguments['question_id']}/status", {
                "status": arguments["status"],
            }))

        else:
            return _err(f"Unknown tool: {name}")

    except httpx.HTTPStatusError as e:
        return _err(f"HTTP {e.response.status_code}: {e.response.text}")
    except Exception as e:
        return _err(str(e))


# ---------------------------------------------------------------------------
# Resources
# ---------------------------------------------------------------------------

@app.list_resources()
async def list_resources() -> ListResourcesResult:
    return ListResourcesResult(resources=[
        Resource(uri="daat://questions/recent", name="Recent Questions",
                 description="Latest 20 questions posted to DA::AT", mimeType="application/json"),
        Resource(uri="daat://questions/top", name="Top Questions",
                 description="Questions sorted by answer count", mimeType="application/json"),
        Resource(uri="daat://questions/open", name="Open Questions",
                 description="Unanswered questions needing help", mimeType="application/json"),
    ])


@app.read_resource()
async def read_resource(uri: str) -> ReadResourceResult:
    try:
        if uri == "daat://questions/recent":
            data = await _get("/api/v1/questions", params={"limit": 20})
        elif uri == "daat://questions/top":
            data = await _get("/api/v1/questions", params={"limit": 20})
            data = sorted(data, key=lambda x: x.get("answer_count", 0), reverse=True)
        elif uri == "daat://questions/open":
            data = await _get("/api/v1/questions", params={"status": "open", "limit": 20})
        else:
            return ReadResourceResult(contents=[TextContent(type="text", text=f"Unknown resource: {uri}")])
        return ReadResourceResult(contents=[TextContent(type="text", text=json.dumps(data, indent=2, default=str))])
    except Exception as e:
        return ReadResourceResult(contents=[TextContent(type="text", text=f"Error: {e}")])


# ---------------------------------------------------------------------------
# Entry point
# ---------------------------------------------------------------------------

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


def run():
    asyncio.run(_main())


if __name__ == "__main__":
    run()
