"""Tests for MCP discoverability validation (Issue #155)."""
import os
import sys
import json
import tempfile
import unittest
from unittest.mock import patch, MagicMock

sys.path.insert(0, str(__import__("pathlib").Path(__file__).resolve().parent.parent))

from mcp_discoverability import (
    validate_mcp_client_config,
    RELOAD_INSTRUCTIONS,
    EXPECTED_JENKINS_TOOLS,
    EXPECTED_GLITCHTIP_TOOLS,
)

class TestMcpDiscoverability(unittest.TestCase):
    def setUp(self):
        self.tmp_dir = tempfile.TemporaryDirectory()
        self.gitea_config_path = os.path.join(self.tmp_dir.name, "gitea-mcp.json")
        self.client_config_path = os.path.join(self.tmp_dir.name, "claude_desktop_config.json")

    def tearDown(self):
        self.tmp_dir.cleanup()

    def _write_json(self, path, data):
        with open(path, "w", encoding="utf-8") as fh:
            json.dump(data, fh)

    def _v2_gitea_config(self, jenkins_enabled=True, glitchtip_enabled=False):
        return {
            "version": 2,
            "contexts": {
                "prod": {
                    "enabled": True,
                    "services": {
                        "jenkins": {
                            "enabled": jenkins_enabled,
                            "kind": "jenkins",
                            "capabilities": ["read"]
                        },
                        "glitchtip": {
                            "enabled": glitchtip_enabled,
                            "kind": "glitchtip",
                            "capabilities": ["read"]
                        }
                    }
                }
            }
        }

    def test_static_validation_success(self):
        g_cfg = self._v2_gitea_config(jenkins_enabled=True, glitchtip_enabled=True)
        self._write_json(self.gitea_config_path, g_cfg)

        c_cfg = {
            "mcpServers": {
                "jenkins-mcp": {
                    "command": "python3",
                    "args": ["-m", "jenkins_mcp"],
                    "env": {
                        "JENKINS_MCP_PROFILE": "jenkins-readonly",
                        "JENKINS_MCP_CONFIG": "/path/to/config.json"
                    }
                },
                "glitchtip-mcp": {
                    "command": "python3",
                    "args": ["-m", "glitchtip_mcp"],
                    "env": {
                        "GLITCHTIP_MCP_PROFILE": "glitchtip-readonly",
                        "GLITCHTIP_MCP_CONFIG": "/path/to/config.json"
                    }
                }
            }
        }
        self._write_json(self.client_config_path, c_cfg)

        res = validate_mcp_client_config(
            client_config_path=self.client_config_path,
            gitea_config_path=self.gitea_config_path,
            live=False
        )
        self.assertTrue(res)

    def test_stale_server_name_rejected(self):
        g_cfg = self._v2_gitea_config(jenkins_enabled=True)
        self._write_json(self.gitea_config_path, g_cfg)

        # Uses stale key in client config
        c_cfg = {
            "mcpServers": {
                "jenkins-readonly": {
                    "command": "python3",
                    "args": ["-m", "jenkins_mcp"],
                    "env": {
                        "JENKINS_MCP_PROFILE": "jenkins-readonly",
                        "JENKINS_MCP_CONFIG": "/path/to/config.json"
                    }
                }
            }
        }
        self._write_json(self.client_config_path, c_cfg)

        res = validate_mcp_client_config(
            client_config_path=self.client_config_path,
            gitea_config_path=self.gitea_config_path,
            live=False
        )
        self.assertFalse(res)

    def test_incorrect_arguments_rejected(self):
        g_cfg = self._v2_gitea_config(jenkins_enabled=True)
        self._write_json(self.gitea_config_path, g_cfg)

        # Missing -m or wrong module
        c_cfg = {
            "mcpServers": {
                "jenkins-mcp": {
                    "command": "python3",
                    "args": ["wrong_runner.py"],
                    "env": {
                        "JENKINS_MCP_PROFILE": "jenkins-readonly",
                        "JENKINS_MCP_CONFIG": "/path/to/config.json"
                    }
                }
            }
        }
        self._write_json(self.client_config_path, c_cfg)

        res = validate_mcp_client_config(
            client_config_path=self.client_config_path,
            gitea_config_path=self.gitea_config_path,
            live=False
        )
        self.assertFalse(res)

    def test_missing_required_env_rejected(self):
        g_cfg = self._v2_gitea_config(jenkins_enabled=True)
        self._write_json(self.gitea_config_path, g_cfg)

        # Missing JENKINS_MCP_PROFILE env variable
        c_cfg = {
            "mcpServers": {
                "jenkins-mcp": {
                    "command": "python3",
                    "args": ["-m", "jenkins_mcp"],
                    "env": {
                        "JENKINS_MCP_CONFIG": "/path/to/config.json"
                    }
                }
            }
        }
        self._write_json(self.client_config_path, c_cfg)

        res = validate_mcp_client_config(
            client_config_path=self.client_config_path,
            gitea_config_path=self.gitea_config_path,
            live=False
        )
        self.assertFalse(res)

    @patch("subprocess.Popen")
    def test_live_check_verification_success(self, mock_popen):
        g_cfg = self._v2_gitea_config(jenkins_enabled=True)
        self._write_json(self.gitea_config_path, g_cfg)

        c_cfg = {
            "mcpServers": {
                "jenkins-mcp": {
                    "command": "python3",
                    "args": ["-m", "jenkins_mcp"],
                    "env": {
                        "JENKINS_MCP_PROFILE": "jenkins-readonly",
                        "JENKINS_MCP_CONFIG": "/path/to/config.json"
                    }
                }
            }
        }
        self._write_json(self.client_config_path, c_cfg)

        # Mock stdout lines for JSON-RPC
        mock_proc = MagicMock()
        mock_popen.return_value = mock_proc

        init_resp = json.dumps({"jsonrpc": "2.0", "id": 1, "result": {"protocolVersion": "2024-11-05"}})
        tools_resp = json.dumps({
            "jsonrpc": "2.0",
            "id": 2,
            "result": {
                "tools": [{"name": tool} for tool in EXPECTED_JENKINS_TOOLS]
            }
        })

        mock_proc.stdout.readline.side_effect = [
            init_resp + "\n",
            tools_resp + "\n",
            ""
        ]

        res = validate_mcp_client_config(
            client_config_path=self.client_config_path,
            gitea_config_path=self.gitea_config_path,
            live=True
        )
        self.assertTrue(res)

    @patch("subprocess.Popen")
    def test_live_check_empty_toolset_rejected(self, mock_popen):
        g_cfg = self._v2_gitea_config(jenkins_enabled=True)
        self._write_json(self.gitea_config_path, g_cfg)

        c_cfg = {
            "mcpServers": {
                "jenkins-mcp": {
                    "command": "python3",
                    "args": ["-m", "jenkins_mcp"],
                    "env": {
                        "JENKINS_MCP_PROFILE": "jenkins-readonly",
                        "JENKINS_MCP_CONFIG": "/path/to/config.json"
                    }
                }
            }
        }
        self._write_json(self.client_config_path, c_cfg)

        mock_proc = MagicMock()
        mock_popen.return_value = mock_proc

        init_resp = json.dumps({"jsonrpc": "2.0", "id": 1, "result": {}})
        tools_resp = json.dumps({
            "jsonrpc": "2.0",
            "id": 2,
            "result": {
                "tools": []  # empty tool list
            }
        })

        mock_proc.stdout.readline.side_effect = [
            init_resp + "\n",
            tools_resp + "\n",
            ""
        ]

        # Should return False (coverage fails) when no tools are exposed
        res = validate_mcp_client_config(
            client_config_path=self.client_config_path,
            gitea_config_path=self.gitea_config_path,
            live=True
        )
        self.assertFalse(res)

    def test_runbook_instructions_content(self):
        self.assertIn("MCP CLIENT RELOAD/RECONNECT RUNBOOK", RELOAD_INSTRUCTIONS)
        self.assertIn("Codex", RELOAD_INSTRUCTIONS)
        self.assertIn("Gemini", RELOAD_INSTRUCTIONS)
        self.assertIn("Claude Desktop", RELOAD_INSTRUCTIONS)

if __name__ == "__main__":
    unittest.main()
