"""
Playwright MCP Installation Validation Tests
Tests for verifying the correct installation and functionality of Playwright MCP
"""

import json
import os
import subprocess
import sys
from pathlib import Path


class PlaywrightMCPValidator:
    """Validator for Playwright MCP installation"""

    def __init__(self):
        self.results = {}
        self.playwright_mcp_path = Path("/home/jay/workspace/mcp/playwright-mcp")
        self.cache_path = Path("/home/jay/.cache/ms-playwright/")
        self.settings_json_path = Path("/home/jay/.claude/settings.json")

    def test_package_installation(self):
        """Test 1: Verify @playwright/mcp package is installed"""
        result = {"status": "FAIL", "message": "", "details": {}}

        try:
            # Check if directory exists
            mcp_dir = self.playwright_mcp_path / "node_modules/@playwright/mcp"
            if not mcp_dir.exists():
                result["message"] = f"Directory not found: {mcp_dir}"
                return result

            # Check for essential files
            essential_files = ["package.json", "index.js", "cli.js"]
            missing_files = []
            for file in essential_files:
                if not (mcp_dir / file).exists():
                    missing_files.append(file)

            if missing_files:
                result["message"] = f"Missing files: {', '.join(missing_files)}"
                return result

            # Check npm ls for version
            cmd = f"cd {self.playwright_mcp_path} && npm ls @playwright/mcp"
            output = subprocess.run(cmd, shell=True, capture_output=True, text=True)

            if "@playwright/mcp@0.0.68" in output.stdout:
                result["status"] = "PASS"
                result["message"] = "Package installed with correct version"
                result["details"]["version"] = "0.0.68"
                result["details"]["path"] = str(mcp_dir)
            else:
                result["message"] = f"Unexpected version or installation issue. Output: {output.stdout}"

        except Exception as e:
            result["message"] = str(e)

        return result

    def test_chromium_binaries(self):
        """Test 2: Verify Chromium browser binaries are installed"""
        result = {"status": "FAIL", "message": "", "details": {}}

        try:
            if not self.cache_path.exists():
                result["message"] = f"Cache directory not found: {self.cache_path}"
                return result

            # Check for chromium directories
            chromium_dirs = list(self.cache_path.glob("chromium*"))
            if not chromium_dirs:
                result["message"] = "No chromium directories found"
                return result

            # Check for INSTALLATION_COMPLETE markers
            installation_complete_files = list(self.cache_path.glob("*/INSTALLATION_COMPLETE"))
            if not installation_complete_files:
                result["message"] = "INSTALLATION_COMPLETE markers not found"
                return result

            result["status"] = "PASS"
            result["message"] = "Chromium binaries properly installed"
            result["details"]["chromium_dirs"] = [d.name for d in chromium_dirs]
            result["details"]["installation_complete_count"] = len(installation_complete_files)

        except Exception as e:
            result["message"] = str(e)

        return result

    def test_mcp_handshake(self):
        """Test 3: Test MCP protocol initialize handshake"""
        result = {"status": "FAIL", "message": "", "details": {}}

        try:
            initialize_msg = json.dumps({
                "jsonrpc": "2.0",
                "id": 1,
                "method": "initialize",
                "params": {
                    "protocolVersion": "2024-11-05",
                    "capabilities": {},
                    "clientInfo": {"name": "test", "version": "1.0"}
                }
            })

            cmd = f"cd {self.playwright_mcp_path} && echo '{initialize_msg}' | timeout 15 npx -y @playwright/mcp --headless 2>/dev/null"
            output = subprocess.run(cmd, shell=True, capture_output=True, text=True)

            if not output.stdout:
                result["message"] = "No response from MCP server"
                return result

            # Parse the response
            try:
                response = json.loads(output.stdout.strip())

                # Check for required fields
                if "result" not in response:
                    result["message"] = "Missing 'result' in response"
                    return result

                if "serverInfo" not in response["result"]:
                    result["message"] = "Missing 'serverInfo' in result"
                    return result

                server_info = response["result"]["serverInfo"]
                if server_info.get("name") != "Playwright":
                    result["message"] = f"Unexpected server name: {server_info.get('name')}"
                    return result

                if server_info.get("version") != "0.0.68":
                    result["message"] = f"Unexpected version: {server_info.get('version')}"
                    return result

                result["status"] = "PASS"
                result["message"] = "MCP handshake successful"
                result["details"]["server_name"] = server_info.get("name")
                result["details"]["server_version"] = server_info.get("version")
                result["details"]["protocol_version"] = response["result"].get("protocolVersion")

            except json.JSONDecodeError as e:
                result["message"] = f"Failed to parse JSON response: {e}. Raw output: {output.stdout[:200]}"

        except Exception as e:
            result["message"] = str(e)

        return result

    def test_tools_list(self):
        """Test 4: Verify tools/list returns available tools"""
        result = {"status": "FAIL", "message": "", "details": {}}

        try:
            messages = (
                '{"jsonrpc":"2.0","id":1,"method":"initialize","params":'
                '{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}\n'
                '{"jsonrpc":"2.0","id":2,"method":"notifications/initialized"}\n'
                '{"jsonrpc":"2.0","id":3,"method":"tools/list","params":{}}\n'
            )

            # Use a more reliable method for multi-line input
            cmd = f"cd {self.playwright_mcp_path} && timeout 15 npx -y @playwright/mcp --headless 2>/dev/null"
            process = subprocess.Popen(
                cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                stderr=subprocess.PIPE, text=True
            )
            output, _ = process.communicate(input=messages, timeout=15)

            if not output:
                result["message"] = "No response from MCP server"
                return result

            # Parse all responses
            responses = []
            for line in output.strip().split("\n"):
                if line.strip():
                    try:
                        responses.append(json.loads(line))
                    except json.JSONDecodeError:
                        continue

            # Find the tools/list response (id=3)
            tools_response = None
            for resp in responses:
                if resp.get("id") == 3 and "result" in resp:
                    tools_response = resp
                    break

            if not tools_response:
                result["message"] = "No tools/list response found"
                return result

            tools = tools_response["result"].get("tools", [])
            if not tools:
                result["message"] = "No tools returned"
                return result

            # Check for essential tools
            tool_names = {tool["name"] for tool in tools}
            essential_tools = ["browser_navigate", "browser_click", "browser_take_screenshot"]
            missing_tools = [t for t in essential_tools if t not in tool_names]

            if missing_tools:
                result["message"] = f"Missing essential tools: {', '.join(missing_tools)}"
                result["details"]["available_tools"] = list(tool_names)
                return result

            result["status"] = "PASS"
            result["message"] = "Tools list retrieved successfully"
            result["details"]["total_tools"] = len(tools)
            result["details"]["essential_tools_found"] = essential_tools
            result["details"]["all_tools"] = list(tool_names)

        except subprocess.TimeoutExpired:
            result["message"] = "MCP server communication timeout"
        except Exception as e:
            result["message"] = str(e)

        return result

    def test_settings_json(self):
        """Test 5: Verify settings.json configuration"""
        result = {"status": "FAIL", "message": "", "details": {}}

        try:
            if not self.settings_json_path.exists():
                result["message"] = f"settings.json not found: {self.settings_json_path}"
                return result

            # Read and parse JSON
            with open(self.settings_json_path, 'r') as f:
                settings = json.load(f)

            result["details"]["json_valid"] = True

            # Check for mcpServers
            if "mcpServers" not in settings:
                result["message"] = "mcpServers key not found in settings.json"
                return result

            mcp_servers = settings["mcpServers"]

            # Check for playwright server
            if "playwright" not in mcp_servers:
                result["message"] = "playwright server not configured in mcpServers"
                result["details"]["configured_servers"] = list(mcp_servers.keys())
                return result

            playwright_config = mcp_servers["playwright"]

            # Verify playwright configuration
            if playwright_config.get("command") != "npx":
                result["message"] = f"Expected command 'npx', got '{playwright_config.get('command')}'"
                return result

            args = playwright_config.get("args", [])
            if not isinstance(args, list):
                result["message"] = "args should be a list"
                return result

            if "--headless" not in args:
                result["message"] = "--headless not found in args"
                result["details"]["args"] = args
                return result

            if "@playwright/mcp" not in args:
                result["message"] = "@playwright/mcp not found in args"
                result["details"]["args"] = args
                return result

            # Check that openclaw is preserved
            if "openclaw" not in mcp_servers:
                result["message"] = "openclaw server configuration was lost"
                return result

            result["status"] = "PASS"
            result["message"] = "settings.json configuration is valid"
            result["details"]["mcp_servers"] = list(mcp_servers.keys())
            result["details"]["playwright_command"] = playwright_config.get("command")
            result["details"]["playwright_args"] = args
            result["details"]["openclaw_preserved"] = True

        except json.JSONDecodeError as e:
            result["message"] = f"Invalid JSON in settings.json: {e}"
        except Exception as e:
            result["message"] = str(e)

        return result

    def run_all_tests(self):
        """Run all validation tests"""
        print("=" * 80)
        print("Playwright MCP Installation Validation Tests")
        print("=" * 80)
        print()

        tests = [
            ("Package Installation", self.test_package_installation),
            ("Chromium Binaries", self.test_chromium_binaries),
            ("MCP Handshake", self.test_mcp_handshake),
            ("Tools List", self.test_tools_list),
            ("Settings Configuration", self.test_settings_json),
        ]

        all_passed = True

        for test_name, test_func in tests:
            print(f"Test: {test_name}")
            print("-" * 80)

            result = test_func()
            self.results[test_name] = result

            status = result["status"]
            print(f"Status: {status}")
            print(f"Message: {result['message']}")

            if result["details"]:
                print("Details:")
                for key, value in result["details"].items():
                    if isinstance(value, (list, dict)) and len(str(value)) > 100:
                        print(f"  {key}: [Large output - {type(value).__name__} with {len(value)} items]")
                    else:
                        print(f"  {key}: {value}")

            print()

            if status != "PASS":
                all_passed = False

        # Summary
        print("=" * 80)
        print("Summary")
        print("=" * 80)
        passed_count = sum(1 for r in self.results.values() if r["status"] == "PASS")
        total_count = len(self.results)
        print(f"Passed: {passed_count}/{total_count}")

        for test_name, result in self.results.items():
            status_symbol = "[PASS]" if result["status"] == "PASS" else "[FAIL]"
            print(f"{status_symbol} {test_name}")

        print()

        if all_passed:
            print("All tests PASSED! Playwright MCP is properly installed and configured.")
            return 0
        else:
            print("Some tests FAILED. Please review the details above.")
            return 1


if __name__ == "__main__":
    validator = PlaywrightMCPValidator()
    exit_code = validator.run_all_tests()
    sys.exit(exit_code)
