from __future__ import annotations

from typing import Any

from ..schemas import ToolDefinition
from .base import object_schema
from .file_tools import (
    append_file_handler,
    delete_path_handler,
    list_dir_handler,
    make_dir_handler,
    read_file_handler,
    replace_in_file_handler,
    write_file_handler,
)
from .http_tools import http_get_handler, http_post_json_handler, web_search_handler
from .project_upgrade_tools import build_tools as build_project_upgrade_tools
from .shell_tools import shell_run_handler


def build_builtin_tools(runtime) -> list[ToolDefinition]:
    async def get_status_handler(context, arguments: dict[str, Any]) -> dict[str, Any]:
        return runtime.get_session_status(context.session)

    async def set_provider_handler(context, arguments: dict[str, Any]) -> dict[str, Any]:
        return runtime.set_provider(context.session, arguments["provider"])

    async def set_model_handler(context, arguments: dict[str, Any]) -> dict[str, Any]:
        return runtime.set_model(context.session, arguments["model"])

    async def set_security_mode_handler(context, arguments: dict[str, Any]) -> dict[str, Any]:
        return runtime.set_security_mode(context.session, arguments["mode"])

    async def list_providers_handler(context, arguments: dict[str, Any]) -> dict[str, Any]:
        return {"providers": runtime.list_provider_names()}

    async def list_models_handler(context, arguments: dict[str, Any]) -> dict[str, Any]:
        provider = arguments.get("provider")
        return {"models": await runtime.list_models(provider)}

    async def list_sessions_handler(context, arguments: dict[str, Any]) -> dict[str, Any]:
        return {"sessions": runtime.list_sessions_payload()}

    async def session_end_handler(context, arguments: dict[str, Any]) -> dict[str, Any]:
        return runtime.end_session(context.session)

    async def session_reopen_handler(context, arguments: dict[str, Any]) -> dict[str, Any]:
        return runtime.reopen_session(context.session)

    async def config_get_handler(context, arguments: dict[str, Any]) -> dict[str, Any]:
        dotted_path = arguments.get("path")
        if dotted_path:
            return {"path": dotted_path, "value": runtime.config_manager.get(dotted_path)}
        return {"config": runtime.config_manager.data}

    async def config_set_handler(context, arguments: dict[str, Any]) -> dict[str, Any]:
        runtime.config_manager.set(arguments["path"], arguments.get("value"), backup_reason="config-set-tool")
        runtime.reload()
        return {"path": arguments["path"], "value": runtime.config_manager.get(arguments["path"])}

    async def providers_add_custom_handler(context, arguments: dict[str, Any]) -> dict[str, Any]:
        payload = {
            "type": arguments.get("type", "openai_compatible"),
            "enabled": bool(arguments.get("enabled", True)),
            "api_key": arguments.get("api_key", ""),
            "base_url": arguments.get("base_url", ""),
            "default_model": arguments.get("default_model", ""),
            "headers": arguments.get("headers", {}) or {},
        }
        runtime.config_manager.set(f"providers.items.{arguments['name']}", payload, backup_reason="add-provider")
        runtime.reload()
        return {"provider": arguments["name"], "config": payload}

    async def reload_runtime_handler(context, arguments: dict[str, Any]) -> dict[str, Any]:
        runtime.reload()
        return {"reloaded": True, "providers": runtime.list_provider_names()}

    async def reload_skills_handler(context, arguments: dict[str, Any]) -> dict[str, Any]:
        return runtime.reload_skills()

    async def list_skills_handler(context, arguments: dict[str, Any]) -> dict[str, Any]:
        return {"skills": runtime.list_skills_payload()}

    async def create_skill_handler(context, arguments: dict[str, Any]) -> dict[str, Any]:
        return runtime.skill_manager.create_skill(
            name=arguments["name"],
            description=arguments["description"],
            instructions=arguments.get("instructions"),
            entrypoint_code=arguments.get("entrypoint_code"),
            with_entrypoint=bool(arguments.get("with_entrypoint", True)),
            extra_files=arguments.get("extra_files"),
            context=runtime.skill_context(),
        )

    async def update_skill_handler(context, arguments: dict[str, Any]) -> dict[str, Any]:
        return runtime.skill_manager.update_skill(
            name=arguments["name"],
            description=arguments.get("description"),
            instructions=arguments.get("instructions"),
            entrypoint_code=arguments.get("entrypoint_code"),
            manifest_updates=arguments.get("manifest_updates"),
            extra_files=arguments.get("extra_files"),
            context=runtime.skill_context(),
        )

    async def auth_summary_handler(context, arguments: dict[str, Any]) -> dict[str, Any]:
        return runtime.auth_summary()

    async def auth_create_pairing_code_handler(context, arguments: dict[str, Any]) -> dict[str, Any]:
        if not runtime.auth_is_owner(user_id=context.user_id, chat_id=context.chat_id):
            raise PermissionError("Only owners can create pairing codes")
        return runtime.create_pairing_code(
            created_by=context.user_id,
            ttl_seconds=arguments.get("ttl_seconds"),
            note=arguments.get("note", ""),
            max_uses=int(arguments.get("max_uses", 1)),
        )

    async def read_project_agents_handler(context, arguments: dict[str, Any]) -> dict[str, Any]:
        return {"path": str(runtime.project_agents_path()), "content": runtime.read_project_agents_md()}

    async def write_project_agents_handler(context, arguments: dict[str, Any]) -> dict[str, Any]:
        return runtime.write_project_agents_md(arguments.get("content", ""))

    async def append_project_agents_handler(context, arguments: dict[str, Any]) -> dict[str, Any]:
        return runtime.append_project_agents_md(arguments.get("content", ""))

    async def auth_approve_access_handler(context, arguments: dict[str, Any]) -> dict[str, Any]:
        if not runtime.auth_is_owner(user_id=context.user_id, chat_id=context.chat_id):
            raise PermissionError("Only owners can approve Telegram access")
        return runtime.approve_telegram_access(
            user_id=arguments.get("user_id"),
            chat_id=arguments.get("chat_id"),
            approved_by=context.user_id,
            username=arguments.get("username"),
            chat_title=arguments.get("chat_title"),
        )

    return [
        ToolDefinition(
            name="shell_run",
            description="Execute a shell command using the current security mode or an explicitly requested mode.",
            parameters=object_schema(
                {
                    "command": {"type": "string", "description": "Shell command to run."},
                    "cwd": {"type": "string", "description": "Optional working directory."},
                    "mode": {"type": "string", "enum": ["safe", "yolo", "god"]},
                    "timeout_seconds": {"type": "integer", "minimum": 0},
                },
                required=["command"],
            ),
            handler=shell_run_handler,
            owner_only=True,
        ),
        ToolDefinition(
            name="read_file",
            description="Read a UTF-8 text file from the project, workspace, data, or logs.",
            parameters=object_schema(
                {
                    "path": {"type": "string"},
                    "max_chars": {"type": "integer", "minimum": 1, "maximum": 200000},
                },
                required=["path"],
            ),
            handler=read_file_handler,
        ),
        ToolDefinition(
            name="write_file",
            description="Write a UTF-8 text file, backing up the previous version if present.",
            parameters=object_schema(
                {
                    "path": {"type": "string"},
                    "content": {"type": "string"},
                    "overwrite": {"type": "boolean"},
                },
                required=["path", "content"],
            ),
            handler=write_file_handler,
            destructive=True,
        ),
        ToolDefinition(
            name="append_file",
            description="Append UTF-8 text to a file, creating it if it does not exist.",
            parameters=object_schema(
                {
                    "path": {"type": "string"},
                    "content": {"type": "string"},
                },
                required=["path", "content"],
            ),
            handler=append_file_handler,
            destructive=True,
        ),
        ToolDefinition(
            name="replace_in_file",
            description="Replace text inside a file, optionally limiting replacement count.",
            parameters=object_schema(
                {
                    "path": {"type": "string"},
                    "old": {"type": "string"},
                    "new": {"type": "string"},
                    "count": {"type": "integer", "minimum": 0},
                },
                required=["path", "old", "new"],
            ),
            handler=replace_in_file_handler,
            destructive=True,
        ),
        ToolDefinition(
            name="list_dir",
            description="List files and directories at a path.",
            parameters=object_schema({"path": {"type": "string"}}),
            handler=list_dir_handler,
        ),
        ToolDefinition(
            name="make_dir",
            description="Create a directory path.",
            parameters=object_schema({"path": {"type": "string"}}, required=["path"]),
            handler=make_dir_handler,
            destructive=True,
        ),
        ToolDefinition(
            name="delete_path",
            description="Delete a file or directory, backing it up first when possible.",
            parameters=object_schema(
                {
                    "path": {"type": "string"},
                    "recursive": {"type": "boolean"},
                },
                required=["path"],
            ),
            handler=delete_path_handler,
            destructive=True,
        ),
        ToolDefinition(
            name="http_get",
            description="Fetch a URL with HTTP GET.",
            parameters=object_schema(
                {
                    "url": {"type": "string"},
                    "headers": {"type": "object"},
                    "timeout_seconds": {"type": "number", "minimum": 1},
                    "max_chars": {"type": "integer", "minimum": 1, "maximum": 200000},
                },
                required=["url"],
            ),
            handler=http_get_handler,
        ),
        ToolDefinition(
            name="http_post_json",
            description="Send a JSON body with HTTP POST.",
            parameters=object_schema(
                {
                    "url": {"type": "string"},
                    "headers": {"type": "object"},
                    "json_body": {"type": "object"},
                    "timeout_seconds": {"type": "number", "minimum": 1},
                    "max_chars": {"type": "integer", "minimum": 1, "maximum": 200000},
                },
                required=["url", "json_body"],
            ),
            handler=http_post_json_handler,
        ),
        ToolDefinition(
            name="web_search",
            description="Search the web using DuckDuckGo HTML and return compact results.",
            parameters=object_schema(
                {
                    "query": {"type": "string"},
                    "max_results": {"type": "integer", "minimum": 1, "maximum": 10},
                },
                required=["query"],
            ),
            handler=web_search_handler,
        ),
        ToolDefinition(
            name="session_get_status",
            description="Get the current session status, model, provider, safety mode, and loaded skills.",
            parameters=object_schema({}),
            handler=get_status_handler,
        ),
        ToolDefinition(
            name="session_list",
            description="List saved sessions known to the runtime.",
            parameters=object_schema({}),
            handler=list_sessions_handler,
        ),
        ToolDefinition(
            name="session_set_provider",
            description="Change the current LLM provider for this session.",
            parameters=object_schema({"provider": {"type": "string"}}, required=["provider"]),
            handler=set_provider_handler,
            destructive=True,
        ),
        ToolDefinition(
            name="session_set_model",
            description="Change the current model for this session.",
            parameters=object_schema({"model": {"type": "string"}}, required=["model"]),
            handler=set_model_handler,
            destructive=True,
        ),
        ToolDefinition(
            name="set_security_mode",
            description="Change the current security mode for shell and file operations. `/safe off` maps to `god`.",
            parameters=object_schema(
                {"mode": {"type": "string", "enum": ["safe", "yolo", "god"]}},
                required=["mode"],
            ),
            handler=set_security_mode_handler,
            destructive=True,
        ),
        ToolDefinition(
            name="session_end",
            description="Close the current session. The agent may call this when it decides the task is done.",
            parameters=object_schema({}),
            handler=session_end_handler,
            destructive=True,
        ),
        ToolDefinition(
            name="session_reopen",
            description="Reopen a previously closed session.",
            parameters=object_schema({}),
            handler=session_reopen_handler,
            destructive=True,
        ),
        ToolDefinition(
            name="read_project_agents_md",
            description="Read the root AGENTS.md file that is injected into the next system prompt if present.",
            parameters=object_schema({}),
            handler=read_project_agents_handler,
        ),
        ToolDefinition(
            name="write_project_agents_md",
            description="Create or replace the root AGENTS.md file. Its contents will be injected into the next system prompt.",
            parameters=object_schema({"content": {"type": "string"}}, required=["content"]),
            handler=write_project_agents_handler,
            destructive=True,
        ),
        ToolDefinition(
            name="append_project_agents_md",
            description="Append instructions to the root AGENTS.md file so they apply on the next generation.",
            parameters=object_schema({"content": {"type": "string"}}, required=["content"]),
            handler=append_project_agents_handler,
            destructive=True,
        ),
        ToolDefinition(
            name="list_providers",
            description="List configured providers.",
            parameters=object_schema({}),
            handler=list_providers_handler,
        ),
        ToolDefinition(
            name="list_models",
            description="List models for one provider or for all providers.",
            parameters=object_schema({"provider": {"type": "string"}}),
            handler=list_models_handler,
        ),
        ToolDefinition(
            name="config_get",
            description="Read a configuration value by dotted path, or return the whole config.",
            parameters=object_schema({"path": {"type": "string"}}),
            handler=config_get_handler,
        ),
        ToolDefinition(
            name="config_set",
            description="Set a configuration value by dotted path and reload the runtime.",
            parameters=object_schema(
                {
                    "path": {"type": "string"},
                    "value": {},
                },
                required=["path", "value"],
            ),
            handler=config_set_handler,
            destructive=True,
        ),
        ToolDefinition(
            name="providers_add_custom",
            description="Add or replace a configured OpenAI-compatible or Gemini provider and reload the runtime.",
            parameters=object_schema(
                {
                    "name": {"type": "string"},
                    "type": {"type": "string", "enum": ["openai_compatible", "custom", "openai", "gemini"]},
                    "api_key": {"type": "string"},
                    "base_url": {"type": "string"},
                    "default_model": {"type": "string"},
                    "enabled": {"type": "boolean"},
                    "headers": {"type": "object"},
                },
                required=["name", "type"],
            ),
            handler=providers_add_custom_handler,
            destructive=True,
        ),
        ToolDefinition(
            name="reload_runtime",
            description="Reload providers, config, and skills without restarting the process.",
            parameters=object_schema({}),
            handler=reload_runtime_handler,
            destructive=True,
        ),
        ToolDefinition(
            name="reload_skills",
            description="Hot reload all workspace skills immediately.",
            parameters=object_schema({}),
            handler=reload_skills_handler,
            destructive=True,
        ),
        ToolDefinition(
            name="list_skills",
            description="List all known skills and their status.",
            parameters=object_schema({}),
            handler=list_skills_handler,
        ),
        ToolDefinition(
            name="create_skill",
            description="Create a new workspace skill with SKILL.md, manifest, and optional Python entrypoint.",
            parameters=object_schema(
                {
                    "name": {"type": "string"},
                    "description": {"type": "string"},
                    "instructions": {"type": "string"},
                    "entrypoint_code": {"type": "string"},
                    "with_entrypoint": {"type": "boolean"},
                    "extra_files": {"type": "object", "additionalProperties": {"type": "string"}},
                },
                required=["name", "description"],
            ),
            handler=create_skill_handler,
            destructive=True,
        ),
        ToolDefinition(
            name="update_skill",
            description="Update an existing skill and hot reload it.",
            parameters=object_schema(
                {
                    "name": {"type": "string"},
                    "description": {"type": "string"},
                    "instructions": {"type": "string"},
                    "entrypoint_code": {"type": "string"},
                    "manifest_updates": {"type": "object"},
                    "extra_files": {"type": "object", "additionalProperties": {"type": "string"}},
                },
                required=["name"],
            ),
            handler=update_skill_handler,
            destructive=True,
        ),
        ToolDefinition(
            name="auth_summary",
            description="Show Telegram access state: owners, granted users/chats, and pending pairing codes.",
            parameters=object_schema({}),
            handler=auth_summary_handler,
            destructive=True,
        ),
        ToolDefinition(
            name="auth_create_pairing_code",
            description="Create a Telegram pairing code for granting access to the bot.",
            parameters=object_schema(
                {
                    "ttl_seconds": {"type": "integer", "minimum": 1},
                    "max_uses": {"type": "integer", "minimum": 1},
                    "note": {"type": "string"},
                }
            ),
            handler=auth_create_pairing_code_handler,
            destructive=True,
        ),
        ToolDefinition(
            name="auth_approve_access",
            description="Approve a Telegram user/chat manually.",
            parameters=object_schema(
                {
                    "user_id": {"type": "integer"},
                    "chat_id": {"type": "integer"},
                    "username": {"type": "string"},
                    "chat_title": {"type": "string"},
                }
            ),
            handler=auth_approve_access_handler,
            destructive=True,
        ),
    ] + build_project_upgrade_tools()
