#!/usr/bin/env python3
"""
MCPStat - Usage tracking and analytics for MCP servers.
https://github.com/tekkidev/mcpstat

Copyright (c) 2026 Vadim Bakhrenkov
SPDX-License-Identifier: MIT

Extended MCP Server Example - Temperature Converter with Stats & Resources.

Demonstrates:
- Tool usage tracking with mcpstat
- Built-in stats prompt for LLM consumption
- Resource exposure (README.md)
- Metadata presets for tool discovery

Fun fact: -40°C = -40°F (the only temperature where both scales meet!)

Run:
    python example_server.py
"""

import asyncio
import json
import os
from pathlib import Path

from mcp.server import NotificationOptions, Server
from mcp.server.models import InitializationOptions
from mcp.server.stdio import stdio_server
from mcp.types import (
    GetPromptResult,
    Prompt,
    PromptArgument,
    PromptMessage,
    Resource,
    TextContent,
    Tool,
)

from mcpstat import (
    BuiltinToolsHandler,
    MCPStat,
    build_tool_definitions,
    generate_stats_prompt,
)

# =============================================================================
# Setup
# =============================================================================

_HERE = Path(__file__).parent

app = Server("example-server")

# Initialize mcpstat with metadata presets for better tool discovery
stat = MCPStat(
    "example-server",
    db_path=str(_HERE / "example_stats.sqlite"),
    log_path=str(_HERE / "example_stats.log"),
    log_enabled=os.getenv("MCPSTAT_LOG_ENABLED", "false").lower() in ("true", "1", "yes"),
    metadata_presets={
        "celsius_to_fahrenheit": {
            "tags": ["temperature", "conversion", "math"],
            "short": "Convert Celsius to Fahrenheit",
        },
        "fahrenheit_to_celsius": {
            "tags": ["temperature", "conversion", "math"],
            "short": "Convert Fahrenheit to Celsius",
        },
        "get_tool_usage_stats": {
            "tags": ["analytics", "stats", "monitoring"],
            "short": "Get usage statistics for all tools",
        },
        "get_tool_catalog": {
            "tags": ["discovery", "catalog", "search"],
            "short": "Browse tools with tags and search",
        },
    },
)

# Handler for built-in stats tools
stats_handler = BuiltinToolsHandler(stat, prefix="get")

# Path to README.md (one level up from examples/)
README_PATH = _HERE.parent / "README.md"


# =============================================================================
# Tools
# =============================================================================


def _build_tool_list() -> list[Tool]:
    """Build the list of available tools."""
    # Custom tools
    custom_tools = [
        Tool(
            name="celsius_to_fahrenheit",
            description="Convert temperature from Celsius to Fahrenheit",
            inputSchema={
                "type": "object",
                "properties": {
                    "celsius": {"type": "number", "description": "Temperature in Celsius"},
                },
                "required": ["celsius"],
            },
        ),
        Tool(
            name="fahrenheit_to_celsius",
            description="Convert temperature from Fahrenheit to Celsius",
            inputSchema={
                "type": "object",
                "properties": {
                    "fahrenheit": {"type": "number", "description": "Temperature in Fahrenheit"},
                },
                "required": ["fahrenheit"],
            },
        ),
    ]

    # Built-in stats tools from mcpstat
    stats_tool_defs = build_tool_definitions(prefix="get", server_name="example-server")
    stats_tools = [
        Tool(
            name=t["name"],
            description=t["description"],
            inputSchema=t["inputSchema"],
        )
        for t in stats_tool_defs
    ]

    return custom_tools + stats_tools


@app.list_tools()
async def list_tools() -> list[Tool]:
    """List available tools."""
    tools = _build_tool_list()
    await stat.sync_tools(tools)
    return tools


@app.call_tool()
async def handle_tool(name: str, arguments: dict) -> list[TextContent]:
    """Handle tool execution with usage tracking."""
    # Record usage FIRST - guarantees 100% tracking
    await stat.record(name, "tool")

    # Handle built-in stats tools
    if stats_handler.is_stats_tool(name):
        result = await stats_handler.handle(name, arguments)
        return [TextContent(type="text", text=json.dumps(result, indent=2))]

    # Custom tools
    if name == "celsius_to_fahrenheit":
        c = arguments.get("celsius", 0)
        f = (c * 9 / 5) + 32
        return [
            TextContent(
                type="text",
                text=json.dumps({"celsius": c, "fahrenheit": round(f, 2)}),
            )
        ]

    if name == "fahrenheit_to_celsius":
        f = arguments.get("fahrenheit", 0)
        c = (f - 32) * 5 / 9
        return [
            TextContent(
                type="text",
                text=json.dumps({"fahrenheit": f, "celsius": round(c, 2)}),
            )
        ]

    # Unknown tool (still tracked above)
    return [TextContent(type="text", text=json.dumps({"error": f"Unknown tool: {name}"}))]


# =============================================================================
# Resources
# =============================================================================


@app.list_resources()
async def list_resources() -> list[Resource]:
    """List available resources."""
    return [
        Resource(
            uri="resource://example-server/readme",
            name="README.md",
            description="mcpstat package documentation and usage guide",
            mimeType="text/markdown",
        ),
        Resource(
            uri="resource://example-server/tool-catalog",
            name="Tool Catalog",
            description="Tag-indexed tool catalog with usage statistics",
            mimeType="text/markdown",
        ),
    ]


@app.read_resource()
async def read_resource(uri: str) -> str:
    """Read resource content with usage tracking."""
    # Extract resource name for tracking
    resource_name = uri.split("/")[-1] if "/" in uri else uri
    await stat.record(resource_name, "resource")

    if uri == "resource://example-server/readme":
        if README_PATH.exists():
            return README_PATH.read_text(encoding="utf-8")
        return "# README not found\n\nThe README.md file was not found at the expected location."

    if uri == "resource://example-server/tool-catalog":
        catalog = await stat.get_catalog(include_usage=True)
        lines = [
            "# Tool Catalog",
            "",
            f"**Total tools:** {catalog['total_tracked']}",
            f"**Available tags:** {', '.join(catalog.get('all_tags', []))}",
            "",
            "## Tools",
            "",
        ]
        for entry in catalog["results"]:
            tags = ", ".join(entry.get("tags", [])) or "(no tags)"
            calls = entry.get("call_count", 0)
            lines.append(f"### `{entry['name']}`")
            lines.append(entry.get("short_description", ""))
            lines.append(f"- **Tags:** {tags}")
            lines.append(f"- **Calls:** {calls}")
            lines.append("")
        return "\n".join(lines)

    raise ValueError(f"Unknown resource: {uri}")


# =============================================================================
# Prompts
# =============================================================================


@app.list_prompts()
async def list_prompts() -> list[Prompt]:
    """List available prompts."""
    return [
        Prompt(
            name="usage_stats",
            description="Generate MCP usage statistics summary for tools, resources, and prompts",
            arguments=[
                PromptArgument(
                    name="period",
                    description="Time period (e.g., 'today', 'past week', 'all time')",
                    required=False,
                ),
                PromptArgument(
                    name="type",
                    description="Filter by type: 'all', 'tool', 'resource', 'prompt'",
                    required=False,
                ),
            ],
        ),
    ]


@app.get_prompt()
async def get_prompt(name: str, arguments: dict | None = None) -> GetPromptResult:
    """Get prompt with usage tracking."""
    await stat.record(name, "prompt")

    args = arguments or {}

    if name == "usage_stats":
        period = args.get("period", "all time")
        type_filter = args.get("type", "all")

        # Generate stats prompt using mcpstat
        prompt_text = await generate_stats_prompt(
            stat,
            period=period,
            type_filter=type_filter,
            include_recommendations=True,
        )

        return GetPromptResult(
            description=f"MCP usage statistics for {period}",
            messages=[
                PromptMessage(
                    role="user",
                    content=TextContent(type="text", text=prompt_text),
                )
            ],
        )

    raise ValueError(f"Unknown prompt: {name}")


# =============================================================================
# Run
# =============================================================================


async def main():
    """Main entry point."""
    async with stdio_server() as (read, write):
        await app.run(
            read,
            write,
            InitializationOptions(
                server_name="example-server",
                server_version="1.0.0",
                capabilities=app.get_capabilities(
                    notification_options=NotificationOptions(),
                    experimental_capabilities={},
                ),
            ),
        )


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