"""GUI tests for Voice and Diagnostics tabs — endpoints, component serving, structure."""

import pytest
from fastapi.testclient import TestClient
from unittest.mock import patch

# Patch settings before importing app
from proxym.main import app


def _get_master_key() -> str:
    """Extract the actual master_key from the app's AuthMiddleware."""
    for mw in app.user_middleware:
        if hasattr(mw, 'kwargs') and 'master_key' in mw.kwargs:
            return mw.kwargs['master_key']
    # Fallback to settings
    from proxym.config import get_settings
    return get_settings().master_key


class TestVoiceComponent:
    """Test Voice tab component serving and structure."""

    @pytest.fixture
    def client(self):
        return TestClient(app)

    def test_voice_jsx_endpoint_serves(self, client):
        """Voice JSX endpoint should return valid JavaScript."""
        response = client.get("/dashboard-voice.jsx")
        assert response.status_code == 200
        assert "javascript" in response.headers["content-type"]

    def test_voice_jsx_has_component(self, client):
        """Voice JSX should define VoiceChat component."""
        response = client.get("/dashboard-voice.jsx")
        assert "function VoiceChat" in response.text

    def test_voice_jsx_has_speech_api_detection(self, client):
        """Voice JSX should detect SpeechRecognition and SpeechSynthesis support."""
        content = client.get("/dashboard-voice.jsx").text
        assert "SpeechRecognition" in content
        assert "speechSynthesis" in content

    def test_voice_jsx_has_stt_controls(self, client):
        """Voice JSX should have start/stop listening logic."""
        content = client.get("/dashboard-voice.jsx").text
        assert "startListening" in content
        assert "stopListening" in content

    def test_voice_jsx_has_tts_controls(self, client):
        """Voice JSX should have TTS speak/stop logic."""
        content = client.get("/dashboard-voice.jsx").text
        assert "speak" in content
        assert "stopSpeaking" in content

    def test_voice_jsx_has_llm_integration(self, client):
        """Voice JSX should call /v1/chat/completions for LLM responses."""
        content = client.get("/dashboard-voice.jsx").text
        assert "/v1/chat/completions" in content
        assert "sendMessage" in content

    def test_voice_jsx_has_model_selector(self, client):
        """Voice JSX should offer model selection (cheap, balanced, local, etc)."""
        content = client.get("/dashboard-voice.jsx").text
        assert "cheap" in content
        assert "balanced" in content
        assert "local" in content

    def test_voice_jsx_has_language_selector(self, client):
        """Voice JSX should support language switching (pl-PL, en-US)."""
        content = client.get("/dashboard-voice.jsx").text
        assert "pl-PL" in content
        assert "en-US" in content

    def test_voice_jsx_has_keyboard_shortcut(self, client):
        """Voice JSX should support Space key to toggle mic."""
        content = client.get("/dashboard-voice.jsx").text
        assert "Space" in content

    def test_voice_jsx_no_external_dependencies(self, client):
        """Voice JSX should use only Web Speech API (no external STT/TTS imports)."""
        content = client.get("/dashboard-voice.jsx").text
        # Should NOT import heavy external libs
        assert "import whisper" not in content.lower()
        assert "import openai" not in content.lower()


class TestDiagnosticsComponent:
    """Test Diagnostics tab component serving and structure."""

    @pytest.fixture
    def client(self):
        return TestClient(app)

    def test_diagnostics_jsx_endpoint_serves(self, client):
        """Diagnostics JSX endpoint should return valid JavaScript."""
        response = client.get("/dashboard-diagnostics.jsx")
        assert response.status_code == 200
        assert "javascript" in response.headers["content-type"]

    def test_diagnostics_jsx_has_panel(self, client):
        """Diagnostics JSX should define DiagnosticsPanel component."""
        content = client.get("/dashboard-diagnostics.jsx").text
        assert "function DiagnosticsPanel" in content

    def test_diagnostics_jsx_has_sub_components(self, client):
        """Diagnostics JSX should include all sub-components (concatenated)."""
        content = client.get("/dashboard-diagnostics.jsx").text
        # helpers
        assert "_diagBadge" in content or "diagBadge" in content.lower()
        # DiagInfrastructure sub-components
        assert "DiagVSCode" in content
        assert "DiagProxy" in content
        # DiagConfig sub-components
        assert "DiagRooConfig" in content
        assert "DiagProviders" in content
        # DiagExtensions sub-components
        assert "DiagExtensions" in content
        assert "DiagCopilot" in content
        # DiagAgent sub-components
        assert "DiagAgentSetup" in content
        assert "DiagAgentTest" in content

    def test_diagnostics_jsx_calls_backend(self, client):
        """Diagnostics JSX should call /tests/status endpoint."""
        content = client.get("/dashboard-diagnostics.jsx").text
        assert "/tests/status" in content

    def test_diagnostics_jsx_no_duplicate_functions(self, client):
        """Diagnostics JSX should not have duplicate function definitions."""
        content = client.get("/dashboard-diagnostics.jsx").text
        # Each function should be defined exactly once
        for func in ["DiagnosticsPanel", "DiagVSCode", "DiagProxy"]:
            count = content.count(f"function {func}")
            assert count == 1, f"function {func} defined {count} times (expected 1)"


class TestDiagnosticsBackend:
    """Test diagnostics backend /tests/* endpoints."""

    @pytest.fixture
    def client(self):
        c = TestClient(app)
        c.headers["Authorization"] = f"Bearer {_get_master_key()}"
        return c

    def test_tests_status_endpoint(self, client):
        """GET /tests/status should return diagnostic results."""
        response = client.get("/tests/status")
        assert response.status_code == 200
        data = response.json()
        assert "overall_status" in data
        assert "passed" in data
        assert "failed" in data
        assert "total" in data
        assert "tests" in data
        assert isinstance(data["tests"], list)

        # Normalized per-test fields required by the dashboard UI.
        for t in data["tests"]:
            assert "service" in t
            assert "passed" in t
            assert isinstance(t["passed"], bool)
            assert "message" in t
            assert isinstance(t["message"], str)
            assert "issues" in t
            assert isinstance(t["issues"], list)

    def test_tests_status_has_all_services(self, client):
        """GET /tests/status should check all 8 services."""
        response = client.get("/tests/status")
        data = response.json()
        services = {t.get("service") for t in data["tests"]}
        expected = {"vscode", "llm_proxy", "roo_cline", "extensions",
                    "providers", "copilot_conflict", "agent_setup", "agent_test"}
        assert expected == services, f"Missing services: {expected - services}"

    def test_tests_proxy_endpoint(self, client):
        """GET /tests/proxy should return proxy health info."""
        response = client.get("/tests/proxy")
        assert response.status_code == 200
        data = response.json()
        assert data["service"] == "llm_proxy"
        assert "reachable" in data

    def test_tests_providers_endpoint(self, client):
        """GET /tests/providers should list configured providers."""
        response = client.get("/tests/providers")
        assert response.status_code == 200
        data = response.json()
        assert data["service"] == "providers"
        assert "providers" in data
        assert "aliases" in data
        assert data["total_providers"] >= 1


class TestConsoleEndpoints:
    """Test console/routing API endpoints."""

    @pytest.fixture
    def client(self):
        c = TestClient(app)
        c.headers["Authorization"] = f"Bearer {_get_master_key()}"
        return c

    def test_routing_test_endpoint(self, client):
        """GET /api/v1/routing/test should return routing status per alias."""
        response = client.get("/api/v1/routing/test")
        assert response.status_code == 200
        data = response.json()
        assert "routing" in data
        assert "summary" in data
        assert "total" in data["summary"]
        assert "ok" in data["summary"]

    def test_routing_ollama_endpoint(self, client):
        """GET /api/v1/routing/ollama should return Ollama status."""
        response = client.get("/api/v1/routing/ollama")
        assert response.status_code == 200
        data = response.json()
        assert "ollama_url" in data
        assert "reachable" in data
        assert "models" in data

    def test_vm_console_not_found(self, client):
        """GET /api/v1/vms/nonexistent/console should return 404."""
        response = client.get("/api/v1/vms/nonexistent/console")
        assert response.status_code == 404


class TestDashboardHTMLIntegrity:
    """Test that dashboard.html loads all required components."""

    @pytest.fixture
    def client(self):
        return TestClient(app)

    def test_html_loads_voice_jsx(self, client):
        """Dashboard HTML should include voice.jsx script tag."""
        content = client.get("/dashboard-ui").text
        assert "/dashboard-voice.jsx" in content

    def test_html_loads_diagnostics_jsx(self, client):
        """Dashboard HTML should include diagnostics.jsx script tag."""
        content = client.get("/dashboard-ui").text
        assert "/dashboard-diagnostics.jsx" in content

    def test_html_no_duplicate_diagnostics_scripts(self, client):
        """Dashboard HTML should NOT load individual diag sub-components separately."""
        content = client.get("/dashboard-ui").text
        # These should NOT appear as separate script tags — they're concatenated
        assert "/dashboard-diag-helpers.jsx" not in content
        assert "/dashboard-diag-infrastructure.jsx" not in content
        assert "/dashboard-diag-config.jsx" not in content
        assert "/dashboard-diag-extensions.jsx" not in content
        assert "/dashboard-diag-agent.jsx" not in content

    def test_html_script_load_order(self, client):
        """Scripts should load in correct dependency order."""
        content = client.get("/dashboard-ui").text
        # api.js before hook.js before components before panel.jsx
        api_pos = content.index("/dashboard-api.js")
        hook_pos = content.index("/dashboard-hook.js")
        voice_pos = content.index("/dashboard-voice.jsx")
        diag_pos = content.index("/dashboard-diagnostics.jsx")
        panel_pos = content.index("/proxym-dashboard.jsx")
        assert api_pos < hook_pos < voice_pos < diag_pos < panel_pos

    def test_panel_jsx_references_all_tabs(self, client):
        """panel.jsx should reference all tab components."""
        content = client.get("/proxym-dashboard.jsx").text
        assert "VoiceChat" in content
        assert "DiagnosticsPanel" in content
        assert "OverviewTab" in content
        assert "AccountsPanel" in content
        assert "VMsPanel" in content

    def test_layout_jsx_has_all_tabs(self, client):
        """layout.jsx should define all tab IDs."""
        content = client.get("/dashboard-layout.jsx").text
        for tab_id in ["overview", "accounts", "models", "vms", "voice", "diagnostics"]:
            assert tab_id in content, f"Tab '{tab_id}' missing from layout.jsx"

    def test_hook_js_valid_tabs_complete(self, client):
        """useDashboardData hook should include all tabs in validTabs."""
        content = client.get("/dashboard-hook.js").text
        for tab_id in ["overview", "accounts", "models", "vms", "voice", "diagnostics"]:
            assert tab_id in content, f"Tab '{tab_id}' missing from validTabs in hook"


class TestAllComponentEndpoints:
    """Verify every JSX/JS component endpoint returns 200."""

    @pytest.fixture
    def client(self):
        return TestClient(app)

    @pytest.mark.parametrize("path", [
        "/dashboard-api.js",
        "/dashboard-formatters.js",
        "/dashboard-hook.js",
        "/dashboard-ui-components.jsx",
        "/dashboard-models.jsx",
        "/dashboard-accounts.jsx",
        "/dashboard-layout.jsx",
        "/dashboard-overview.jsx",
        "/dashboard-vms.jsx",
        "/dashboard-logs.jsx",
        "/dashboard-voice.jsx",
        "/dashboard-diagnostics.jsx",
        "/proxym-dashboard.jsx",
    ])
    def test_component_endpoint_returns_200(self, client, path):
        """Each component endpoint should serve content successfully."""
        response = client.get(path)
        assert response.status_code == 200, f"{path} returned {response.status_code}"
        assert len(response.text) > 10, f"{path} returned too little content"
        assert "javascript" in response.headers["content-type"]
