"""Tests for the Orcheo MCP server."""

from __future__ import annotations
import json
from typing import Any
from unittest.mock import Mock, patch
import httpx
import pytest
import respx
from orcheo_sdk.cli.errors import CLIError


@pytest.fixture()
def mock_env(monkeypatch: pytest.MonkeyPatch) -> None:
    """Set up mock environment variables."""
    monkeypatch.setenv("ORCHEO_API_URL", "http://api.test")
    monkeypatch.setenv("ORCHEO_SERVICE_TOKEN", "test-token")


# ==============================================================================
# Configuration Tests
# ==============================================================================


def test_get_api_client_with_env_vars(mock_env: None) -> None:
    """Test API client configuration from environment variables."""
    from orcheo_sdk.mcp_server.config import get_api_client

    client, settings = get_api_client()
    assert client.base_url == "http://api.test"
    assert settings.api_url == "http://api.test"
    assert settings.service_token == "test-token"


def test_get_api_client_missing_url(monkeypatch: pytest.MonkeyPatch) -> None:
    """Test API client raises error when URL is explicitly None."""
    from unittest.mock import patch
    from orcheo_sdk.mcp_server.config import get_api_client

    # Mock resolve_settings to return None for api_url to test the error path
    with patch("orcheo_sdk.mcp_server.config.resolve_settings") as mock_resolve:
        mock_settings = type(
            "CLISettings", (), {"api_url": None, "service_token": "test"}
        )()
        mock_resolve.return_value = mock_settings

        with pytest.raises(ValueError, match="ORCHEO_API_URL must be set"):
            get_api_client()


def test_create_server(mock_env: None) -> None:
    """Test MCP server creation."""
    from orcheo_sdk.mcp_server.main import create_server

    server = create_server()
    assert server is not None
    assert server.name == "Orcheo CLI"


def test_mcp_init_lazy_import() -> None:
    """Test lazy import in mcp_server __init__."""
    import orcheo_sdk.mcp_server

    # Test that accessing create_server works via __getattr__
    create_server = orcheo_sdk.mcp_server.create_server
    assert create_server is not None

    # Test that accessing non-existent attribute raises AttributeError
    with pytest.raises(AttributeError, match="has no attribute"):
        _ = orcheo_sdk.mcp_server.nonexistent_function


def test_main_entry_point(mock_env: None) -> None:
    """Test main entry point function."""
    from unittest.mock import patch
    from orcheo_sdk.mcp_server.main import main

    # Mock mcp.run() to avoid actually starting the server
    with patch("orcheo_sdk.mcp_server.main.mcp.run") as mock_run:
        main()
        mock_run.assert_called_once()


# ==============================================================================
# Workflow Tools Tests
# ==============================================================================


def test_list_workflows_with_profile(mock_env: None) -> None:
    """Test listing workflows with explicit profile parameter."""
    from orcheo_sdk.mcp_server import tools

    payload = [
        {"id": "wf-1", "name": "Test Workflow", "slug": "test", "is_archived": False}
    ]

    with respx.mock() as router:
        router.get("http://api.test/api/workflows").mock(
            return_value=httpx.Response(200, json=payload)
        )
        result = tools.list_workflows(archived=False, profile=None)

    assert result == payload


def test_list_workflows_success(mock_env: None) -> None:
    """Test listing workflows."""
    from orcheo_sdk.mcp_server import tools

    payload = [
        {"id": "wf-1", "name": "Test Workflow", "slug": "test", "is_archived": False}
    ]

    with respx.mock() as router:
        router.get("http://api.test/api/workflows").mock(
            return_value=httpx.Response(200, json=payload)
        )
        result = tools.list_workflows()

    assert result == payload


def test_list_workflows_with_archived(mock_env: None) -> None:
    """Test listing workflows including archived ones."""
    from orcheo_sdk.mcp_server import tools

    payload = [
        {"id": "wf-1", "name": "Active", "slug": "active", "is_archived": False},
        {"id": "wf-2", "name": "Archived", "slug": "archived", "is_archived": True},
    ]

    with respx.mock() as router:
        router.get("http://api.test/api/workflows?include_archived=true").mock(
            return_value=httpx.Response(200, json=payload)
        )
        result = tools.list_workflows(archived=True)

    assert len(result) == 2


def test_show_workflow_success(mock_env: None) -> None:
    """Test showing workflow details."""
    from orcheo_sdk.mcp_server import tools

    workflow = {"id": "wf-1", "name": "Test"}
    versions = [{"id": "v1", "version": 1, "graph": {}}]
    runs = [{"id": "r1", "status": "completed", "created_at": "2025-01-01T00:00:00Z"}]

    with respx.mock() as router:
        router.get("http://api.test/api/workflows/wf-1").mock(
            return_value=httpx.Response(200, json=workflow)
        )
        router.get("http://api.test/api/workflows/wf-1/versions").mock(
            return_value=httpx.Response(200, json=versions)
        )
        router.get("http://api.test/api/workflows/wf-1/runs").mock(
            return_value=httpx.Response(200, json=runs)
        )

        result = tools.show_workflow("wf-1")

    assert result["workflow"] == workflow
    assert result["latest_version"] == versions[0]
    assert len(result["recent_runs"]) == 1


def test_show_workflow_with_cached_runs(mock_env: None) -> None:
    """Test show_workflow_data with pre-fetched runs."""
    from orcheo_sdk.mcp_server.config import get_api_client
    from orcheo_sdk.services.workflows import show_workflow_data

    client, _ = get_api_client()
    workflow = {"id": "wf-1", "name": "Test"}
    versions = [{"id": "v1", "version": 1, "graph": {}}]
    runs = [{"id": "r1", "status": "completed", "created_at": "2025-01-01T00:00:00Z"}]

    with respx.mock():
        # No API calls should be made for runs since we pass them in
        result = show_workflow_data(
            client,
            "wf-1",
            include_runs=True,
            workflow=workflow,
            versions=versions,
            runs=runs,
        )

    assert result["workflow"] == workflow
    assert result["latest_version"] == versions[0]
    assert len(result["recent_runs"]) == 1


def test_show_workflow_with_runs_none_path(mock_env: None) -> None:
    """Test show_workflow_data when runs is None and include_runs is True."""
    from orcheo_sdk.mcp_server.config import get_api_client
    from orcheo_sdk.services.workflows import show_workflow_data

    client, _ = get_api_client()
    workflow = {"id": "wf-1", "name": "Test"}
    versions = [{"id": "v1", "version": 1, "graph": {}}]
    runs = [{"id": "r1", "status": "completed", "created_at": "2025-01-01T00:00:00Z"}]

    with respx.mock() as router:
        # Mock the runs API call that should be made when runs is None
        router.get("http://api.test/api/workflows/wf-1/runs").mock(
            return_value=httpx.Response(200, json=runs)
        )

        # Don't pass runs parameter, so it defaults to None
        result = show_workflow_data(
            client,
            "wf-1",
            include_runs=True,
            workflow=workflow,
            versions=versions,
            runs=None,  # Explicitly pass None to trigger the branch
        )

    assert result["workflow"] == workflow
    assert result["latest_version"] == versions[0]
    assert len(result["recent_runs"]) == 1


def test_show_workflow_without_runs(mock_env: None) -> None:
    """Test show_workflow_data with include_runs=False to cover line 70->80 branch."""
    from orcheo_sdk.mcp_server.config import get_api_client
    from orcheo_sdk.services.workflows import show_workflow_data

    client, _ = get_api_client()
    workflow = {"id": "wf-1", "name": "Test"}
    versions = [{"id": "v1", "version": 1, "graph": {}}]

    with respx.mock():
        # No API calls should be made for runs since include_runs is False
        result = show_workflow_data(
            client,
            "wf-1",
            include_runs=False,  # This covers the 70->80 branch
            workflow=workflow,
            versions=versions,
        )

    assert result["workflow"] == workflow
    assert result["latest_version"] == versions[0]
    assert result["recent_runs"] == []  # Should be empty list


def test_upload_workflow_fake_console_print(mock_env: None, tmp_path: Any) -> None:
    """Test that _FakeConsole.print is called in upload_workflow_data."""
    from orcheo_sdk.mcp_server.config import get_api_client
    from orcheo_sdk.services.workflows import upload_workflow_data

    client, _ = get_api_client()

    # Create a Python file with LangGraph workflow code
    py_file = tmp_path / "workflow.py"
    py_file.write_text("""
from langgraph.graph import StateGraph, START, END
from orcheo.nodes import SetVariableNode

builder = StateGraph(dict)
builder.add_node("set_var", SetVariableNode(name="set_var", key="result", value="test"))
builder.add_edge(START, "set_var")
builder.add_edge("set_var", END)
graph = builder.compile()
""")

    workflow_response = {
        "id": "wf-1",
        "name": "Test Workflow",
        "slug": "test-workflow",
        "metadata": {},
    }
    version_response = {"id": "v1", "version": 1, "workflow_id": "wf-1"}

    with respx.mock(assert_all_called=False) as router:
        router.post("http://api.test/api/workflows").mock(
            return_value=httpx.Response(201, json=workflow_response)
        )
        router.post("http://api.test/api/workflows/wf-1/versions/ingest").mock(
            return_value=httpx.Response(201, json=version_response)
        )

        # Call with console=None to trigger _FakeConsole usage
        # This will call _upload_langgraph_script which uses state.console.print
        result = upload_workflow_data(
            client=client,
            file_path=str(py_file),
            workflow_name="Test Workflow",
            console=None,  # This triggers _FakeConsole.print on line 219
        )

    assert result["id"] == "wf-1"


def test_upload_workflow_with_entrypoint(mock_env: None, tmp_path: Any) -> None:
    """Test uploading a LangGraph workflow with custom entrypoint."""
    from orcheo_sdk.mcp_server.config import get_api_client
    from orcheo_sdk.services.workflows import upload_workflow_data

    client, _ = get_api_client()

    # Create a Python file with LangGraph workflow code
    py_file = tmp_path / "workflow.py"
    py_file.write_text("""
from langgraph.graph import StateGraph, START, END
from orcheo.nodes import SetVariableNode

builder = StateGraph(dict)
builder.add_node("set_var", SetVariableNode(name="set_var", key="result", value="test"))
builder.add_edge(START, "set_var")
builder.add_edge("set_var", END)
my_custom_graph = builder.compile()
""")

    workflow_response = {
        "id": "wf-1",
        "name": "Test Workflow",
        "slug": "test-workflow",
        "metadata": {},
    }
    version_response = {"id": "v1", "version": 1, "workflow_id": "wf-1"}

    with respx.mock(assert_all_called=False) as router:
        router.post("http://api.test/api/workflows").mock(
            return_value=httpx.Response(201, json=workflow_response)
        )
        router.post("http://api.test/api/workflows/wf-1/versions/ingest").mock(
            return_value=httpx.Response(201, json=version_response)
        )

        # Call with custom entrypoint to cover line 241
        result = upload_workflow_data(
            client=client,
            file_path=str(py_file),
            workflow_name="Test Workflow",
            entrypoint="my_custom_graph",  # This triggers line 241
            console=None,
        )

    assert result["id"] == "wf-1"


def test_run_workflow_success(mock_env: None) -> None:
    """Test running a workflow."""
    from orcheo_sdk.mcp_server import tools

    versions = [{"id": "v1", "version": 1, "graph": {}}]
    run_result = {"id": "run-1", "status": "pending"}

    with respx.mock() as router:
        router.get("http://api.test/api/workflows/wf-1/versions").mock(
            return_value=httpx.Response(200, json=versions)
        )
        # Mock the executor (imported from orcheo_sdk.client)
        with patch("orcheo_sdk.client.HttpWorkflowExecutor") as mock_exec:
            mock_executor = Mock()
            mock_executor.trigger_run.return_value = run_result
            mock_exec.return_value = mock_executor

            result = tools.run_workflow("wf-1", inputs={"test": "value"})

    assert result == run_result


def test_run_workflow_no_versions(mock_env: None) -> None:
    """Test running workflow fails when no versions exist."""
    from orcheo_sdk.mcp_server import tools

    with respx.mock() as router:
        router.get("http://api.test/api/workflows/wf-1/versions").mock(
            return_value=httpx.Response(200, json=[])
        )

        with pytest.raises(CLIError, match="no versions"):
            tools.run_workflow("wf-1")


def test_delete_workflow_success(mock_env: None) -> None:
    """Test deleting a workflow."""
    from orcheo_sdk.mcp_server import tools

    with respx.mock() as router:
        router.delete("http://api.test/api/workflows/wf-1").mock(
            return_value=httpx.Response(204)
        )
        result = tools.delete_workflow("wf-1")

    assert result["status"] == "success"
    assert "wf-1" in result["message"]


def test_download_workflow_json(mock_env: None) -> None:
    """Test downloading workflow as JSON."""
    from orcheo_sdk.mcp_server import tools

    workflow = {"id": "wf-1", "name": "Test", "metadata": {}}
    versions = [{"id": "v1", "version": 1, "graph": {"nodes": [], "edges": []}}]

    with respx.mock() as router:
        router.get("http://api.test/api/workflows/wf-1").mock(
            return_value=httpx.Response(200, json=workflow)
        )
        router.get("http://api.test/api/workflows/wf-1/versions").mock(
            return_value=httpx.Response(200, json=versions)
        )

        result = tools.download_workflow("wf-1", format_type="json")

    assert result["format"] == "json"
    assert "content" in result
    assert "Test" in result["content"]


def test_download_workflow_to_file(mock_env: None, tmp_path: Any) -> None:
    """Test downloading workflow to a file."""
    from orcheo_sdk.mcp_server import tools

    workflow = {"id": "wf-1", "name": "Test", "metadata": {}}
    versions = [{"id": "v1", "version": 1, "graph": {"nodes": [], "edges": []}}]
    output_file = tmp_path / "workflow.json"

    with respx.mock() as router:
        router.get("http://api.test/api/workflows/wf-1").mock(
            return_value=httpx.Response(200, json=workflow)
        )
        router.get("http://api.test/api/workflows/wf-1/versions").mock(
            return_value=httpx.Response(200, json=versions)
        )

        result = tools.download_workflow(
            "wf-1", output_path=str(output_file), format_type="json"
        )

    assert result["status"] == "success"
    assert str(output_file) in result["message"]
    assert output_file.exists()
    assert "Test" in output_file.read_text()


def test_download_workflow_no_versions(mock_env: None) -> None:
    """Test downloading workflow fails when no versions exist."""
    from orcheo_sdk.mcp_server import tools

    workflow = {"id": "wf-1", "name": "Test"}

    with respx.mock() as router:
        router.get("http://api.test/api/workflows/wf-1").mock(
            return_value=httpx.Response(200, json=workflow)
        )
        router.get("http://api.test/api/workflows/wf-1/versions").mock(
            return_value=httpx.Response(200, json=[])
        )

        with pytest.raises(CLIError, match="no versions"):
            tools.download_workflow("wf-1")


def test_upload_workflow_json_file(mock_env: None, tmp_path: Any) -> None:
    """Test uploading workflow from JSON file."""
    from orcheo_sdk.mcp_server import tools

    workflow_json = {
        "name": "Test Workflow",
        "graph": {"nodes": [], "edges": []},
    }
    json_file = tmp_path / "workflow.json"
    json_file.write_text(json.dumps(workflow_json))

    response = {"id": "wf-1", "name": "Test Workflow", "slug": "test-workflow"}

    with respx.mock() as router:
        router.post("http://api.test/api/workflows").mock(
            return_value=httpx.Response(201, json=response)
        )

        result = tools.upload_workflow(str(json_file))

    assert result["id"] == "wf-1"
    assert result["name"] == "Test Workflow"


# ==============================================================================
# Node Tools Tests
# ==============================================================================


def test_list_nodes(mock_env: None) -> None:
    """Test listing nodes."""
    from orcheo_sdk.mcp_server import tools

    result = tools.list_nodes()
    assert isinstance(result, list)
    assert len(result) > 0
    # Check that known nodes are present
    node_names = [node["name"] for node in result]
    assert "WebhookTriggerNode" in node_names


def test_list_nodes_with_tag_filter(mock_env: None) -> None:
    """Test listing nodes with tag filter."""
    from orcheo_sdk.mcp_server import tools

    result = tools.list_nodes(tag="trigger")
    assert isinstance(result, list)
    # All results should match the filter
    for node in result:
        assert (
            "trigger" in node["name"].lower() or "trigger" in node["category"].lower()
        )


def test_show_node_success(mock_env: None) -> None:
    """Test showing node details."""
    from orcheo_sdk.mcp_server import tools

    result = tools.show_node("WebhookTriggerNode")
    assert result["name"] == "WebhookTriggerNode"
    assert "category" in result
    assert "description" in result
    assert "schema" in result


def test_show_node_not_found(mock_env: None) -> None:
    """Test showing non-existent node."""
    from orcheo_sdk.mcp_server import tools

    with pytest.raises(CLIError, match="not registered"):
        tools.show_node("NonExistentNode")


def test_show_node_with_attributes_only(mock_env: None) -> None:
    """Test showing node that has attributes but no model_json_schema."""
    from orcheo.nodes.registry import NodeMetadata, registry

    # Register a test node without model_json_schema
    test_meta = NodeMetadata(
        name="TestNodeWithAttrs",
        description="Test node with attributes",
        category="test",
    )

    class TestNodeWithAttrs:
        """Node with annotations but no model_json_schema."""

        test_attr: str
        count: int

    # Register the test node
    registry._nodes["TestNodeWithAttrs"] = TestNodeWithAttrs
    registry._metadata["TestNodeWithAttrs"] = test_meta

    try:
        from orcheo_sdk.mcp_server import tools

        result = tools.show_node("TestNodeWithAttrs")
        assert result["name"] == "TestNodeWithAttrs"
        assert "attributes" in result
        assert "test_attr" in result["attributes"]
        assert "count" in result["attributes"]
    finally:
        # Clean up
        registry._nodes.pop("TestNodeWithAttrs", None)
        registry._metadata.pop("TestNodeWithAttrs", None)


# ==============================================================================
# Credential Tools Tests
# ==============================================================================


def test_list_credentials_success(mock_env: None) -> None:
    """Test listing credentials."""
    from orcheo_sdk.mcp_server import tools

    payload = [
        {
            "id": "cred-1",
            "name": "test-cred",
            "provider": "openai",
            "status": "active",
            "access": "private",
        }
    ]

    with respx.mock() as router:
        router.get("http://api.test/api/credentials").mock(
            return_value=httpx.Response(200, json=payload)
        )
        result = tools.list_credentials()

    assert result == payload


def test_list_credentials_with_workflow_filter(mock_env: None) -> None:
    """Test listing credentials filtered by workflow."""
    from orcheo_sdk.mcp_server import tools

    payload: list[dict[str, Any]] = []

    with respx.mock() as router:
        router.get("http://api.test/api/credentials?workflow_id=wf-1").mock(
            return_value=httpx.Response(200, json=payload)
        )
        result = tools.list_credentials(workflow_id="wf-1")

    assert result == payload


def test_create_credential_success(mock_env: None) -> None:
    """Test creating a credential."""
    from orcheo_sdk.mcp_server import tools

    response = {
        "id": "cred-1",
        "name": "test-cred",
        "provider": "openai",
        "status": "active",
    }

    with respx.mock() as router:
        router.post("http://api.test/api/credentials").mock(
            return_value=httpx.Response(201, json=response)
        )
        result = tools.create_credential(
            name="test-cred",
            provider="openai",
            secret="sk-test",
        )

    assert result["id"] == "cred-1"


def test_delete_credential_success(mock_env: None) -> None:
    """Test deleting a credential."""
    from orcheo_sdk.mcp_server import tools

    with respx.mock() as router:
        router.delete("http://api.test/api/credentials/cred-1").mock(
            return_value=httpx.Response(204)
        )
        result = tools.delete_credential("cred-1")

    assert result["status"] == "success"


# ==============================================================================
# Code Generation Tools Tests
# ==============================================================================


def test_generate_workflow_scaffold_success(mock_env: None) -> None:
    """Test generating workflow scaffold."""
    from orcheo_sdk.mcp_server import tools

    workflow = {"id": "wf-1", "name": "Test"}
    versions = [{"id": "v1", "version": 1}]

    with respx.mock() as router:
        router.get("http://api.test/api/workflows/wf-1").mock(
            return_value=httpx.Response(200, json=workflow)
        )
        router.get("http://api.test/api/workflows/wf-1/versions").mock(
            return_value=httpx.Response(200, json=versions)
        )

        result = tools.generate_workflow_scaffold("wf-1")

    assert "code" in result
    assert "workflow" in result
    assert "wf-1" in result["code"]
    assert "HttpWorkflowExecutor" in result["code"]


def test_generate_workflow_scaffold_no_versions(mock_env: None) -> None:
    """Test scaffold generation fails when no versions exist."""
    from orcheo_sdk.mcp_server import tools

    workflow = {"id": "wf-1", "name": "Test"}

    with respx.mock() as router:
        router.get("http://api.test/api/workflows/wf-1").mock(
            return_value=httpx.Response(200, json=workflow)
        )
        router.get("http://api.test/api/workflows/wf-1/versions").mock(
            return_value=httpx.Response(200, json=[])
        )

        with pytest.raises(CLIError, match="no versions"):
            tools.generate_workflow_scaffold("wf-1")


def test_generate_workflow_template() -> None:
    """Test generating workflow template."""
    from orcheo_sdk.mcp_server import tools

    result = tools.generate_workflow_template()
    assert "code" in result
    assert "description" in result
    assert "LangGraph" in result["code"]
    assert "StateGraph" in result["code"]
    assert "SetVariableNode" in result["code"]


# ==============================================================================
# Agent Tool Discovery Tests
# ==============================================================================


def test_ensure_agent_tools_registered_success(mock_env: None) -> None:
    """Test _ensure_agent_tools_registered when module is found."""
    from unittest.mock import MagicMock
    from orcheo_sdk.mcp_server.tools import _ensure_agent_tools_registered

    # Clear the lru_cache to ensure fresh execution
    _ensure_agent_tools_registered.cache_clear()

    # Mock find_spec to return a valid spec
    mock_spec = MagicMock()
    mock_spec.name = "orcheo.nodes.agent_tools.tools"

    with patch("orcheo_sdk.mcp_server.tools.util.find_spec", return_value=mock_spec):
        with patch("orcheo_sdk.mcp_server.tools.import_module") as mock_import:
            # Call the function - should successfully import
            _ensure_agent_tools_registered()
            # Verify import_module was called with the correct module name
            mock_import.assert_called_once_with("orcheo.nodes.agent_tools.tools")


def test_ensure_agent_tools_registered_not_found(mock_env: None) -> None:
    """Test _ensure_agent_tools_registered when module is not found."""
    from orcheo_sdk.mcp_server.tools import _ensure_agent_tools_registered

    # Clear the lru_cache to ensure fresh execution
    _ensure_agent_tools_registered.cache_clear()

    with patch("orcheo_sdk.mcp_server.tools.util.find_spec", return_value=None):
        with patch("orcheo_sdk.mcp_server.tools.logger") as mock_logger:
            # Call the function - should log warning and return early
            _ensure_agent_tools_registered()
            # Verify logger.warning was called
            mock_logger.warning.assert_called_once()
            # Verify the warning message mentions the module name
            call_args = mock_logger.warning.call_args
            assert "orcheo.nodes.agent_tools.tools" in call_args[0][1]


def test_list_agent_tools_import_error(mock_env: None) -> None:
    """Test list_agent_tools handles ImportError gracefully."""
    import sys
    from orcheo_sdk.mcp_server import tools

    # Mock ImportError for orcheo.nodes.agent_tools.tools
    with patch.dict(sys.modules, {"orcheo.nodes.agent_tools.tools": None}):
        result = tools.list_agent_tools()
        assert isinstance(result, list)


def test_list_agent_tools(mock_env: None) -> None:
    """Test listing agent tools."""
    from orcheo_sdk.mcp_server import tools

    result = tools.list_agent_tools()
    assert isinstance(result, list)
    # Result may be empty if no tools are registered
    if result:
        assert "name" in result[0]
        assert "category" in result[0]


def test_list_agent_tools_with_category_filter(mock_env: None) -> None:
    """Test listing agent tools with category filter."""
    from orcheo_sdk.mcp_server import tools

    result = tools.list_agent_tools(category="test")
    assert isinstance(result, list)
    # All results should match the filter if any exist
    for tool in result:
        assert "test" in tool["name"].lower() or "test" in tool["category"].lower()


def test_show_agent_tool_import_error(mock_env: None) -> None:
    """Test show_agent_tool handles ImportError gracefully."""
    import sys
    from orcheo_sdk.mcp_server import tools

    # Mock ImportError for orcheo.nodes.agent_tools.tools
    with patch.dict(sys.modules, {"orcheo.nodes.agent_tools.tools": None}):
        with pytest.raises(CLIError, match="not registered"):
            tools.show_agent_tool("NonExistentTool")


def test_show_agent_tool_not_found(mock_env: None) -> None:
    """Test showing non-existent agent tool."""
    from orcheo_sdk.mcp_server import tools

    with pytest.raises(CLIError, match="not registered"):
        tools.show_agent_tool("NonExistentTool")


def test_show_agent_tool_with_schema(mock_env: None) -> None:
    """Test showing agent tool with schema extraction."""
    from pydantic import BaseModel
    from orcheo.nodes.agent_tools.registry import ToolMetadata, tool_registry

    class TestSchema(BaseModel):
        """Test schema."""

        query: str
        count: int

    class TestTool:
        """Test tool with args_schema."""

        args_schema = TestSchema

    test_meta = ToolMetadata(
        name="test_tool_with_schema",
        description="Test tool with schema",
        category="test",
    )

    tool_registry._tools["test_tool_with_schema"] = TestTool()  # type: ignore[assignment]
    tool_registry._metadata["test_tool_with_schema"] = test_meta

    try:
        from orcheo_sdk.mcp_server import tools

        result = tools.show_agent_tool("test_tool_with_schema")
        assert result["name"] == "test_tool_with_schema"
        assert "schema" in result
    finally:
        # Clean up
        tool_registry._tools.pop("test_tool_with_schema", None)
        tool_registry._metadata.pop("test_tool_with_schema", None)


def test_show_agent_tool_with_model_json_schema(mock_env: None) -> None:
    """Test showing agent tool with model_json_schema method."""
    from orcheo.nodes.agent_tools.registry import ToolMetadata, tool_registry

    class TestToolWithModel:
        """Test tool with model_json_schema."""

        @staticmethod
        def model_json_schema() -> dict:
            return {"type": "object", "properties": {"query": {"type": "string"}}}

    test_meta = ToolMetadata(
        name="test_tool_model",
        description="Test tool with model schema",
        category="test",
    )

    tool_registry._tools["test_tool_model"] = TestToolWithModel()  # type: ignore[assignment]
    tool_registry._metadata["test_tool_model"] = test_meta

    try:
        from orcheo_sdk.mcp_server import tools

        result = tools.show_agent_tool("test_tool_model")
        assert result["name"] == "test_tool_model"
        assert "schema" in result
        assert result["schema"]["type"] == "object"
    finally:
        # Clean up
        tool_registry._tools.pop("test_tool_model", None)
        tool_registry._metadata.pop("test_tool_model", None)


def test_show_agent_tool_no_schema(mock_env: None) -> None:
    """Test showing agent tool with no schema attributes."""
    from orcheo.nodes.agent_tools.registry import ToolMetadata, tool_registry

    class TestToolNoSchema:
        """Test tool with no schema."""

        pass

    test_meta = ToolMetadata(
        name="test_tool_no_schema",
        description="Test tool without schema",
        category="test",
    )

    tool_registry._tools["test_tool_no_schema"] = TestToolNoSchema()  # type: ignore[assignment]
    tool_registry._metadata["test_tool_no_schema"] = test_meta

    try:
        from orcheo_sdk.mcp_server import tools

        result = tools.show_agent_tool("test_tool_no_schema")
        assert result["name"] == "test_tool_no_schema"
        # Should not have schema key if no schema is available
        assert "schema" not in result
    finally:
        # Clean up
        tool_registry._tools.pop("test_tool_no_schema", None)
        tool_registry._metadata.pop("test_tool_no_schema", None)


def test_show_agent_tool_with_args_schema_no_method(mock_env: None) -> None:
    """Test showing agent tool with args_schema but no model_json_schema method."""
    from orcheo.nodes.agent_tools.registry import ToolMetadata, tool_registry

    class FakeSchema:
        """Fake schema without model_json_schema."""

        pass

    class TestToolWithArgsSchemaNoMethod:
        """Test tool with args_schema but no model_json_schema."""

        args_schema = FakeSchema()

    test_meta = ToolMetadata(
        name="test_tool_args_no_method",
        description="Test tool with args_schema but no method",
        category="test",
    )

    tool_registry._tools["test_tool_args_no_method"] = TestToolWithArgsSchemaNoMethod()  # type: ignore[assignment]
    tool_registry._metadata["test_tool_args_no_method"] = test_meta

    try:
        from orcheo_sdk.mcp_server import tools

        result = tools.show_agent_tool("test_tool_args_no_method")
        assert result["name"] == "test_tool_args_no_method"
        # Should not have schema key if method is not available
        assert "schema" not in result
    finally:
        # Clean up
        tool_registry._tools.pop("test_tool_args_no_method", None)
        tool_registry._metadata.pop("test_tool_args_no_method", None)


# ==============================================================================
# Integration Tests
# ==============================================================================


def test_workflow_lifecycle(mock_env: None) -> None:
    """Test complete workflow lifecycle: list, show, run, delete."""
    from orcheo_sdk.mcp_server import tools

    workflow = {"id": "wf-1", "name": "Test"}
    workflows_list = [workflow]
    versions = [{"id": "v1", "version": 1, "graph": {}}]
    runs: list[dict[str, Any]] = []

    with respx.mock() as router:
        # List workflows
        router.get("http://api.test/api/workflows").mock(
            return_value=httpx.Response(200, json=workflows_list)
        )

        # Show workflow
        router.get("http://api.test/api/workflows/wf-1").mock(
            return_value=httpx.Response(200, json=workflow)
        )
        router.get("http://api.test/api/workflows/wf-1/versions").mock(
            return_value=httpx.Response(200, json=versions)
        )
        router.get("http://api.test/api/workflows/wf-1/runs").mock(
            return_value=httpx.Response(200, json=runs)
        )

        # Delete workflow
        router.delete("http://api.test/api/workflows/wf-1").mock(
            return_value=httpx.Response(204)
        )

        # Execute lifecycle
        list_result = tools.list_workflows()
        assert len(list_result) == 1

        show_result = tools.show_workflow("wf-1")
        assert show_result["workflow"]["id"] == "wf-1"

        delete_result = tools.delete_workflow("wf-1")
        assert delete_result["status"] == "success"


def test_credential_lifecycle(mock_env: None) -> None:
    """Test complete credential lifecycle: list, create, delete."""
    from orcheo_sdk.mcp_server import tools

    credentials_list: list[dict[str, Any]] = []
    created_cred = {
        "id": "cred-1",
        "name": "test-cred",
        "provider": "openai",
        "status": "active",
    }

    with respx.mock() as router:
        # List credentials (empty)
        router.get("http://api.test/api/credentials").mock(
            return_value=httpx.Response(200, json=credentials_list)
        )

        # Create credential
        router.post("http://api.test/api/credentials").mock(
            return_value=httpx.Response(201, json=created_cred)
        )

        # Delete credential
        router.delete("http://api.test/api/credentials/cred-1").mock(
            return_value=httpx.Response(204)
        )

        # Execute lifecycle
        list_result = tools.list_credentials()
        assert len(list_result) == 0

        create_result = tools.create_credential(
            name="test-cred",
            provider="openai",
            secret="sk-test",
        )
        assert create_result["id"] == "cred-1"

        delete_result = tools.delete_credential("cred-1")
        assert delete_result["status"] == "success"


# ==============================================================================
# MCP Tool Wrapper Tests (main.py return statement coverage)
# ==============================================================================


def test_mcp_list_workflows(mock_env: None) -> None:
    """Test list_workflows MCP tool wrapper to cover return statement."""
    # Import main.py to trigger coverage of the function definitions
    import orcheo_sdk.mcp_server.main as main_module

    # The decorated function is stored as .fn attribute
    payload = [
        {"id": "wf-1", "name": "Test Workflow", "slug": "test", "is_archived": False}
    ]

    with respx.mock() as router:
        router.get("http://api.test/api/workflows").mock(
            return_value=httpx.Response(200, json=payload)
        )
        # Access the underlying function via .fn
        result = main_module.list_workflows.fn()

    assert result == payload


def test_mcp_show_workflow(mock_env: None) -> None:
    """Test show_workflow MCP tool wrapper to cover return statement."""
    import orcheo_sdk.mcp_server.main as main_module

    workflow = {"id": "wf-1", "name": "Test"}
    versions = [{"id": "v1", "version": 1, "graph": {}}]
    runs = [{"id": "r1", "status": "completed", "created_at": "2025-01-01T00:00:00Z"}]

    with respx.mock() as router:
        router.get("http://api.test/api/workflows/wf-1").mock(
            return_value=httpx.Response(200, json=workflow)
        )
        router.get("http://api.test/api/workflows/wf-1/versions").mock(
            return_value=httpx.Response(200, json=versions)
        )
        router.get("http://api.test/api/workflows/wf-1/runs").mock(
            return_value=httpx.Response(200, json=runs)
        )

        result = main_module.show_workflow.fn("wf-1")

    assert result["workflow"] == workflow


def test_mcp_run_workflow(mock_env: None) -> None:
    """Test run_workflow MCP tool wrapper to cover return statement."""
    import orcheo_sdk.mcp_server.main as main_module

    versions = [{"id": "v1", "version": 1, "graph": {}}]
    run_result = {"id": "run-1", "status": "pending"}

    with respx.mock() as router:
        router.get("http://api.test/api/workflows/wf-1/versions").mock(
            return_value=httpx.Response(200, json=versions)
        )
        with patch("orcheo_sdk.client.HttpWorkflowExecutor") as mock_exec:
            mock_executor = Mock()
            mock_executor.trigger_run.return_value = run_result
            mock_exec.return_value = mock_executor

            result = main_module.run_workflow.fn("wf-1")

    assert result == run_result


def test_mcp_delete_workflow(mock_env: None) -> None:
    """Test delete_workflow MCP tool wrapper to cover return statement."""
    import orcheo_sdk.mcp_server.main as main_module

    with respx.mock() as router:
        router.delete("http://api.test/api/workflows/wf-1").mock(
            return_value=httpx.Response(204)
        )
        result = main_module.delete_workflow.fn("wf-1")

    assert result["status"] == "success"


def test_mcp_upload_workflow(mock_env: None, tmp_path: Any) -> None:
    """Test upload_workflow MCP tool wrapper to cover return statement."""
    import orcheo_sdk.mcp_server.main as main_module

    workflow_json = {
        "name": "Test Workflow",
        "graph": {"nodes": [], "edges": []},
    }
    json_file = tmp_path / "workflow.json"
    json_file.write_text(json.dumps(workflow_json))

    response = {"id": "wf-1", "name": "Test Workflow", "slug": "test-workflow"}

    with respx.mock() as router:
        router.post("http://api.test/api/workflows").mock(
            return_value=httpx.Response(201, json=response)
        )

        result = main_module.upload_workflow.fn(str(json_file))

    assert result["id"] == "wf-1"


def test_mcp_download_workflow(mock_env: None) -> None:
    """Test download_workflow MCP tool wrapper to cover return statement."""
    import orcheo_sdk.mcp_server.main as main_module

    workflow = {"id": "wf-1", "name": "Test", "metadata": {}}
    versions = [{"id": "v1", "version": 1, "graph": {"nodes": [], "edges": []}}]

    with respx.mock() as router:
        router.get("http://api.test/api/workflows/wf-1").mock(
            return_value=httpx.Response(200, json=workflow)
        )
        router.get("http://api.test/api/workflows/wf-1/versions").mock(
            return_value=httpx.Response(200, json=versions)
        )

        result = main_module.download_workflow.fn("wf-1", format_type="json")

    assert "content" in result


def test_mcp_list_nodes(mock_env: None) -> None:
    """Test list_nodes MCP tool wrapper to cover return statement."""
    import orcheo_sdk.mcp_server.main as main_module

    result = main_module.list_nodes.fn()
    assert isinstance(result, list)


def test_mcp_show_node(mock_env: None) -> None:
    """Test show_node MCP tool wrapper to cover return statement."""
    import orcheo_sdk.mcp_server.main as main_module

    result = main_module.show_node.fn("WebhookTriggerNode")
    assert result["name"] == "WebhookTriggerNode"


def test_mcp_list_credentials(mock_env: None) -> None:
    """Test list_credentials MCP tool wrapper to cover return statement."""
    import orcheo_sdk.mcp_server.main as main_module

    payload = [
        {
            "id": "cred-1",
            "name": "test-cred",
            "provider": "openai",
            "status": "active",
        }
    ]

    with respx.mock() as router:
        router.get("http://api.test/api/credentials").mock(
            return_value=httpx.Response(200, json=payload)
        )
        result = main_module.list_credentials.fn()

    assert result == payload


def test_mcp_create_credential(mock_env: None) -> None:
    """Test create_credential MCP tool wrapper to cover return statement."""
    import orcheo_sdk.mcp_server.main as main_module

    response = {
        "id": "cred-1",
        "name": "test-cred",
        "provider": "openai",
        "status": "active",
    }

    with respx.mock() as router:
        router.post("http://api.test/api/credentials").mock(
            return_value=httpx.Response(201, json=response)
        )
        result = main_module.create_credential.fn(
            name="test-cred",
            provider="openai",
            secret="sk-test",
        )

    assert result["id"] == "cred-1"


def test_mcp_delete_credential(mock_env: None) -> None:
    """Test delete_credential MCP tool wrapper to cover return statement."""
    import orcheo_sdk.mcp_server.main as main_module

    with respx.mock() as router:
        router.delete("http://api.test/api/credentials/cred-1").mock(
            return_value=httpx.Response(204)
        )
        result = main_module.delete_credential.fn("cred-1")

    assert result["status"] == "success"


def test_mcp_generate_workflow_scaffold(mock_env: None) -> None:
    """Test generate_workflow_scaffold MCP tool wrapper to cover return statement."""
    import orcheo_sdk.mcp_server.main as main_module

    workflow = {"id": "wf-1", "name": "Test"}
    versions = [{"id": "v1", "version": 1}]

    with respx.mock() as router:
        router.get("http://api.test/api/workflows/wf-1").mock(
            return_value=httpx.Response(200, json=workflow)
        )
        router.get("http://api.test/api/workflows/wf-1/versions").mock(
            return_value=httpx.Response(200, json=versions)
        )

        result = main_module.generate_workflow_scaffold.fn("wf-1")

    assert "code" in result


def test_mcp_generate_workflow_template() -> None:
    """Test generate_workflow_template MCP tool wrapper to cover return statement."""
    import orcheo_sdk.mcp_server.main as main_module

    result = main_module.generate_workflow_template.fn()
    assert "code" in result


def test_mcp_list_agent_tools(mock_env: None) -> None:
    """Test list_agent_tools MCP tool wrapper to cover return statement."""
    import orcheo_sdk.mcp_server.main as main_module

    result = main_module.list_agent_tools.fn()
    assert isinstance(result, list)


def test_mcp_show_agent_tool(mock_env: None) -> None:
    """Test show_agent_tool MCP tool wrapper to cover return statement."""
    import orcheo_sdk.mcp_server.main as main_module
    from orcheo.nodes.agent_tools.registry import ToolMetadata, tool_registry

    class TestTool:
        """Test tool."""

        pass

    test_meta = ToolMetadata(
        name="test_tool_mcp",
        description="Test tool",
        category="test",
    )

    tool_registry._tools["test_tool_mcp"] = TestTool()  # type: ignore[assignment]
    tool_registry._metadata["test_tool_mcp"] = test_meta

    try:
        result = main_module.show_agent_tool.fn("test_tool_mcp")
        assert result["name"] == "test_tool_mcp"
    finally:
        # Clean up
        tool_registry._tools.pop("test_tool_mcp", None)
        tool_registry._metadata.pop("test_tool_mcp", None)


# ==============================================================================
# Service Token Tools Tests
# ==============================================================================


def test_list_service_tokens_success(mock_env: None) -> None:
    """Test listing service tokens."""
    from orcheo_sdk.mcp_server import tools

    payload = {
        "tokens": [
            {
                "identifier": "token-1",
                "scopes": ["read", "write"],
                "workspace_ids": ["ws-1"],
                "issued_at": "2025-01-01T00:00:00Z",
            }
        ],
        "total": 1,
    }

    with respx.mock() as router:
        router.get("http://api.test/api/admin/service-tokens").mock(
            return_value=httpx.Response(200, json=payload)
        )
        result = tools.list_service_tokens()

    assert result == payload
    assert result["total"] == 1


def test_show_service_token_success(mock_env: None) -> None:
    """Test showing service token details."""
    from orcheo_sdk.mcp_server import tools

    token = {
        "identifier": "token-1",
        "scopes": ["read"],
        "workspace_ids": ["ws-1"],
        "issued_at": "2025-01-01T00:00:00Z",
    }

    with respx.mock() as router:
        router.get("http://api.test/api/admin/service-tokens/token-1").mock(
            return_value=httpx.Response(200, json=token)
        )
        result = tools.show_service_token("token-1")

    assert result == token


def test_create_service_token_success(mock_env: None) -> None:
    """Test creating a service token."""
    from orcheo_sdk.mcp_server import tools

    response = {
        "identifier": "token-1",
        "secret": "secret-value",
        "scopes": ["read"],
        "workspace_ids": ["ws-1"],
    }

    with respx.mock() as router:
        router.post("http://api.test/api/admin/service-tokens").mock(
            return_value=httpx.Response(201, json=response)
        )
        result = tools.create_service_token(
            identifier="token-1",
            scopes=["read"],
            workspace_ids=["ws-1"],
        )

    assert result["identifier"] == "token-1"
    assert result["secret"] == "secret-value"


def test_rotate_service_token_success(mock_env: None) -> None:
    """Test rotating a service token."""
    from orcheo_sdk.mcp_server import tools

    response = {
        "identifier": "token-2",
        "secret": "new-secret-value",
        "message": "Token rotated successfully",
    }

    with respx.mock() as router:
        router.post("http://api.test/api/admin/service-tokens/token-1/rotate").mock(
            return_value=httpx.Response(200, json=response)
        )
        result = tools.rotate_service_token("token-1")

    assert result["identifier"] == "token-2"
    assert result["secret"] == "new-secret-value"


def test_revoke_service_token_success(mock_env: None) -> None:
    """Test revoking a service token."""
    from orcheo_sdk.mcp_server import tools

    with respx.mock() as router:
        router.delete("http://api.test/api/admin/service-tokens/token-1").mock(
            return_value=httpx.Response(204)
        )
        result = tools.revoke_service_token("token-1", "Security breach")

    assert result["status"] == "success"


def test_mcp_list_service_tokens(mock_env: None) -> None:
    """Test list_service_tokens MCP tool wrapper."""
    import orcheo_sdk.mcp_server.main as main_module

    payload = {
        "tokens": [{"identifier": "token-1"}],
        "total": 1,
    }

    with respx.mock() as router:
        router.get("http://api.test/api/admin/service-tokens").mock(
            return_value=httpx.Response(200, json=payload)
        )
        result = main_module.list_service_tokens.fn()

    assert result == payload


def test_mcp_show_service_token(mock_env: None) -> None:
    """Test show_service_token MCP tool wrapper."""
    import orcheo_sdk.mcp_server.main as main_module

    token = {"identifier": "token-1"}

    with respx.mock() as router:
        router.get("http://api.test/api/admin/service-tokens/token-1").mock(
            return_value=httpx.Response(200, json=token)
        )
        result = main_module.show_service_token.fn("token-1")

    assert result == token


def test_mcp_create_service_token(mock_env: None) -> None:
    """Test create_service_token MCP tool wrapper."""
    import orcheo_sdk.mcp_server.main as main_module

    response = {"identifier": "token-1", "secret": "secret-value"}

    with respx.mock() as router:
        router.post("http://api.test/api/admin/service-tokens").mock(
            return_value=httpx.Response(201, json=response)
        )
        result = main_module.create_service_token.fn()

    assert result["identifier"] == "token-1"


def test_mcp_rotate_service_token(mock_env: None) -> None:
    """Test rotate_service_token MCP tool wrapper."""
    import orcheo_sdk.mcp_server.main as main_module

    response = {"identifier": "token-2", "secret": "new-secret"}

    with respx.mock() as router:
        router.post("http://api.test/api/admin/service-tokens/token-1/rotate").mock(
            return_value=httpx.Response(200, json=response)
        )
        result = main_module.rotate_service_token.fn("token-1")

    assert result["identifier"] == "token-2"


def test_mcp_revoke_service_token(mock_env: None) -> None:
    """Test revoke_service_token MCP tool wrapper."""
    import orcheo_sdk.mcp_server.main as main_module

    with respx.mock() as router:
        router.delete("http://api.test/api/admin/service-tokens/token-1").mock(
            return_value=httpx.Response(204)
        )
        result = main_module.revoke_service_token.fn("token-1", "Test reason")

    assert result["status"] == "success"


# NOTE: The `if __name__ == "__main__":` block on line 387 of main.py is tested
# indirectly by test_main_entry_point(), which verifies that main() is callable
# and calls mcp.run(). The if __name__ guard itself is a standard Python pattern
# that only executes when the file is run as a script (not when imported).
# Testing this line directly would require running the module as __main__ with
# stdin/stdout mocking, which is complex and provides minimal value since:
# 1. The main() function is already tested
# 2. The if __name__ == "__main__" pattern is a Python idiom
# 3. The line contains no logic, just a guard
# Coverage tools typically exclude such lines or accept them as untestable.
# Current coverage: 97% (56/57 statements, missing only line 387)
