"""Parametrized tests for MCP configuration screen functionality."""

import json
from pathlib import Path
from unittest.mock import patch

import pytest
from openhands_cli.locations import MCP_CONFIG_FILE
from openhands_cli.tui.settings.mcp_screen import MCPScreen

from openhands.sdk import LLM, Agent


@pytest.fixture
def persistence_dir(tmp_path, monkeypatch):
    """Patch PERSISTENCE_DIR to tmp and return the directory Path."""
    monkeypatch.setattr(
        'openhands_cli.tui.settings.mcp_screen.PERSISTENCE_DIR',
        str(tmp_path),
        raising=True,
    )
    return tmp_path


def _create_agent(mcp_config=None) -> Agent:
    if mcp_config is None:
        mcp_config = {}
    return Agent(
        llm=LLM(model='test-model', api_key='test-key', usage_id='test-service'),
        tools=[],
        mcp_config=mcp_config,
    )


def _maybe_write_mcp_file(dirpath: Path, file_content):
    """Write mcp.json if file_content is provided.

    file_content:
      - None     -> do not create file (missing)
      - "INVALID"-> write invalid JSON
      - dict     -> dump as JSON
    """
    if file_content is None:
        return
    cfg_path = dirpath / MCP_CONFIG_FILE
    if file_content == 'INVALID':
        cfg_path.write_text('{"invalid": json content}')
    else:
        cfg_path.write_text(json.dumps(file_content))


# Shared "always expected" help text snippets
ALWAYS_EXPECTED = [
    'MCP (Model Context Protocol) Configuration',
    'To get started:',
    '~/.openhands/mcp.json',
    'https://gofastmcp.com/clients/client#configuration-format',
    'Restart your OpenHands session',
]


CASES = [
    # Agent has an existing server; should list "Current Agent MCP Servers"
    dict(
        id='agent_has_existing',
        agent_mcp={
            'mcpServers': {
                'existing_server': {
                    'command': 'python',
                    'args': ['-m', 'existing_server'],
                }
            }
        },
        file_content=None,  # no incoming file
        expected=[
            'Current Agent MCP Servers:',
            'existing_server',
        ],
        unexpected=[],
    ),
    # Agent has none; should show "None configured on the current agent"
    dict(
        id='agent_has_none',
        agent_mcp={},
        file_content=None,
        expected=[
            'Current Agent MCP Servers:',
            'None configured on the current agent',
        ],
        unexpected=[],
    ),
    # New servers present only in mcp.json
    dict(
        id='new_servers_on_restart',
        agent_mcp={},
        file_content={
            'mcpServers': {
                'fetch': {'command': 'uvx', 'args': ['mcp-server-fetch']},
                'notion': {'url': 'https://mcp.notion.com/mcp', 'auth': 'oauth'},
            }
        },
        expected=[
            'Incoming Servers on Restart',
            'New servers (will be added):',
            'fetch',
            'notion',
        ],
        unexpected=[],
    ),
    # Overriding/updating servers present in both agent and mcp.json (but different config)
    dict(
        id='overriding_servers_on_restart',
        agent_mcp={
            'mcpServers': {
                'fetch': {'command': 'python', 'args': ['-m', 'old_fetch_server']}
            }
        },
        file_content={
            'mcpServers': {'fetch': {'command': 'uvx', 'args': ['mcp-server-fetch']}}
        },
        expected=[
            'Incoming Servers on Restart',
            'Updated servers (configuration will change):',
            'fetch',
            'Current:',
            'Incoming:',
        ],
        unexpected=[],
    ),
    # All servers already synced (matching config)
    dict(
        id='already_synced',
        agent_mcp={
            'mcpServers': {
                'fetch': {
                    'command': 'uvx',
                    'args': ['mcp-server-fetch'],
                    'env': {},
                    'transport': 'stdio',
                }
            }
        },
        file_content={
            'mcpServers': {'fetch': {'command': 'uvx', 'args': ['mcp-server-fetch']}}
        },
        expected=[
            'Incoming Servers on Restart',
            'All configured servers match the current agent configuration',
        ],
        unexpected=[],
    ),
    # Invalid JSON file handling
    dict(
        id='invalid_json_file',
        agent_mcp={},
        file_content='INVALID',
        expected=[
            'Invalid MCP configuration file',
            'Please check your configuration file format',
        ],
        unexpected=[],
    ),
    # Missing JSON file handling
    dict(
        id='missing_json_file',
        agent_mcp={},
        file_content=None,  # explicitly missing
        expected=[
            'Configuration file not found',
            'No incoming servers detected for next restart',
        ],
        unexpected=[],
    ),
]


@pytest.mark.parametrize('case', CASES, ids=[c['id'] for c in CASES])
@patch('openhands_cli.tui.settings.mcp_screen.print_formatted_text')
def test_display_mcp_info_parametrized(mock_print, case, persistence_dir):
    """Table-driven test for MCPScreen.display_mcp_info covering all scenarios."""
    # Arrange
    agent = _create_agent(case['agent_mcp'])
    _maybe_write_mcp_file(persistence_dir, case['file_content'])
    screen = MCPScreen()

    # Act
    screen.display_mcp_info(agent)

    # Gather output
    all_calls = [str(call_args) for call_args in mock_print.call_args_list]
    content = ' '.join(all_calls)

    # Invariants: help instructions should always be present
    for snippet in ALWAYS_EXPECTED:
        assert snippet in content, f'Missing help snippet: {snippet}'

    # Scenario-specific expectations
    for snippet in case['expected']:
        assert snippet in content, (
            f'Expected snippet not found for case {case["id"]}: {snippet}'
        )

    for snippet in case.get('unexpected', []):
        assert snippet not in content, (
            f'Unexpected snippet found for case {case["id"]}: {snippet}'
        )
