"""Auto-fixture and test file generator for MCP servers.

Used by `forge record` and `forge init` CLI commands to auto-generate
test files and fixtures from a running server.
"""

from __future__ import annotations

import json
import textwrap
from pathlib import Path
from typing import Any

from mcp_forge._types import ToolInfo
from mcp_forge.schema import generate_sample_input


def generate_test_file(
    tools: list[ToolInfo],
    server_command: str,
    output_path: str | Path,
) -> Path:
    """Generate a complete pytest test file for the given server.

    Introspects all tools and creates test stubs with example calls.
    """
    output = Path(output_path)

    tool_tests = []
    for tool in sorted(tools, key=lambda t: t.name):
        sample = generate_sample_input(tool.input_schema)
        safe_name = tool.name.replace("-", "_").replace(".", "_")

        args_str = json.dumps(sample, indent=8) if sample else "None"

        tool_tests.append(
            f'''
@pytest.mark.asyncio
async def test_{safe_name}_exists(client):
    """Verify the '{tool.name}' tool is exposed."""
    tools = await client.list_tools()
    assert_tool_exists(tools, "{tool.name}")


@pytest.mark.asyncio
async def test_{safe_name}_call(client):
    """Call '{tool.name}' with sample input and verify it succeeds."""
    result = await client.call_tool(
        "{tool.name}",
        {args_str},
    )
    assert_no_error(result)


@pytest.mark.asyncio
async def test_{safe_name}_snapshot(client, forge_snapshot):
    """Snapshot test for '{tool.name}'."""
    result = await client.call_tool(
        "{tool.name}",
        {args_str},
    )
    forge_snapshot.assert_match(result)
'''
        )

    safe_output = str(output).replace("\\", "/")
    content = f'''"""Auto-generated tests for MCP server: {server_command}

Generated by mcp-forge. Edit freely — this is your test file now.
Re-generate with: forge init "{server_command}" -o {safe_output}
"""

import pytest
from mcp_forge import MCPTestClient
from mcp_forge.assertions import assert_tool_exists, assert_no_error, assert_tool_count


SERVER_COMMAND = "{server_command}"


@pytest.fixture
async def client():
    """Create a test client connected to the MCP server."""
    async with MCPTestClient.from_command(SERVER_COMMAND) as c:
        yield c


# ── Discovery Tests ──

@pytest.mark.asyncio
async def test_server_connects(client):
    """Verify the server starts and responds to initialization."""
    tools = await client.list_tools()
    assert isinstance(tools, list)


@pytest.mark.asyncio
async def test_tool_count(client):
    """Verify the expected number of tools."""
    tools = await client.list_tools()
    assert_tool_count(tools, {len(tools)})


@pytest.mark.asyncio
async def test_schema_snapshot(client, forge_snapshot):
    """Snapshot the entire tool schema surface — detects API changes."""
    tools = await client.list_tools()
    forge_snapshot.assert_schema_unchanged(tools)


# ── Per-Tool Tests ──
{"".join(tool_tests)}
'''

    output.parent.mkdir(parents=True, exist_ok=True)
    output.write_text(content.strip() + "\n", encoding="utf-8")
    return output


def record_fixtures(
    tools: list[ToolInfo],
    results: dict[str, Any],
    output_dir: str | Path,
) -> list[Path]:
    """Save tool call results as JSON fixture files.

    Args:
        tools: List of tools from the server.
        results: Dict of {tool_name: ToolResult.to_dict()}.
        output_dir: Directory to write fixture files to.

    Returns:
        List of created file paths.
    """
    out = Path(output_dir)
    out.mkdir(parents=True, exist_ok=True)

    created: list[Path] = []

    for tool in sorted(tools, key=lambda t: t.name):
        tool_dir = out / tool.name
        tool_dir.mkdir(parents=True, exist_ok=True)

        # Save tool schema
        schema_path = tool_dir / "schema.json"
        schema_path.write_text(
            json.dumps(tool.to_dict(), indent=2, ensure_ascii=False) + "\n",
            encoding="utf-8",
        )
        created.append(schema_path)

        # Save sample input
        sample = generate_sample_input(tool.input_schema)
        input_path = tool_dir / "sample_input.json"
        input_path.write_text(
            json.dumps(sample, indent=2, ensure_ascii=False) + "\n",
            encoding="utf-8",
        )
        created.append(input_path)

        # Save result if we have one
        if tool.name in results:
            result_path = tool_dir / "sample_output.json"
            result_path.write_text(
                json.dumps(results[tool.name], indent=2, ensure_ascii=False) + "\n",
                encoding="utf-8",
            )
            created.append(result_path)

    return created
