#!/usr/bin/env python3
"""
Comprehensive test suite for FastMCP Server Implementation
Tests all 17 tools across 5 categories with comprehensive coverage
"""

import json
import os
from unittest.mock import MagicMock, Mock, patch

import pytest

pytestmark = pytest.mark.mcp_only

# WORKAROUND for shadowing issue: Temporarily change to /tmp to import external mcp
# This avoids any local mcp/ directory being in the import path
_original_dir = os.getcwd()
MCP_AVAILABLE = False
FASTMCP_AVAILABLE = False

try:
    os.chdir("/tmp")  # Change away from project directory
    from mcp.server import FastMCP
    from mcp.types import TextContent

    MCP_AVAILABLE = True
    FASTMCP_AVAILABLE = True
except ImportError:
    TextContent = None
    FastMCP = None
finally:
    os.chdir(_original_dir)  # Restore original directory

# Import FastMCP server
if FASTMCP_AVAILABLE:
    try:
        from skill_seekers.mcp import server_fastmcp
    except ImportError as e:
        print(f"Warning: Could not import server_fastmcp: {e}")
        server_fastmcp = None
        FASTMCP_AVAILABLE = False


# ============================================================================
# FIXTURES
# ============================================================================


@pytest.fixture
def temp_dirs(tmp_path):
    """Create temporary directories for testing."""
    config_dir = tmp_path / "configs"
    output_dir = tmp_path / "output"
    cache_dir = tmp_path / "cache"

    config_dir.mkdir()
    output_dir.mkdir()
    cache_dir.mkdir()

    return {"config": config_dir, "output": output_dir, "cache": cache_dir, "base": tmp_path}


@pytest.fixture
def sample_config(temp_dirs):
    """Create a sample config file (unified format)."""
    config_data = {
        "name": "test-framework",
        "description": "Test framework for testing",
        "sources": [
            {
                "type": "documentation",
                "base_url": "https://test-framework.dev/",
                "selectors": {"main_content": "article", "title": "h1", "code_blocks": "pre"},
                "url_patterns": {"include": ["/docs/"], "exclude": ["/blog/", "/search/"]},
                "categories": {
                    "getting_started": ["introduction", "getting-started"],
                    "api": ["api", "reference"],
                },
                "rate_limit": 0.5,
                "max_pages": 100,
            }
        ],
    }

    config_path = temp_dirs["config"] / "test-framework.json"
    config_path.write_text(json.dumps(config_data, indent=2))
    return config_path


@pytest.fixture
def unified_config(temp_dirs):
    """Create a sample unified config file."""
    config_data = {
        "name": "test-unified",
        "description": "Test unified scraping",
        "merge_mode": "rule-based",
        "sources": [
            {
                "type": "documentation",
                "base_url": "https://example.com/docs/",
                "extract_api": True,
                "max_pages": 10,
            },
            {"type": "github", "repo": "test/repo", "extract_readme": True},
        ],
    }

    config_path = temp_dirs["config"] / "test-unified.json"
    config_path.write_text(json.dumps(config_data, indent=2))
    return config_path


# ============================================================================
# SERVER INITIALIZATION TESTS
# ============================================================================


@pytest.mark.skipif(not FASTMCP_AVAILABLE, reason="FastMCP not available")
class TestFastMCPServerInitialization:
    """Test FastMCP server initialization and setup."""

    def test_server_import(self):
        """Test that FastMCP server module can be imported."""
        assert server_fastmcp is not None
        assert hasattr(server_fastmcp, "mcp")

    def test_server_has_name(self):
        """Test that server has correct name."""
        assert server_fastmcp.mcp.name == "skill-seeker"

    def test_server_has_instructions(self):
        """Test that server has instructions."""
        assert server_fastmcp.mcp.instructions is not None
        assert "Skill Seeker" in server_fastmcp.mcp.instructions

    def test_all_tools_registered(self):
        """Test that all 17 tools are registered."""
        # FastMCP uses decorator-based registration
        # Tools should be available via the mcp instance
        tool_names = [
            # Config tools (3)
            "generate_config",
            "list_configs",
            "validate_config",
            # Scraping tools (4)
            "estimate_pages",
            "scrape_docs",
            "scrape_github",
            "scrape_pdf",
            # Packaging tools (3)
            "package_skill",
            "upload_skill",
            "install_skill",
            # Splitting tools (2)
            "split_config",
            "generate_router",
            # Source tools (5)
            "fetch_config",
            "submit_config",
            "add_config_source",
            "list_config_sources",
            "remove_config_source",
        ]

        # Check that decorators were applied
        for tool_name in tool_names:
            assert hasattr(server_fastmcp, tool_name), f"Missing tool: {tool_name}"


# ============================================================================
# CONFIG TOOLS TESTS (3 tools)
# ============================================================================


@pytest.mark.skipif(not FASTMCP_AVAILABLE, reason="FastMCP not available")
@pytest.mark.asyncio
class TestConfigTools:
    """Test configuration management tools."""

    async def test_generate_config_basic(self, temp_dirs, monkeypatch):
        """Test basic config generation."""
        monkeypatch.chdir(temp_dirs["base"])

        args = {
            "name": "my-framework",
            "url": "https://my-framework.dev/",
            "description": "My framework skill",
        }

        result = await server_fastmcp.generate_config(**args)

        assert isinstance(result, str)
        assert "✅" in result or "Generated" in result.lower()

        # Verify config file was created
        config_path = temp_dirs["config"] / "my-framework.json"
        if not config_path.exists():
            config_path = temp_dirs["base"] / "configs" / "my-framework.json"

    async def test_generate_config_with_options(self, temp_dirs, monkeypatch):
        """Test config generation with custom options."""
        monkeypatch.chdir(temp_dirs["base"])

        args = {
            "name": "custom-framework",
            "url": "https://custom.dev/",
            "description": "Custom skill",
            "max_pages": 200,
            "rate_limit": 1.0,
        }

        result = await server_fastmcp.generate_config(**args)
        assert isinstance(result, str)

    async def test_generate_config_unlimited(self, temp_dirs, monkeypatch):
        """Test config generation with unlimited pages."""
        monkeypatch.chdir(temp_dirs["base"])

        args = {
            "name": "unlimited-framework",
            "url": "https://unlimited.dev/",
            "description": "Unlimited skill",
            "unlimited": True,
        }

        result = await server_fastmcp.generate_config(**args)
        assert isinstance(result, str)

    async def test_list_configs(self, temp_dirs):
        """Test listing available configs."""
        result = await server_fastmcp.list_configs()

        assert isinstance(result, str)
        # Should return some configs or indicate none available
        assert len(result) > 0

    async def test_validate_config_valid(self, sample_config):
        """Test validating a valid config file."""
        result = await server_fastmcp.validate_config(config_path=str(sample_config))

        assert isinstance(result, str)
        assert "✅" in result or "valid" in result.lower()

    async def test_validate_config_unified(self, unified_config):
        """Test validating a unified config file."""
        result = await server_fastmcp.validate_config(config_path=str(unified_config))

        assert isinstance(result, str)
        # Should detect unified format
        assert "unified" in result.lower() or "source" in result.lower()

    async def test_validate_config_missing_file(self, temp_dirs):
        """Test validating a non-existent config file."""
        result = await server_fastmcp.validate_config(
            config_path=str(temp_dirs["config"] / "nonexistent.json")
        )

        assert isinstance(result, str)
        # Should indicate error
        assert "error" in result.lower() or "❌" in result or "not found" in result.lower()


# ============================================================================
# SCRAPING TOOLS TESTS (4 tools)
# ============================================================================


@pytest.mark.skipif(not FASTMCP_AVAILABLE, reason="FastMCP not available")
@pytest.mark.asyncio
class TestScrapingTools:
    """Test scraping tools."""

    async def test_estimate_pages_basic(self, sample_config):
        """Test basic page estimation."""
        with patch("subprocess.run") as mock_run:
            mock_run.return_value = Mock(
                returncode=0, stdout="Estimated pages: 150\nRecommended max_pages: 200"
            )

            result = await server_fastmcp.estimate_pages(config_path=str(sample_config))

            assert isinstance(result, str)

    async def test_estimate_pages_unlimited(self, sample_config):
        """Test estimation with unlimited discovery."""
        result = await server_fastmcp.estimate_pages(config_path=str(sample_config), unlimited=True)

        assert isinstance(result, str)

    async def test_estimate_pages_custom_discovery(self, sample_config):
        """Test estimation with custom max_discovery."""
        result = await server_fastmcp.estimate_pages(
            config_path=str(sample_config), max_discovery=500
        )

        assert isinstance(result, str)

    async def test_scrape_docs_basic(self, sample_config):
        """Test basic documentation scraping."""
        with patch("subprocess.run") as mock_run:
            mock_run.return_value = Mock(returncode=0, stdout="Scraping completed successfully")

            result = await server_fastmcp.scrape_docs(config_path=str(sample_config), dry_run=True)

            assert isinstance(result, str)

    async def test_scrape_docs_with_enhancement(self, sample_config):
        """Test scraping with local enhancement."""
        result = await server_fastmcp.scrape_docs(
            config_path=str(sample_config), enhance_local=True, dry_run=True
        )

        assert isinstance(result, str)

    async def test_scrape_docs_skip_scrape(self, sample_config):
        """Test scraping with skip_scrape flag."""
        result = await server_fastmcp.scrape_docs(config_path=str(sample_config), skip_scrape=True)

        assert isinstance(result, str)

    async def test_scrape_docs_unified(self, unified_config):
        """Test scraping with unified config."""
        result = await server_fastmcp.scrape_docs(config_path=str(unified_config), dry_run=True)

        assert isinstance(result, str)

    async def test_scrape_docs_merge_mode_override(self, unified_config):
        """Test scraping with merge mode override."""
        result = await server_fastmcp.scrape_docs(
            config_path=str(unified_config), merge_mode="claude-enhanced", dry_run=True
        )

        assert isinstance(result, str)

    async def test_scrape_github_basic(self):
        """Test basic GitHub scraping wiring (in-process converter, no network).

        scrape_github routes through get_converter("github", config).run();
        mock that collaborator so the test verifies the wiring without a real
        GitHub API scrape.
        """
        mock_converter = Mock()
        mock_converter.run.return_value = 0

        with patch(
            "skill_seekers.cli.skill_converter.get_converter",
            return_value=mock_converter,
        ) as mock_get_converter:
            result = await server_fastmcp.scrape_github(
                repo="facebook/react", name="react-github-test"
            )

        mock_get_converter.assert_called_once()
        assert mock_get_converter.call_args.args[0] == "github"
        mock_converter.run.assert_called_once()
        assert isinstance(result, str)

    @pytest.mark.network
    async def test_scrape_github_with_token(self):
        """Test GitHub scraping with authentication token."""
        result = await server_fastmcp.scrape_github(
            repo="private/repo", token="fake_token_for_testing", name="private-test"
        )

        assert isinstance(result, str)

    @pytest.mark.network
    async def test_scrape_github_options(self):
        """Test GitHub scraping with various options."""
        result = await server_fastmcp.scrape_github(
            repo="test/repo",
            no_issues=True,
            no_changelog=True,
            no_releases=True,
            max_issues=50,
            scrape_only=True,
        )

        assert isinstance(result, str)

    async def test_scrape_pdf_basic(self, temp_dirs):
        """Test basic PDF scraping."""
        # Create a dummy PDF config
        pdf_config = {
            "name": "test-pdf",
            "pdf_path": "/path/to/test.pdf",
            "description": "Test PDF skill",
        }
        config_path = temp_dirs["config"] / "test-pdf.json"
        config_path.write_text(json.dumps(pdf_config))

        result = await server_fastmcp.scrape_pdf(config_path=str(config_path))

        assert isinstance(result, str)

    async def test_scrape_pdf_direct_path(self):
        """Test PDF scraping with direct path."""
        result = await server_fastmcp.scrape_pdf(
            pdf_path="/path/to/manual.pdf", name="manual-skill"
        )

        assert isinstance(result, str)

    async def test_scrape_codebase_basic(self, temp_dirs):
        """Test basic codebase scraping."""
        # Create a dummy source directory
        src_dir = temp_dirs["output"] / "test_codebase"
        src_dir.mkdir()
        (src_dir / "test.py").write_text("def hello(): pass")

        result = await server_fastmcp.scrape_codebase(
            directory=str(src_dir), output=str(temp_dirs["output"] / "codebase_analysis")
        )

        assert isinstance(result, str)

    async def test_scrape_codebase_with_options(self, temp_dirs):
        """Test codebase scraping with various options."""
        # Create a dummy source directory
        src_dir = temp_dirs["output"] / "test_codebase2"
        src_dir.mkdir()
        (src_dir / "main.py").write_text("class Foo: pass")
        (src_dir / "utils.js").write_text("function bar() {}")

        result = await server_fastmcp.scrape_codebase(
            directory=str(src_dir),
            depth="deep",
            languages="Python,JavaScript",
            file_patterns="*.py,*.js",
            build_api_reference=True,
        )

        assert isinstance(result, str)


# ============================================================================
# PACKAGING TOOLS TESTS (3 tools)
# ============================================================================


@pytest.mark.skipif(not FASTMCP_AVAILABLE, reason="FastMCP not available")
@pytest.mark.asyncio
class TestPackagingTools:
    """Test packaging and upload tools."""

    async def test_package_skill_basic(self, temp_dirs):
        """Test basic skill packaging."""
        # Create a mock skill directory
        skill_dir = temp_dirs["output"] / "test-skill"
        skill_dir.mkdir()
        (skill_dir / "SKILL.md").write_text("# Test Skill")

        with patch("skill_seekers.mcp.tools.packaging_tools.run_cli_main") as mock_run:
            mock_run.return_value = ("Packaging completed", "", 0)

            result = await server_fastmcp.package_skill(skill_dir=str(skill_dir), auto_upload=False)

            assert isinstance(result, str)

    async def test_package_skill_with_auto_upload(self, temp_dirs):
        """Test packaging with auto-upload."""
        skill_dir = temp_dirs["output"] / "test-skill"
        skill_dir.mkdir()
        (skill_dir / "SKILL.md").write_text("# Test Skill")

        result = await server_fastmcp.package_skill(skill_dir=str(skill_dir), auto_upload=True)

        assert isinstance(result, str)

    async def test_upload_skill_basic(self, temp_dirs):
        """Test basic skill upload."""
        # Create a mock zip file
        zip_path = temp_dirs["output"] / "test-skill.zip"
        zip_path.write_text("fake zip content")

        with patch("skill_seekers.mcp.tools.packaging_tools.run_cli_main") as mock_run:
            mock_run.return_value = ("Upload successful", "", 0)

            result = await server_fastmcp.upload_skill(skill_zip=str(zip_path))

            assert isinstance(result, str)

    async def test_upload_skill_missing_file(self, temp_dirs):
        """Test upload with missing file."""
        result = await server_fastmcp.upload_skill(
            skill_zip=str(temp_dirs["output"] / "nonexistent.zip")
        )

        assert isinstance(result, str)

    async def test_install_skill_with_config_name(self):
        """Test complete install workflow with config name."""
        # Mock the fetch_config_tool import that install_skill_tool uses
        with patch("skill_seekers.mcp.tools.source_tools.fetch_config_tool") as mock_fetch:
            mock_fetch.return_value = [Mock(text="Config fetched")]

            result = await server_fastmcp.install_skill(
                config_name="react", destination="output", dry_run=True
            )

            assert isinstance(result, str)

    async def test_install_skill_with_config_path(self, sample_config):
        """Test complete install workflow with config path."""
        with patch("skill_seekers.mcp.tools.source_tools.fetch_config_tool") as mock_fetch:
            mock_fetch.return_value = [Mock(text="Config ready")]

            result = await server_fastmcp.install_skill(
                config_path=str(sample_config), destination="output", dry_run=True
            )

            assert isinstance(result, str)

    async def test_install_skill_unlimited(self):
        """Test install workflow with unlimited pages."""
        with patch("skill_seekers.mcp.tools.source_tools.fetch_config_tool") as mock_fetch:
            mock_fetch.return_value = [Mock(text="Config fetched")]

            result = await server_fastmcp.install_skill(
                config_name="react", unlimited=True, dry_run=True
            )

            assert isinstance(result, str)

    async def test_install_skill_no_upload(self):
        """Test install workflow without auto-upload."""
        with patch("skill_seekers.mcp.tools.source_tools.fetch_config_tool") as mock_fetch:
            mock_fetch.return_value = [Mock(text="Config fetched")]

            result = await server_fastmcp.install_skill(
                config_name="react", auto_upload=False, dry_run=True
            )

            assert isinstance(result, str)


# ============================================================================
# SPLITTING TOOLS TESTS (2 tools)
# ============================================================================


@pytest.mark.skipif(not FASTMCP_AVAILABLE, reason="FastMCP not available")
@pytest.mark.asyncio
class TestSplittingTools:
    """Test config splitting and router generation tools."""

    async def test_split_config_auto_strategy(self, sample_config):
        """Test config splitting with auto strategy."""
        result = await server_fastmcp.split_config(
            config_path=str(sample_config), strategy="auto", dry_run=True
        )

        assert isinstance(result, str)

    async def test_split_config_category_strategy(self, sample_config):
        """Test config splitting with category strategy."""
        result = await server_fastmcp.split_config(
            config_path=str(sample_config), strategy="category", target_pages=5000, dry_run=True
        )

        assert isinstance(result, str)

    async def test_split_config_size_strategy(self, sample_config):
        """Test config splitting with size strategy."""
        result = await server_fastmcp.split_config(
            config_path=str(sample_config), strategy="size", target_pages=3000, dry_run=True
        )

        assert isinstance(result, str)

    async def test_generate_router_basic(self, temp_dirs):
        """Test router generation."""
        # Create some mock config files
        (temp_dirs["config"] / "godot-scripting.json").write_text("{}")
        (temp_dirs["config"] / "godot-physics.json").write_text("{}")

        result = await server_fastmcp.generate_router(
            config_pattern=str(temp_dirs["config"] / "godot-*.json")
        )

        assert isinstance(result, str)

    async def test_generate_router_with_name(self, temp_dirs):
        """Test router generation with custom name."""
        result = await server_fastmcp.generate_router(
            config_pattern=str(temp_dirs["config"] / "godot-*.json"), router_name="godot-hub"
        )

        assert isinstance(result, str)


# ============================================================================
# SOURCE TOOLS TESTS (5 tools)
# ============================================================================


@pytest.mark.skipif(not FASTMCP_AVAILABLE, reason="FastMCP not available")
@pytest.mark.asyncio
class TestSourceTools:
    """Test config source management tools."""

    async def test_fetch_config_list_api(self):
        """Test fetching config list from API."""
        with patch("skill_seekers.mcp.tools.source_tools.httpx.AsyncClient") as mock_client:
            mock_response = MagicMock()
            mock_response.json.return_value = {
                "configs": [
                    {"name": "react", "category": "web-frameworks"},
                    {"name": "vue", "category": "web-frameworks"},
                ],
                "total": 2,
            }
            mock_client.return_value.__aenter__.return_value.get.return_value = mock_response

            result = await server_fastmcp.fetch_config(list_available=True)

            assert isinstance(result, str)

    async def test_fetch_config_download_api(self, temp_dirs):
        """Test downloading specific config from API."""
        result = await server_fastmcp.fetch_config(
            config_name="react", destination=str(temp_dirs["config"])
        )

        assert isinstance(result, str)

    async def test_fetch_config_with_category_filter(self):
        """Test fetching configs with category filter."""
        result = await server_fastmcp.fetch_config(list_available=True, category="web-frameworks")

        assert isinstance(result, str)

    async def test_fetch_config_from_git_url(self, temp_dirs):
        """Test fetching config from git URL."""
        result = await server_fastmcp.fetch_config(
            config_name="react",
            git_url="https://github.com/myorg/configs.git",
            destination=str(temp_dirs["config"]),
        )

        assert isinstance(result, str)

    async def test_fetch_config_from_source(self, temp_dirs):
        """Test fetching config from named source."""
        result = await server_fastmcp.fetch_config(
            config_name="react", source="team", destination=str(temp_dirs["config"])
        )

        assert isinstance(result, str)

    async def test_fetch_config_with_token(self, temp_dirs):
        """Test fetching config with authentication token."""
        result = await server_fastmcp.fetch_config(
            config_name="react",
            git_url="https://github.com/private/configs.git",
            token="fake_token",
            destination=str(temp_dirs["config"]),
        )

        assert isinstance(result, str)

    async def test_fetch_config_refresh_cache(self, temp_dirs):
        """Test fetching config with cache refresh."""
        result = await server_fastmcp.fetch_config(
            config_name="react",
            git_url="https://github.com/myorg/configs.git",
            refresh=True,
            destination=str(temp_dirs["config"]),
        )

        assert isinstance(result, str)

    async def test_submit_config_with_path(self, sample_config):
        """Test submitting config from file path."""
        result = await server_fastmcp.submit_config(
            config_path=str(sample_config), testing_notes="Tested with 20 pages, works well"
        )

        assert isinstance(result, str)

    async def test_submit_config_with_json(self):
        """Test submitting config as JSON string."""
        config_json = json.dumps({"name": "my-framework", "base_url": "https://my-framework.dev/"})

        result = await server_fastmcp.submit_config(
            config_json=config_json, testing_notes="Works great!"
        )

        assert isinstance(result, str)

    async def test_add_config_source_basic(self):
        """Test adding a config source."""
        result = await server_fastmcp.add_config_source(
            name="team", git_url="https://github.com/myorg/configs.git"
        )

        assert isinstance(result, str)

    async def test_add_config_source_with_options(self):
        """Test adding config source with all options."""
        result = await server_fastmcp.add_config_source(
            name="company",
            git_url="https://gitlab.com/mycompany/configs.git",
            source_type="gitlab",
            token_env="GITLAB_TOKEN",
            branch="develop",
            priority=50,
            enabled=True,
        )

        assert isinstance(result, str)

    async def test_add_config_source_ssh_url(self):
        """Test adding config source with SSH URL."""
        result = await server_fastmcp.add_config_source(
            name="private", git_url="git@github.com:myorg/private-configs.git", source_type="github"
        )

        assert isinstance(result, str)

    async def test_list_config_sources_all(self):
        """Test listing all config sources."""
        result = await server_fastmcp.list_config_sources(enabled_only=False)

        assert isinstance(result, str)

    async def test_list_config_sources_enabled_only(self):
        """Test listing only enabled sources."""
        result = await server_fastmcp.list_config_sources(enabled_only=True)

        assert isinstance(result, str)

    async def test_remove_config_source(self):
        """Test removing a config source."""
        result = await server_fastmcp.remove_config_source(name="team")

        assert isinstance(result, str)


# ============================================================================
# INTEGRATION TESTS
# ============================================================================


@pytest.mark.skipif(not FASTMCP_AVAILABLE, reason="FastMCP not available")
@pytest.mark.asyncio
class TestFastMCPIntegration:
    """Test integration scenarios across multiple tools."""

    async def test_workflow_generate_validate_scrape(self, temp_dirs, monkeypatch):
        """Test complete workflow: generate → validate → scrape."""
        monkeypatch.chdir(temp_dirs["base"])

        # Step 1: Generate config
        result1 = await server_fastmcp.generate_config(
            name="workflow-test", url="https://workflow.dev/", description="Workflow test"
        )
        assert isinstance(result1, str)

        # Step 2: Validate config
        config_path = temp_dirs["base"] / "configs" / "workflow-test.json"
        if config_path.exists():
            result2 = await server_fastmcp.validate_config(config_path=str(config_path))
            assert isinstance(result2, str)

    async def test_workflow_source_fetch_scrape(self, temp_dirs):
        """Test workflow: add source → fetch config → scrape."""
        # Step 1: Add source
        result1 = await server_fastmcp.add_config_source(
            name="test-source", git_url="https://github.com/test/configs.git"
        )
        assert isinstance(result1, str)

        # Step 2: Fetch config
        result2 = await server_fastmcp.fetch_config(
            config_name="react", source="test-source", destination=str(temp_dirs["config"])
        )
        assert isinstance(result2, str)

    async def test_workflow_split_router(self, sample_config, temp_dirs):
        """Test workflow: split config → generate router."""
        # Step 1: Split config
        result1 = await server_fastmcp.split_config(
            config_path=str(sample_config), strategy="category", dry_run=True
        )
        assert isinstance(result1, str)

        # Step 2: Generate router
        result2 = await server_fastmcp.generate_router(
            config_pattern=str(temp_dirs["config"] / "test-framework-*.json")
        )
        assert isinstance(result2, str)


# ============================================================================
# ERROR HANDLING TESTS
# ============================================================================


@pytest.mark.skipif(not FASTMCP_AVAILABLE, reason="FastMCP not available")
@pytest.mark.asyncio
class TestErrorHandling:
    """Test error handling across all tools."""

    async def test_generate_config_invalid_url(self, temp_dirs, monkeypatch):
        """Test error handling for invalid URL."""
        monkeypatch.chdir(temp_dirs["base"])

        result = await server_fastmcp.generate_config(
            name="invalid-test", url="not-a-valid-url", description="Test invalid URL"
        )

        assert isinstance(result, str)
        # Should indicate error or handle gracefully

    async def test_validate_config_invalid_json(self, temp_dirs):
        """Test error handling for invalid JSON."""
        bad_config = temp_dirs["config"] / "bad.json"
        bad_config.write_text("{ invalid json }")

        result = await server_fastmcp.validate_config(config_path=str(bad_config))

        assert isinstance(result, str)

    async def test_scrape_docs_missing_config(self):
        """Test error handling for missing config file."""
        # This should handle the error gracefully and return a string
        try:
            result = await server_fastmcp.scrape_docs(config_path="/nonexistent/config.json")
            assert isinstance(result, str)
            # Should contain error message
            assert "error" in result.lower() or "not found" in result.lower() or "❌" in result
        except FileNotFoundError:
            # If it raises, that's also acceptable error handling
            pass

    async def test_package_skill_missing_directory(self):
        """Test error handling for missing skill directory."""
        result = await server_fastmcp.package_skill(skill_dir="/nonexistent/skill")

        assert isinstance(result, str)


# ============================================================================
# TYPE VALIDATION TESTS
# ============================================================================


@pytest.mark.skipif(not FASTMCP_AVAILABLE, reason="FastMCP not available")
@pytest.mark.asyncio
class TestTypeValidation:
    """Test type validation for tool parameters."""

    async def test_generate_config_return_type(self, temp_dirs, monkeypatch):
        """Test that generate_config returns string."""
        monkeypatch.chdir(temp_dirs["base"])

        result = await server_fastmcp.generate_config(
            name="type-test", url="https://test.dev/", description="Type test"
        )

        assert isinstance(result, str)

    async def test_list_configs_return_type(self):
        """Test that list_configs returns string."""
        result = await server_fastmcp.list_configs()
        assert isinstance(result, str)

    async def test_estimate_pages_return_type(self, sample_config):
        """Test that estimate_pages returns string."""
        result = await server_fastmcp.estimate_pages(config_path=str(sample_config))
        assert isinstance(result, str)

    async def test_all_tools_return_strings(self, sample_config, temp_dirs):
        """Test that all tools return string type."""
        # Sample a few tools from each category
        tools_to_test = [
            (server_fastmcp.validate_config, {"config_path": str(sample_config)}),
            (server_fastmcp.list_configs, {}),
            (server_fastmcp.list_config_sources, {"enabled_only": False}),
        ]

        for tool_func, args in tools_to_test:
            result = await tool_func(**args)
            assert isinstance(result, str), f"{tool_func.__name__} should return string"


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
