"""GUI tests for the Proxym Dashboard."""

import pytest
import httpx
from unittest.mock import patch, AsyncMock
from fastapi.testclient import TestClient

# Patch settings before importing app
with patch.dict("os.environ", {
    "AI_PROXY_MASTER_KEY": "sk-test-e2e",
    "ANTHROPIC_API_KEY": "sk-ant-test",
    "OPENAI_API_KEY": "sk-test",
    "DEEPSEEK_API_KEY": "sk-ds-test",
    "GOOGLE_AI_KEY": "AI-test",
    "OPENROUTER_API_KEY": "sk-or-test",
    "GROQ_API_KEY": "gsk-test",
    "REDIS_URL": "redis://localhost:6379/0",
}):
    from proxym.main import app


class TestDashboardGUI:
    """Test suite for dashboard GUI functionality."""

    def setup_method(self, tmp_path_factory=None):
        """Setup test client with isolated storage."""
        import tempfile, pathlib
        import proxym.dashboard._shared as dash_shared
        import proxym.dashboard._environments_api as env_api
        from proxym.accounts import AccountManager
        from proxym.virt import VMOrchestrator
        td = pathlib.Path(tempfile.mkdtemp())
        mgr = AccountManager(config_path=str(td / "accounts.json"))
        orch = VMOrchestrator(
            config_dir=str(td / "vms"),
            images_dir=str(td / "images"),
            overlays_dir=str(td / "overlays"),
        )
        dash_shared._acct_mgr = mgr
        dash_shared._vm_orch = orch
        self._original_environment_sqlite_path = env_api.DEFAULT_SQLITE_PATH
        env_api._registry = None
        env_api.DEFAULT_SQLITE_PATH = td / "environments.sqlite"
        self.client = TestClient(app)

    def teardown_method(self):
        import proxym.dashboard._environments_api as env_api
        env_api._registry = None
        if hasattr(self, "_original_environment_sqlite_path"):
            env_api.DEFAULT_SQLITE_PATH = self._original_environment_sqlite_path

    def test_dashboard_ui_serves_html(self):
        """Test that dashboard UI serves proper HTML."""
        response = self.client.get("/dashboard-ui")
        
        assert response.status_code == 200
        assert "text/html" in response.headers["content-type"]
        assert "<!DOCTYPE html>" in response.text
        assert "Proxym Dashboard" in response.text
        assert "react" in response.text.lower()

    def test_dashboard_jsx_serves_javascript(self):
        """Test that JSX file serves as JavaScript."""
        response = self.client.get("/proxym-dashboard.jsx")
        
        assert response.status_code == 200
        assert "javascript" in response.headers["content-type"]
        assert "function Dashboard" in response.text
        assert "useDashboardData" in response.text

    def test_dashboard_setup_jsx_serves_javascript(self):
        """Test that setup panel JSX file serves as JavaScript."""
        response = self.client.get("/dashboard-setup.jsx")

        assert response.status_code == 200
        assert "javascript" in response.headers["content-type"]
        assert "function SetupPanel" in response.text
        assert "SETUP_STEPS" in response.text

    def test_dashboard_integrations_jsx_serves_javascript(self):
        """Test that integrations panel JSX file serves as JavaScript."""
        response = self.client.get("/dashboard-integrations.jsx")

        assert response.status_code == 200
        assert "javascript" in response.headers["content-type"]
        assert "function IntegrationsPanel" in response.text
        assert "Webhook integrations" in response.text

    def test_dashboard_tasks_jsx_serves_javascript(self):
        """Test that tasks panel JSX file serves the quick task creator UI."""
        response = self.client.get("/dashboard-tasks.jsx")

        assert response.status_code == 200
        assert "javascript" in response.headers["content-type"]
        assert "function TasksPanel" in response.text
        assert "QuickTaskCreator" in response.text
        assert "Quick Create" in response.text
        assert "TaskBulkEditBar" in response.text
        assert "Select all visible" in response.text
        assert "Apply bulk edit" in response.text

    def test_dashboard_ticket_review_jsx_serves_javascript(self):
        """Test that the refactored tickets panel exposes the LLM review sidebar."""
        response = self.client.get("/dashboard-tickets-TicketsPanelRefactored.jsx")

        assert response.status_code == 200
        assert "javascript" in response.headers["content-type"]
        assert "function useTicketReviewDetail" in response.text
        assert "LLM review trail" in response.text

    def test_dashboard_users_jsx_serves_javascript(self):
        """Test that users panel JSX file serves as JavaScript."""
        response = self.client.get("/dashboard-users.jsx")

        assert response.status_code == 200
        assert "javascript" in response.headers["content-type"]
        assert "function UsersPanel" in response.text
        assert "useUsers" in response.text

    def test_dashboard_home_jsx_serves_javascript(self):
        """Test that home JSX file exposes human in QuickDo selectors."""
        response = self.client.get("/dashboard-home.jsx")

        assert response.status_code == 200
        assert "javascript" in response.headers["content-type"]
        assert "QuickDo" in response.text
        assert 'value="human"' in response.text

    def test_dashboard_ui_includes_integrations_script(self):
        """Test that the dashboard HTML loads the integrations panel script."""
        response = self.client.get("/dashboard-ui")

        assert response.status_code == 200
        assert "/dashboard-integrations.jsx" in response.text

    def test_dashboard_api_no_auth_required(self):
        """Test that dashboard API endpoints don't require authentication."""
        endpoints = [
            "/dashboard/",
            "/dashboard/environments",
            "/dashboard/setup",
            "/dashboard/system",
            "/dashboard/accounts",
            "/dashboard/costs",
            "/dashboard/vms"
        ]
        
        for endpoint in endpoints:
            response = self.client.get(endpoint)
            assert response.status_code == 200, f"Endpoint {endpoint} should be accessible"
            assert "application/json" in response.headers["content-type"]

    def test_dashboard_system_status_structure(self):
        """Test system status response structure."""
        response = self.client.get("/dashboard/system")
        data = response.json()
        
        required_keys = ["accounts", "vms", "libvirt"]
        for key in required_keys:
            assert key in data, f"Missing key: {key}"
        
        # Check accounts structure
        accounts = data["accounts"]
        account_keys = ["daily_total_usd", "monthly_total_usd", "total_requests", "accounts_count"]
        for key in account_keys:
            assert key in accounts, f"Missing account key: {key}"
        
        # Check VMs structure
        vms = data["vms"]
        vm_keys = ["total", "running"]
        for key in vm_keys:
            assert key in vms, f"Missing VM key: {key}"

    def test_dashboard_accounts_empty(self):
        """Test accounts endpoint when no accounts configured."""
        response = self.client.get("/dashboard/accounts")
        data = response.json()
        
        assert data == {"accounts": []}

    def test_dashboard_costs_initial_state(self):
        """Test costs endpoint initial state."""
        response = self.client.get("/dashboard/costs")
        data = response.json()
        
        assert data["daily_total_usd"] == 0
        assert data["monthly_total_usd"] == 0
        assert data["total_requests"] == 0
        assert data["accounts_count"] == 0

    def test_dashboard_environments_api_structure(self):
        """Test environments endpoint returns the nested dashboard payload."""
        response = self.client.get("/dashboard/environments")
        assert response.status_code == 200
        data = response.json()
        assert "environments" in data
        assert "tool_assignments" in data
        assert "summary" in data

    def test_dashboard_environment_assignment_records_instance(self):
        """Test assigning a tool to an environment also stores the selected instance."""
        created = self.client.post("/dashboard/environments", json={
            "name": "Local dev",
            "kind": "local_path",
            "path": "/tmp/project",
            "active": True,
        })
        assert created.status_code == 200
        environment = created.json()

        assign = self.client.post("/dashboard/environments/assign", json={
            "tool": "roo-code",
            "environment_id": environment["id"],
            "instance_name": "docker",
        })
        assert assign.status_code == 200
        assign_data = assign.json()
        assert assign_data["environment_id"] == environment["id"]
        assert assign_data["instance_name"] == "docker"

        tool_response = self.client.get("/dashboard/tools/roo-code")
        assert tool_response.status_code == 200
        tool_data = tool_response.json()
        assert tool_data["environment"]["id"] == environment["id"]
        assert tool_data["environment"]["tool_instance_name"] == "docker"
        assert tool_data["environment"]["tool_instance"]["name"] == "docker"

    def test_dashboard_setup_api_uses_cqrs_projection(self):
        """Test setup checklist state is served from the CQRS read model."""
        reset = self.client.post("/dashboard/setup/reset", json={})
        assert reset.status_code == 200
        assert reset.json()["completed_steps"] == []

        response = self.client.get("/dashboard/setup")
        assert response.status_code == 200
        data = response.json()
        assert data["aggregate_id"] == "setup-checklist"
        assert data["completed_steps"] == []
        assert data["completed_count"] == 0
        assert "revision" in data
        assert "event_count" in data

        toggle = self.client.post("/dashboard/setup/steps/requirements/toggle", json={"completed": True})
        assert toggle.status_code == 200
        toggled = toggle.json()
        assert toggled["completed_map"]["requirements"] is True
        assert "requirements" in toggled["completed_steps"]

        refreshed = self.client.get("/dashboard/setup")
        assert refreshed.status_code == 200
        assert "requirements" in refreshed.json()["completed_steps"]

        reset = self.client.post("/dashboard/setup/reset", json={})
        assert reset.status_code == 200
        assert reset.json()["completed_steps"] == []

    def test_dashboard_vms_structure(self):
        """Test VMs endpoint returns correct structure."""
        response = self.client.get("/dashboard/vms")
        data = response.json()
        
        assert "vms" in data
        assert isinstance(data["vms"], list)

    def test_dashboard_jsx_api_calls(self):
        """Test that dashboard JS files contain correct API call patterns."""
        # panel.jsx uses components; API calls are in api.js
        jsx_response = self.client.get("/proxym-dashboard.jsx")
        assert "useDashboardData" in jsx_response.text
        
        api_response = self.client.get("/dashboard-api.js")
        assert api_response.status_code == 200
        assert "fetch(" in api_response.text
        assert "Authorization" in api_response.text

    def test_dashboard_ui_loads_react_dependencies(self):
        """Test that dashboard UI loads React dependencies."""
        response = self.client.get("/dashboard-ui")
        content = response.text
        
        # Check for React CDN links
        assert "unpkg.com/react@18" in content
        assert "unpkg.com/react-dom@18" in content
        assert "unpkg.com/@babel/standalone" in content
        assert "cdn.tailwindcss.com" in content

    def test_dashboard_ui_has_root_element(self):
        """Test that dashboard UI has root element for React."""
        response = self.client.get("/dashboard-ui")
        content = response.text
        
        assert '<div id="root"></div>' in content

    def test_dashboard_ui_script_type_babel(self):
        """Test that JSX script has correct type for Babel transpilation."""
        response = self.client.get("/dashboard-ui")
        content = response.text
        
        assert 'type="text/babel"' in content
        assert 'src="/proxym-dashboard.jsx"' in content
        assert 'src="/dashboard-setup.jsx"' in content

    def test_dashboard_performance_response_time(self):
        """Test dashboard endpoints response time."""
        import time
        
        endpoints = ["/dashboard/", "/dashboard/system", "/dashboard/accounts"]
        
        for endpoint in endpoints:
            start_time = time.time()
            response = self.client.get(endpoint)
            end_time = time.time()
            
            assert response.status_code == 200
            assert (end_time - start_time) < 1.0, f"Endpoint {endpoint} too slow: {end_time - start_time:.2f}s"

    def test_dashboard_concurrent_requests(self):
        """Test dashboard handles concurrent requests."""
        import threading
        import time
        
        results = []
        
        def make_request(endpoint):
            response = self.client.get(endpoint)
            results.append(response.status_code)
        
        threads = []
        endpoints = ["/dashboard/", "/dashboard/system", "/dashboard/accounts"]
        
        # Make concurrent requests
        start_time = time.time()
        for endpoint in endpoints:
            for _ in range(3):  # 3 concurrent requests per endpoint
                thread = threading.Thread(target=make_request, args=(endpoint,))
                threads.append(thread)
                thread.start()
        
        # Wait for all threads to complete
        for thread in threads:
            thread.join()
        
        end_time = time.time()
        
        # All requests should succeed
        assert all(status == 200 for status in results)
        # Should complete within reasonable time
        assert (end_time - start_time) < 5.0

    def test_dashboard_error_handling(self):
        """Test dashboard error handling."""
        # Test non-existent endpoint
        response = self.client.get("/dashboard/nonexistent")
        assert response.status_code == 404
        
        # Test invalid method
        response = self.client.post("/dashboard/")
        # Should either 405 (Method Not Allowed) or 404
        assert response.status_code in [404, 405]

    def test_dashboard_content_type_headers(self):
        """Test proper content-type headers."""
        html_response = self.client.get("/dashboard-ui")
        assert "text/html" in html_response.headers["content-type"]
        
        jsx_response = self.client.get("/proxym-dashboard.jsx")
        assert "text/javascript" in jsx_response.headers["content-type"]
        
        api_response = self.client.get("/dashboard/system")
        assert "application/json" in api_response.headers["content-type"]

    def test_dashboard_integration_with_api(self):
        """Test dashboard integration with main API."""
        # Test that dashboard can access main API endpoints
        response = self.client.get("/v1/models")
        assert response.status_code == 401  # Should require auth
        
        # Test health endpoint
        response = self.client.get("/health")
        assert response.status_code == 200
        assert response.json()["status"] == "ok"

    def test_dashboard_jsx_react_components(self):
        """Test that JSX contains React components."""
        response = self.client.get("/proxym-dashboard.jsx")
        jsx_content = response.text
        
        # Check for React component patterns
        assert "function " in jsx_content or "const " in jsx_content
        assert "useState" in jsx_content
        assert "useEffect" in jsx_content
        assert "return (" in jsx_content

    def test_dashboard_ui_responsive_design(self):
        """Test that dashboard UI is responsive."""
        response = self.client.get("/dashboard-ui")
        content = response.text
        
        # Check for responsive meta tag
        assert 'name="viewport"' in content
        assert 'width=device-width' in content

    def test_dashboard_security_headers(self):
        """Test security headers on dashboard endpoints."""
        response = self.client.get("/dashboard-ui")
        
        # Should not expose sensitive information
        assert "password" not in response.text.lower()
        assert "secret" not in response.text.lower()
        assert "token" not in response.text.lower() or "Bearer" in response.text

    def test_dashboard_users_api_list_empty(self):
        """Test users API returns empty list initially."""
        response = self.client.get("/dashboard/users")
        assert response.status_code == 200
        data = response.json()
        assert "users" in data
        assert isinstance(data["users"], list)
        assert "default_user" in data

    def test_dashboard_users_api_sync_git(self):
        """Test users git sync endpoint."""
        response = self.client.get("/dashboard/users/sync-git")
        assert response.status_code == 200
        data = response.json()
        assert "discovered" in data
        assert "users" in data
        assert "total_users" in data

    def test_dashboard_tools_healthcheck_structure(self):
        """Test tools healthcheck returns proper structure."""
        response = self.client.get("/dashboard/tools/healthcheck")
        assert response.status_code == 200
        data = response.json()
        assert "tools" in data
        assert "summary" in data
        assert "providers_configured" in data
        assert "tool_providers" in data

    def test_dashboard_tools_autofix_endpoint_exists(self):
        """Test autofix endpoint exists and accepts POST."""
        # Test with invalid action - should return error but endpoint exists
        response = self.client.post("/dashboard/tools/autofix", json={
            "tool_name": "test-tool",
            "fix_action": "unknown_action",
            "command": ""
        })
        # Should either fail validation (422) or execute (200)
        assert response.status_code in [200, 422]

    def test_dashboard_tools_bulk_autofix_endpoint_exists(self):
        """Test bulk autofix endpoint exists."""
        response = self.client.post("/dashboard/tools/autofix/bulk")
        # Should return 200 with results even if nothing to fix
        assert response.status_code == 200
        data = response.json()
        assert "executed" in data
        assert "failed" in data
        assert "human_required" in data
        assert "results" in data
