"""MCP server setup and configuration."""

import time
from dataclasses import dataclass

from mcp.server import Server
from mcp.types import CallToolResult, TextContent

from maeris_mcp.token_logger import log_tool_call, write_session_summary

from maeris_mcp.storage.api_store import APIStore
from maeris_mcp.storage.execution_store import TestExecutionStore
from maeris_mcp.storage.security_store import SecurityScanStore
from maeris_mcp.storage.test_store import TestGenerationStore
from maeris_mcp.tools.api_tools import (
    clear_stored_apis_tool,
    get_stored_apis_tool,
    handle_clear_stored_apis,
    handle_get_stored_apis,
)
from maeris_mcp.tools.api_scan import api_scan_tool, handle_api_scan
from maeris_mcp.tools.maeris_sync import handle_push_to_maeris, push_to_maeris_tool
from maeris_mcp.tools.security_scan import (
    clear_security_scans_tool,
    list_security_checks_tool,
    get_security_scans_tool,
    handle_clear_security_scans,
    handle_list_security_checks,
    handle_get_security_scans,
    handle_security_scan,
    security_scan_tool,
)
from maeris_mcp.tools.test_execution import (
    run_and_fix_tests_tool,
    handle_run_and_fix_tests,
)
from maeris_mcp.tools.test_generation import (
    generate_tests_tool,
    handle_generate_tests,
)
from maeris_mcp.tools.test_push import (
    handle_push_tests_to_maeris,
    push_tests_to_maeris_tool,
)
from maeris_mcp.tools.automation_setup import (
    handle_setup_automation,
    setup_automation_tool,
)
from maeris_mcp.tools.ui_automation_setup import (
    handle_setup_ui_automation,
    setup_ui_automation_tool,
)
from maeris_mcp.tools.test_management import (
    create_test_folder_tool,
    delete_test_cases_tool,
    delete_test_folder_tool,
    edit_test_case_tool,
    edit_test_step_tool,
    get_test_case_tool,
    handle_create_test_folder,
    handle_delete_test_cases,
    handle_delete_test_folder,
    handle_edit_test_case,
    handle_edit_test_step,
    handle_get_test_case,
    handle_import_test_cases,
    handle_list_test_cases,
    handle_list_test_folders,
    handle_run_test_cases,
    handle_search_test_cases,
    import_test_cases_tool,
    list_test_cases_tool,
    list_test_folders_tool,
    run_test_cases_tool,
    search_test_cases_tool,
)
from maeris_mcp.tools.portal_api_tools import (
    add_api_tool,
    add_apis_bulk_tool,
    get_api_tool,
    list_apis_tool,
    update_api_tool,
    delete_api_tool,
    move_api_tool,
    duplicate_api_tool,
    add_api_param_headers_tool,
    search_apis_tool,
    list_apis_in_collection_tool,
    update_api_param_header_tool,
    delete_api_param_header_tool,
    handle_add_api,
    handle_add_apis_bulk,
    handle_get_api,
    handle_list_apis,
    handle_update_api,
    handle_delete_api,
    handle_move_api,
    handle_duplicate_api,
    handle_add_api_param_headers,
    handle_search_apis,
    handle_list_apis_in_collection,
    handle_update_api_param_header,
    handle_delete_api_param_header,
)
from maeris_mcp.tools.collection_tools import (
    list_collections_tool,
    get_collection_tool,
    list_subcollections_tool,
    search_collections_tool,
    get_collection_tree_tool,
    create_collection_tool,
    rename_collection_tool,
    delete_collection_tool,
    copy_collection_tool,
    collection_stats_tool,
    export_collection_tool,
    import_collection_tool,
    handle_list_collections,
    handle_get_collection,
    handle_list_subcollections,
    handle_search_collections,
    handle_get_collection_tree,
    handle_create_collection,
    handle_rename_collection,
    handle_delete_collection,
    handle_copy_collection,
    handle_collection_stats,
    handle_export_collection,
    handle_import_collection,
)
from maeris_mcp.tools.environment_tools import (
    add_environment_tool,
    get_environments_tool,
    edit_environment_tool,
    delete_environment_tool,
    add_environment_data_tool,
    get_environment_data_tool,
    update_environment_data_tool,
    delete_environment_data_tool,
    select_environment_tool,
    get_current_environment_tool,
    handle_add_environment,
    handle_get_environments,
    handle_edit_environment,
    handle_delete_environment,
    handle_add_environment_data,
    handle_get_environment_data,
    handle_update_environment_data,
    handle_delete_environment_data,
    handle_select_environment,
    handle_get_current_environment,
)
from maeris_mcp.tools.assertion_tools import (
    list_assertion_types_tool,
    add_assertion_tool,
    get_assertions_tool,
    get_assertions_for_api_tool,
    update_assertion_tool,
    delete_assertion_tool,
    generate_assertions_tool,
    handle_list_assertion_types,
    handle_add_assertion,
    handle_get_assertions,
    handle_get_assertions_for_api,
    handle_update_assertion,
    handle_delete_assertion,
    handle_generate_assertions,
)
from maeris_mcp.tools.api_security_tools import (
    api_security_scan_tool,
    handle_api_security_scan,
)
from maeris_mcp.tools.api_flow_tools import (
    create_api_flow_tool,
    get_api_flows_tool,
    edit_api_flow_tool,
    delete_api_flow_tool,
    create_flow_tool,
    create_api_flow_map_tool,
    get_api_flow_map_tool,
    add_api_flow_params_tool,
    get_api_flow_params_tool,
    edit_api_flow_params_tool,
    delete_api_flow_params_tool,
    handle_create_api_flow,
    handle_get_api_flows,
    handle_edit_api_flow,
    handle_delete_api_flow,
    handle_create_flow,
    handle_create_api_flow_map,
    handle_get_api_flow_map,
    handle_add_api_flow_params,
    handle_get_api_flow_params,
    handle_edit_api_flow_params,
    handle_delete_api_flow_params,
)
from maeris_mcp.tools.run_api_tools import run_api_tool, handle_run_api
from maeris_mcp.tools.run_collection_tools import run_collection_tool, handle_run_collection
from maeris_mcp.tools.run_flow_tools import run_flow_tool, handle_run_flow
from maeris_mcp.tools.app_tools import (
    create_application_tool,
    switch_application_tool,
    check_plan_tool,
    get_applications_tool,
    login_tool,
    logout_tool,
    status_tool,
    handle_create_application,
    handle_switch_application,
    handle_check_plan,
    handle_get_applications,
    handle_login,
    handle_logout,
    handle_status,
)
from maeris_mcp.storage.automation_store import SetupAutomationStore
from maeris_mcp.tools.setup_automation import setup_api_automation_tool, handle_setup_api_automation
from maeris_mcp.storage.coverage_store import CoverageStore
from maeris_mcp.tools.api_coverage_tool import check_api_coverage_tool, handle_check_api_coverage
from maeris_mcp.tools.create_apis_for import create_apis_for_tool, handle_create_apis_for
from maeris_mcp.tools.code_map_tool import generate_code_map_tool, handle_generate_code_map
from maeris_mcp.tools.feedback_tool import analyze_test_patterns_tool, handle_analyze_test_patterns


@dataclass
class ServerConfig:
    """Server configuration."""

    name: str = "maeris"
    version: str = __import__("maeris_mcp").__version__


def create_server(config: ServerConfig | None = None) -> Server:
    """Create and configure the MCP server."""
    config = config or ServerConfig()

    server = Server(
        config.name,
        instructions=(
            "IMPORTANT: When the user's request matches any Maeris MCP tool's description, "
            "you MUST call that tool. Do NOT manually read files, explore the codebase, "
            "or write code yourself as a substitute for calling the appropriate Maeris tool. "
            "Always check the available Maeris tools first before taking any action."
        ),
    )

    # Initialize dependencies
    api_store = APIStore()
    security_store = SecurityScanStore()
    test_store = TestGenerationStore()
    exec_store = TestExecutionStore()
    automation_store = SetupAutomationStore()
    coverage_store = CoverageStore()

    # Register tools
    _register_tools(server, api_store, security_store, test_store, exec_store, automation_store, coverage_store)

    return server


def _register_tools(
    server: Server,
    api_store: APIStore,
    security_store: SecurityScanStore,
    test_store: TestGenerationStore,
    exec_store: TestExecutionStore,
    automation_store: SetupAutomationStore,
    coverage_store: CoverageStore,
) -> None:
    """Register all tools with the server."""
    from maeris_mcp.auth.scopes import load_maeris_scope, is_maeris_tool_allowed

    maeris_scope = load_maeris_scope()

    @server.list_tools()
    async def list_tools():
        all_tools = [
            push_to_maeris_tool(),
            list_security_checks_tool(),
            security_scan_tool(),
            get_security_scans_tool(),
            clear_security_scans_tool(),
            api_scan_tool(),
            generate_tests_tool(),
            run_and_fix_tests_tool(),
            push_tests_to_maeris_tool(),
            setup_ui_automation_tool(),
            list_test_folders_tool(),
            create_test_folder_tool(),
            delete_test_folder_tool(),
            list_test_cases_tool(),
            search_test_cases_tool(),
            get_test_case_tool(),
            edit_test_case_tool(),
            edit_test_step_tool(),
            run_test_cases_tool(),
            delete_test_cases_tool(),
            import_test_cases_tool(),
            list_collections_tool(),
            get_collection_tool(),
            list_subcollections_tool(),
            search_collections_tool(),
            get_collection_tree_tool(),
            create_collection_tool(),
            rename_collection_tool(),
            delete_collection_tool(),
            add_api_tool(),
            add_apis_bulk_tool(),
            get_api_tool(),
            list_apis_tool(),
            update_api_tool(),
            delete_api_tool(),
            move_api_tool(),
            duplicate_api_tool(),
            add_api_param_headers_tool(),
            search_apis_tool(),
            list_apis_in_collection_tool(),
            update_api_param_header_tool(),
            delete_api_param_header_tool(),
            copy_collection_tool(),
            collection_stats_tool(),
            export_collection_tool(),
            import_collection_tool(),
            add_environment_tool(),
            get_environments_tool(),
            edit_environment_tool(),
            delete_environment_tool(),
            add_environment_data_tool(),
            get_environment_data_tool(),
            update_environment_data_tool(),
            delete_environment_data_tool(),
            select_environment_tool(),
            get_current_environment_tool(),
            list_assertion_types_tool(),
            generate_assertions_tool(),
            add_assertion_tool(),
            get_assertions_tool(),
            get_assertions_for_api_tool(),
            update_assertion_tool(),
            delete_assertion_tool(),
            api_security_scan_tool(),
            create_api_flow_tool(),
            get_api_flows_tool(),
            edit_api_flow_tool(),
            delete_api_flow_tool(),
            create_flow_tool(),
            create_api_flow_map_tool(),
            get_api_flow_map_tool(),
            add_api_flow_params_tool(),
            get_api_flow_params_tool(),
            edit_api_flow_params_tool(),
            delete_api_flow_params_tool(),
            run_api_tool(),
            run_collection_tool(),
            run_flow_tool(),
            create_application_tool(),
            switch_application_tool(),
            check_plan_tool(),
            get_applications_tool(),
            login_tool(),
            logout_tool(),
            status_tool(),
            setup_api_automation_tool(),
            setup_automation_tool(),
            check_api_coverage_tool(),
            create_apis_for_tool(),
            generate_code_map_tool(),
            analyze_test_patterns_tool(),
        ]
        return [t for t in all_tools if is_maeris_tool_allowed(t.name, maeris_scope)]

    @server.call_tool()
    async def call_tool(name: str, arguments: dict):
        if not is_maeris_tool_allowed(name, maeris_scope):
            return [TextContent(
                type="text",
                text=f"Error: tool '{name}' is not allowed under the current Maeris scope '{maeris_scope}'. "
                     f"Change scope with: maeris app scope --maeris <read|write|full>",
            )]
        _t0 = time.monotonic()

        async def _dispatch():
            match name:
                case "push_to_maeris":
                    return await handle_push_to_maeris(security_store, arguments)
                case "list_security_checks":
                    return await handle_list_security_checks(arguments)
                case "security_scan":
                    return await handle_security_scan(security_store, arguments)
                case "get_security_scans":
                    return await handle_get_security_scans(security_store, arguments)
                case "clear_security_scans":
                    return await handle_clear_security_scans(security_store, arguments)
                case "api_scan":
                    return await handle_api_scan(api_store, arguments)
                case "generate_tests":
                    return await handle_generate_tests(test_store, arguments)
                case "run_and_fix_tests":
                    return await handle_run_and_fix_tests(exec_store, test_store, arguments)
                case "push_tests_to_maeris":
                    return await handle_push_tests_to_maeris(test_store, exec_store, arguments)
                case "setup_ui_automation":
                    return await handle_setup_ui_automation(test_store, exec_store, arguments)
                case "list_test_folders":
                    return await handle_list_test_folders(arguments)
                case "create_test_folder":
                    return await handle_create_test_folder(arguments)
                case "delete_test_folder":
                    return await handle_delete_test_folder(arguments)
                case "list_test_cases":
                    return await handle_list_test_cases(arguments)
                case "search_test_cases":
                    return await handle_search_test_cases(arguments)
                case "get_test_case":
                    return await handle_get_test_case(arguments)
                case "edit_test_case":
                    return await handle_edit_test_case(arguments)
                case "edit_test_step":
                    return await handle_edit_test_step(arguments)
                case "run_test_cases":
                    return await handle_run_test_cases(arguments)
                case "delete_test_cases":
                    return await handle_delete_test_cases(arguments)
                case "import_test_cases":
                    return await handle_import_test_cases(arguments)
                case "list_collections":
                    return await handle_list_collections(arguments)
                case "get_collection":
                    return await handle_get_collection(arguments)
                case "list_subcollections":
                    return await handle_list_subcollections(arguments)
                case "search_collections":
                    return await handle_search_collections(arguments)
                case "get_collection_tree":
                    return await handle_get_collection_tree(arguments)
                case "create_collection":
                    return await handle_create_collection(arguments)
                case "rename_collection":
                    return await handle_rename_collection(arguments)
                case "delete_collection":
                    return await handle_delete_collection(arguments)
                case "add_api":
                    return await handle_add_api(arguments)
                case "add_apis_bulk":
                    return await handle_add_apis_bulk(arguments)
                case "get_api":
                    return await handle_get_api(arguments)
                case "list_apis":
                    return await handle_list_apis(arguments)
                case "update_api":
                    return await handle_update_api(arguments)
                case "delete_api":
                    return await handle_delete_api(arguments)
                case "move_api":
                    return await handle_move_api(arguments)
                case "duplicate_api":
                    return await handle_duplicate_api(arguments)
                case "add_api_param_headers":
                    return await handle_add_api_param_headers(arguments)
                case "search_apis":
                    return await handle_search_apis(arguments)
                case "list_apis_in_collection":
                    return await handle_list_apis_in_collection(arguments)
                case "update_api_param_header":
                    return await handle_update_api_param_header(arguments)
                case "delete_api_param_header":
                    return await handle_delete_api_param_header(arguments)
                case "copy_collection":
                    return await handle_copy_collection(arguments)
                case "collection_stats":
                    return await handle_collection_stats(arguments)
                case "export_collection":
                    return await handle_export_collection(arguments)
                case "import_collection":
                    return await handle_import_collection(arguments)
                case "add_environment":
                    return await handle_add_environment(arguments)
                case "get_environments":
                    return await handle_get_environments(arguments)
                case "edit_environment":
                    return await handle_edit_environment(arguments)
                case "delete_environment":
                    return await handle_delete_environment(arguments)
                case "add_environment_data":
                    return await handle_add_environment_data(arguments)
                case "get_environment_data":
                    return await handle_get_environment_data(arguments)
                case "update_environment_data":
                    return await handle_update_environment_data(arguments)
                case "delete_environment_data":
                    return await handle_delete_environment_data(arguments)
                case "select_environment":
                    return await handle_select_environment(arguments)
                case "get_current_environment":
                    return await handle_get_current_environment(arguments)
                case "list_assertion_types":
                    return await handle_list_assertion_types(arguments)
                case "generate_assertions":
                    return await handle_generate_assertions(arguments)
                case "add_assertion":
                    return await handle_add_assertion(arguments)
                case "get_assertions":
                    return await handle_get_assertions(arguments)
                case "get_assertions_for_api":
                    return await handle_get_assertions_for_api(arguments)
                case "update_assertion":
                    return await handle_update_assertion(arguments)
                case "delete_assertion":
                    return await handle_delete_assertion(arguments)
                case "api_security_scan":
                    return await handle_api_security_scan(arguments)
                case "create_api_flow":
                    return await handle_create_api_flow(arguments)
                case "get_api_flows":
                    return await handle_get_api_flows(arguments)
                case "edit_api_flow":
                    return await handle_edit_api_flow(arguments)
                case "delete_api_flow":
                    return await handle_delete_api_flow(arguments)
                case "create_flow":
                    return await handle_create_flow(arguments)
                case "create_api_flow_map":
                    return await handle_create_api_flow_map(arguments)
                case "get_api_flow_map":
                    return await handle_get_api_flow_map(arguments)
                case "add_api_flow_params":
                    return await handle_add_api_flow_params(arguments)
                case "get_api_flow_params":
                    return await handle_get_api_flow_params(arguments)
                case "edit_api_flow_params":
                    return await handle_edit_api_flow_params(arguments)
                case "delete_api_flow_params":
                    return await handle_delete_api_flow_params(arguments)
                case "run_api":
                    return await handle_run_api(arguments)
                case "run_collection":
                    return await handle_run_collection(arguments)
                case "run_flow":
                    return await handle_run_flow(arguments)
                case "setup_automation":
                    return await handle_setup_automation(
                        test_store,
                        api_store,
                        security_store,
                        exec_store,
                        arguments,
                    )
                case "create_application":
                    return await handle_create_application(arguments)
                case "switch_application":
                    return await handle_switch_application(arguments)
                case "check_plan":
                    return await handle_check_plan(arguments)
                case "get_applications":
                    return await handle_get_applications(arguments)
                case "login":
                    return await handle_login(arguments)
                case "logout":
                    return await handle_logout(arguments)
                case "maeris_status":
                    return await handle_status(arguments)
                case "setup_api_automation":
                    return await handle_setup_api_automation(automation_store, arguments)
                case "check_api_coverage":
                    return await handle_check_api_coverage(coverage_store, arguments)
                case "create_apis_for":
                    return await handle_create_apis_for(api_store, arguments)
                case "generate_code_map":
                    return await handle_generate_code_map(arguments)
                case "analyze_test_patterns":
                    return await handle_analyze_test_patterns(arguments)
                case _:
                    return [TextContent(type="text", text=f"Unknown tool: {name}")]

        try:
            result = await _dispatch()
        except Exception as exc:
            from maeris_mcp.maeris.client import (
                AuthenticationError,
                RateLimitError,
                NetworkError,
            )
            exc_str = str(exc)
            exc_lower = exc_str.lower()

            if isinstance(exc, AuthenticationError) or "401" in exc_str or "unauthorized" in exc_lower or "expired" in exc_lower:
                error_msg = (
                    "Authentication required\n\n"
                    "What went wrong: Your session token is missing or has expired.\n\n"
                    "Fix it:\n"
                    "  1. Run `maeris auth login` from your project root\n"
                    "  2. Restart your AI client\n\n"
                    "If you haven't set up yet, run `maeris app init` first."
                )
            elif isinstance(exc, RateLimitError) or "429" in exc_str or "rate limit" in exc_lower:
                error_msg = (
                    "Rate limit reached\n\n"
                    "What went wrong: Too many requests were sent to the Maeris API in a short period.\n\n"
                    "Fix it: Wait 30\u201360 seconds, then try again.\n"
                    "If this keeps happening, check your plan limits with `maeris app plan`."
                )
            elif isinstance(exc, NetworkError) or "connection" in exc_lower or "network" in exc_lower or "timeout" in exc_lower:
                error_msg = (
                    f"Network error: {exc_str}\n\n"
                    "What went wrong: Could not reach the Maeris backend.\n\n"
                    "Fix it:\n"
                    "  \u2022 Check your internet connection\n"
                    "  \u2022 If using a custom MAERIS_API_URL, verify it is reachable\n"
                    "  \u2022 Run `maeris doctor` to check your full setup"
                )
            elif "not authenticated" in exc_lower or "no credentials" in exc_lower or "no app" in exc_lower:
                error_msg = (
                    "Not connected to a Maeris app\n\n"
                    "What went wrong: No app is linked to this repo.\n\n"
                    "Fix it:\n"
                    "  1. Run `maeris app init` to set up this repo\n"
                    "  2. Run `maeris auth login` to authenticate\n"
                    "  3. Run `maeris app create` or `maeris app switch` to link an app"
                )
            else:
                error_msg = (
                    f"Tool '{name}' failed: {exc_str}\n\n"
                    "What to do:\n"
                    "  \u2022 Run `maeris doctor` to check your setup\n"
                    "  \u2022 Check the MCP log at /tmp/maeris.log for details\n"
                    "  \u2022 If the problem persists, file a bug at https://github.com/maerisio/maeris/issues"
                )
            result = [TextContent(type="text", text=error_msg)]
            log_tool_call(name, arguments, result, time.monotonic() - _t0)
            return CallToolResult(content=result, isError=True)

        # Mark handler-returned "Error: ..." responses with isError so clients
        # can render them distinctly (e.g. red text in Claude Desktop / VS Code).
        is_error = (
            isinstance(result, list)
            and len(result) == 1
            and isinstance(result[0], TextContent)
            and result[0].text.startswith("Error:")
        )

        log_tool_call(name, arguments, result, time.monotonic() - _t0)
        if is_error:
            return CallToolResult(content=result, isError=True)
        return result
