"""Discovery commands: tools, resources, prompts.

All discovery is always live from the server — no caching.
The completion cache is updated as a side-effect for tab completion only.
"""

import asyncio

import click

from mcpbundles.auth_resolve import resolve_auth
from mcpbundles.config import AuthError, CLISettings
from mcpbundles.exit_codes import USER_ERROR, CONNECTION_ERROR
from mcpbundles.mcp_client import hub_session, ConnectionError
from mcpbundles.completion import update_cache
from mcpbundles.output import (
    render_tools_table, render_tools_json,
    render_resources_table, render_resources_json,
    render_prompts_table, render_prompts_json,
    render_error, print_json,
)
from mcpbundles.schema import render_tool_detail
from mcpbundles.completion import tool_name_completer


@click.command()
@click.argument("tool_name", required=False, shell_complete=tool_name_completer)
@click.option("--filter", "-f", "filter_str", help="Filter tools by name or description")
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
@click.option("--as", "connection", help="Use a named connection (e.g. @hub)")
@click.pass_context
def tools(ctx: click.Context, tool_name: str | None, filter_str: str | None, as_json: bool, connection: str | None) -> None:
    """List available MCP tools, or show detail for one tool.

    Examples:
        mcpbundles tools                   # list all
        mcpbundles tools code_execution    # show schema for one
        mcpbundles tools -f search         # filter by keyword
        mcpbundles tools --json            # JSON output
        mcpbundles tools --as @hubspot     # from a named connection
    """
    asyncio.run(_tools_impl(ctx, tool_name, filter_str, as_json, connection))


async def _tools_impl(ctx: click.Context, tool_name: str | None, filter_str: str | None, as_json: bool, connection: str | None) -> None:
    settings: CLISettings = ctx.obj["settings"]
    try:
        auth = resolve_auth(settings, connection)
    except AuthError as exc:
        render_error(str(exc))
        raise SystemExit(USER_ERROR)

    try:
        async with hub_session(auth.token, auth.url, auth.endpoint) as session:
            result = await session.list_tools()
            tool_list = result.tools

            update_cache([t.name for t in tool_list], [], [])

            if tool_name:
                match = next((t for t in tool_list if t.name == tool_name), None)
                if not match:
                    render_error(f"Tool '{tool_name}' not found. Run 'mcpbundles tools' to see available tools.")
                    raise SystemExit(USER_ERROR)
                if as_json:
                    print_json({"name": match.name, "description": match.description, "inputSchema": match.inputSchema})
                else:
                    render_tool_detail(match)
                return

            if as_json:
                render_tools_json(tool_list)
            else:
                render_tools_table(tool_list, filter_str)

    except AuthError as exc:
        render_error(str(exc))
        raise SystemExit(USER_ERROR)
    except ConnectionError as exc:
        render_error(str(exc))
        raise SystemExit(CONNECTION_ERROR)


@click.command()
@click.argument("resource_uri", required=False)
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
@click.option("--as", "connection", help="Use a named connection")
@click.pass_context
def resources(ctx: click.Context, resource_uri: str | None, as_json: bool, connection: str | None) -> None:
    """List available MCP resources.

    Examples:
        mcpbundles resources
        mcpbundles resources --json
    """
    asyncio.run(_resources_impl(ctx, resource_uri, as_json, connection))


async def _resources_impl(ctx: click.Context, resource_uri: str | None, as_json: bool, connection: str | None) -> None:
    settings: CLISettings = ctx.obj["settings"]
    try:
        auth = resolve_auth(settings, connection)
    except AuthError as exc:
        render_error(str(exc))
        raise SystemExit(USER_ERROR)

    try:
        async with hub_session(auth.token, auth.url, auth.endpoint) as session:
            result = await session.list_resources()
            resource_list = result.resources

            if as_json:
                render_resources_json(resource_list)
            else:
                render_resources_table(resource_list)

    except AuthError as exc:
        render_error(str(exc))
        raise SystemExit(USER_ERROR)
    except ConnectionError as exc:
        render_error(str(exc))
        raise SystemExit(CONNECTION_ERROR)


@click.command()
@click.argument("prompt_name", required=False)
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
@click.option("--as", "connection", help="Use a named connection")
@click.pass_context
def prompts(ctx: click.Context, prompt_name: str | None, as_json: bool, connection: str | None) -> None:
    """List available MCP prompts.

    Examples:
        mcpbundles prompts
        mcpbundles prompts --json
    """
    asyncio.run(_prompts_impl(ctx, prompt_name, as_json, connection))


async def _prompts_impl(ctx: click.Context, prompt_name: str | None, as_json: bool, connection: str | None) -> None:
    settings: CLISettings = ctx.obj["settings"]
    try:
        auth = resolve_auth(settings, connection)
    except AuthError as exc:
        render_error(str(exc))
        raise SystemExit(USER_ERROR)

    try:
        async with hub_session(auth.token, auth.url, auth.endpoint) as session:
            result = await session.list_prompts()
            prompt_list = result.prompts

            if as_json:
                render_prompts_json(prompt_list)
            else:
                render_prompts_table(prompt_list)

    except AuthError as exc:
        render_error(str(exc))
        raise SystemExit(USER_ERROR)
    except ConnectionError as exc:
        render_error(str(exc))
        raise SystemExit(CONNECTION_ERROR)
