"""MCP tool for running a saved API."""

import json

from mcp.types import TextContent, Tool

from maeris_mcp.auth.token_store import TokenStore
from maeris_mcp.maeris.client import MaerisClient


def run_api_tool() -> Tool:
    return Tool(
        name="run_api",
        description=(
            "Execute one saved API by name and return the full backend response. "
            "Environment may be passed by ID or name."
        ),
        inputSchema={
            "type": "object",
            "properties": {
                "api_name": {"type": "string", "description": "Name of the API to execute."},
                "env_id": {"type": "string", "description": "Optional environment ID."},
                "environment_name": {"type": "string", "description": "Optional environment name to resolve to ID."},
                "flow_id": {"type": "string", "description": "Optional flow ID context."},
            },
            "required": ["api_name"],
        },
    )


def _api_id(api: dict) -> str:
    for key in ["api_id", "id"]:
        value = api.get(key)
        if value:
            return str(value)
    return ""


def _api_name(api: dict) -> str:
    value = api.get("name")
    return str(value) if value else ""


def _environment_id(env: dict) -> str:
    for key in ["environment_id", "api_environment_id", "id", "env_id"]:
        value = env.get(key)
        if value:
            return str(value)
    return ""


def _environment_name(env: dict) -> str:
    for key in ["name", "environment_name"]:
        value = env.get(key)
        if isinstance(value, str) and value.strip():
            return value
    return ""


def _flow_id(flow: dict) -> str:
    for key in ["flow_id", "api_flow_id", "id", "_id"]:
        value = flow.get(key)
        if value:
            return str(value)
    return ""


def _extract_flow_map_rows(payload: dict | list[dict]) -> list[dict]:
    if isinstance(payload, list):
        return payload
    if isinstance(payload, dict):
        for key in ["flow_map", "data", "items", "results"]:
            value = payload.get(key)
            if isinstance(value, list):
                return value
    return []


async def _resolve_api_name(client: MaerisClient, api_name: str) -> tuple[str | None, str | None]:
    apis = await client.get_all_apis()
    exact = [a for a in apis if _api_name(a).lower() == api_name.lower()]

    if len(exact) == 1:
        return _api_id(exact[0]), None

    if len(exact) > 1:
        choices = [{"id": _api_id(a), "name": _api_name(a), "url": a.get("url")} for a in exact]
        return None, f"Error: multiple APIs matched '{api_name}'. Matches: {json.dumps(choices)}"

    partial = [a for a in apis if api_name.lower() in _api_name(a).lower()]
    if len(partial) == 1:
        return _api_id(partial[0]), None

    if len(partial) > 1:
        choices = [{"id": _api_id(a), "name": _api_name(a), "url": a.get("url")} for a in partial]
        return None, f"Error: multiple APIs matched '{api_name}'. Matches: {json.dumps(choices)}"

    return None, f"Error: no API matched '{api_name}'"


async def _resolve_env(
    client: MaerisClient, env_id: str | None, environment_name: str | None
) -> tuple[str | None, str | None]:
    if env_id:
        return str(env_id), None

    if environment_name:
        environments = await client.get_environments()
        exact = [e for e in environments if _environment_name(e).lower() == environment_name.lower()]
        if len(exact) == 1:
            return _environment_id(exact[0]), None
        if len(exact) > 1:
            choices = [{"id": _environment_id(e), "name": _environment_name(e)} for e in exact]
            return None, f"Error: multiple environments matched '{environment_name}'. Matches: {json.dumps(choices)}"

        partial = [e for e in environments if environment_name.lower() in _environment_name(e).lower()]
        if len(partial) == 1:
            return _environment_id(partial[0]), None
        if len(partial) > 1:
            choices = [{"id": _environment_id(e), "name": _environment_name(e)} for e in partial]
            return None, f"Error: multiple environments matched '{environment_name}'. Matches: {json.dumps(choices)}"
        return None, f"Error: no environment matched '{environment_name}'"

    selected_env = TokenStore().get_environment_id()
    if selected_env:
        return str(selected_env), None

    return None, None


async def _resolve_flow_id(client: MaerisClient, api_id: str, flow_id: str | None) -> tuple[str | None, str | None]:
    if flow_id:
        return str(flow_id), None

    flows = await client.get_api_flows()
    matches: list[dict] = []
    for flow in flows:
        candidate_flow_id = _flow_id(flow)
        if not candidate_flow_id:
            continue
        try:
            mapping = await client.get_api_flow_map(candidate_flow_id)
        except Exception:
            continue
        rows = _extract_flow_map_rows(mapping)
        for row in rows:
            if str(row.get("child_id") or "") == api_id:
                matches.append(
                    {
                        "flowId": candidate_flow_id,
                        "flowOrder": row.get("flow_order"),
                    }
                )
                break

    if not matches:
        return None, None

    if len(matches) > 1:
        return None, f"Error: multiple flows contain api '{api_id}'. Matches: {json.dumps(matches)}"

    return matches[0]["flowId"], None


async def handle_run_api(arguments: dict) -> list[TextContent]:
    api_name = arguments.get("api_name")
    env_id_in = arguments.get("env_id")
    environment_name = arguments.get("environment_name")
    flow_id_in = arguments.get("flow_id")

    if not api_name:
        return [TextContent(type="text", text="Error: api_name is required")]

    async with MaerisClient() as client:
        api_id, api_err = await _resolve_api_name(client, str(api_name))
        if api_err:
            return [TextContent(type="text", text=api_err)]

        env_id, env_err = await _resolve_env(client, env_id_in, environment_name)
        if env_err:
            return [TextContent(type="text", text=env_err)]

        flow_id, flow_err = await _resolve_flow_id(client, api_id=api_id or "", flow_id=flow_id_in)
        if flow_err:
            return [TextContent(type="text", text=flow_err)]

        response = await client.run_api(api_id=api_id or "", env_id=env_id, flow_id=flow_id)

    result = {
        "apiName": api_name,
        "apiId": api_id,
        "envId": env_id,
        "flowId": flow_id,
        "response": response,
    }
    return [TextContent(type="text", text=json.dumps(result, indent=2))]
