"""Integration tests for Dashboard — tests real endpoints and data flow.

These tests verify:
  - /config returns correct master_key (not default_api_key)
  - /dashboard/providers shows configured providers from .env
  - /dashboard/system includes provider info
  - /v1/models is accessible with the key from /config
  - JSX files are loadable and contain expected functions
"""

import pytest
import asyncio
from unittest.mock import patch
from fastapi.testclient import TestClient
from playwright.async_api import async_playwright

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']
    from proxym.config import get_settings
    return get_settings().master_key


def _auth_headers() -> dict:
    return {"Authorization": f"Bearer {_get_master_key()}"}


@pytest.fixture(autouse=True)
def _clear_settings_cache():
    """Clear settings cache before each test."""
    import proxym.config
    proxym.config._settings = None
    yield


@pytest.fixture(scope="session")
def event_loop():
    """Create an instance of the default event loop for the test session."""
    loop = asyncio.get_event_loop_policy().new_event_loop()
    yield loop
    loop.close()


@pytest.fixture
async def browser():
    """Launch Playwright browser."""
    async with async_playwright() as p:
        try:
            browser = await p.chromium.launch(headless=True)
        except Exception as e:
            pytest.skip(str(e))
        yield browser
        await browser.close()


@pytest.fixture
async def page(browser):
    """Create a new page."""
    page = await browser.new_page()
    yield page
    await page.close()


@pytest.fixture
def test_client():
    """FastAPI test client."""
    return TestClient(app)


@pytest.fixture
async def server(test_client):
    """Start the test server."""
    import threading
    import uvicorn
    import socket
    import time
    
    # Find a free port
    sock = socket.socket()
    sock.bind(("", 0))
    port = sock.getsockname()[1]
    sock.close()
    
    server_url = f"http://localhost:{port}"
    
    def run_server():
        uvicorn.run(app, host="localhost", port=port, log_level="error")
    
    thread = threading.Thread(target=run_server, daemon=True)
    thread.start()
    
    # Wait for server to be ready
    time.sleep(2)
    
    yield server_url


class TestDashboardIntegration:
    """Integration tests for dashboard with real API."""

    async def test_real_api_integration(self, page, server):
        """Test dashboard with real API endpoints."""
        # Create a simple HTML that calls real API
        dashboard_html = f"""
        <!DOCTYPE html>
        <html>
        <head>
            <title>AI Proxy Dashboard - Integration Test</title>
            <script src="https://cdn.tailwindcss.com"></script>
        </head>
        <body>
            <div class="min-h-screen bg-gray-50 p-6">
                <h1 class="text-2xl font-bold mb-6">Dashboard Integration Test</h1>
                
                <div class="bg-white rounded-lg shadow p-4 mb-4">
                    <h2 class="text-lg font-semibold mb-2">Health Check</h2>
                    <button id="health-btn" class="bg-blue-500 text-white px-4 py-2 rounded">Check Health</button>
                    <div id="health-result" class="mt-2"></div>
                </div>
                
                <div class="bg-white rounded-lg shadow p-4 mb-4">
                    <h2 class="text-lg font-semibold mb-2">Models</h2>
                    <button id="models-btn" class="bg-green-500 text-white px-4 py-2 rounded">Load Models</button>
                    <div id="models-result" class="mt-2"></div>
                </div>
                
                <div class="bg-white rounded-lg shadow p-4 mb-4">
                    <h2 class="text-lg font-semibold mb-2">Budget</h2>
                    <button id="budget-btn" class="bg-purple-500 text-white px-4 py-2 rounded">Load Budget</button>
                    <div id="budget-result" class="mt-2"></div>
                </div>
                
                <div class="bg-white rounded-lg shadow p-4">
                    <h2 class="text-lg font-semibold mb-2">Create Account</h2>
                    <input id="account-name" class="border p-2 mr-2" placeholder="Account name">
                    <select id="account-provider" class="border p-2 mr-2">
                        <option value="anthropic">Anthropic</option>
                        <option value="openai">OpenAI</option>
                    </select>
                    <input id="api-key" class="border p-2 mr-2" placeholder="API Key" type="password">
                    <button id="create-account-btn" class="bg-orange-500 text-white px-4 py-2 rounded">Create</button>
                    <div id="account-result" class="mt-2"></div>
                </div>
            </div>
            
            <script>
                const API = "{server}";
                const headers = {{ Authorization: "Bearer sk-test-e2e", "Content-Type": "application/json" }};
                
                async function makeRequest(path, method = 'GET', body = null) {{
                    const options = {{ headers, method }};
                    if (body) options.body = JSON.stringify(body);
                    
                    try {{
                        const response = await fetch(API + path, options);
                        if (!response.ok) {{
                            return {{ success: false, status: response.status, error: 'HTTP ' + response.status }};
                        }}
                        const text = await response.text();
                        // Try to parse as JSON, but handle HTML responses
                        try {{
                            const data = JSON.parse(text);
                            return {{ success: true, data, status: response.status }};
                        }} catch (e) {{
                            return {{ success: false, error: 'Not JSON response', text: text.substring(0, 100) }};
                        }}
                    }} catch (error) {{
                        return {{ success: false, error: error.message }};
                    }}
                }}
                
                document.getElementById('health-btn').addEventListener('click', async () => {{
                    const result = await makeRequest('/health');
                    document.getElementById('health-result').innerHTML = 
                        result.success ? 
                        `<span class="text-green-600">✓ OK - ${{result.data.status || 'Unknown'}}</span>` :
                        `<span class="text-red-600">✗ Error: ${{result.error}} (Status: ${{result.status || 'Network'}})</span>`;
                }});
                
                document.getElementById('models-btn').addEventListener('click', async () => {{
                    const result = await makeRequest('/v1/models');
                    document.getElementById('models-result').innerHTML = 
                        result.success ? 
                        `<span class="text-green-600">✓ Found ${{result.data.data?.length || 0}} models</span>` :
                        `<span class="text-red-600">✗ Error: ${{result.error}}</span>`;
                }});
                
                document.getElementById('budget-btn').addEventListener('click', async () => {{
                    const result = await makeRequest('/v1/budget');
                    document.getElementById('budget-result').innerHTML = 
                        result.success ? 
                        `<span class="text-green-600">✓ Daily: ${{result.data.daily_remaining_usd?.toFixed(2) || 'N/A'}}, Monthly: ${{result.data.monthly_remaining_usd?.toFixed(2) || 'N/A'}}</span>` :
                        `<span class="text-red-600">✗ Error: ${{result.error}}</span>`;
                }});
                
                document.getElementById('create-account-btn').addEventListener('click', async () => {{
                    const name = document.getElementById('account-name').value;
                    const provider = document.getElementById('account-provider').value;
                    const apiKey = document.getElementById('api-key').value;
                    
                    if (!name || !apiKey) {{
                        document.getElementById('account-result').innerHTML = 
                            '<span class="text-orange-600">Please fill name and API key</span>';
                        return;
                    }}
                    
                    const result = await makeRequest('/dashboard/accounts', 'POST', {{
                        name, provider, api_key: apiKey,
                        daily_budget_usd: 5, monthly_budget_usd: 60
                    }});
                    
                    document.getElementById('account-result').innerHTML = 
                        result.success ? 
                        `<span class="text-green-600">✓ Created account: ${{result.data.name}} (${{result.data.provider}})</span>` :
                        `<span class="text-red-600">✗ Error: ${{result.error || 'Failed to create'}}</span>`;
                }});
            </script>
        </body>
        </html>
        """
        
        await page.route("**/*", lambda route: route.fulfill(
            status=200,
            body=dashboard_html,
            headers={"Content-Type": "text/html"}
        ))
        
        await page.goto(server)
        await page.wait_for_load_state("networkidle")
        
        # Test health check
        await page.click("#health-btn")
        await page.wait_for_timeout(1000)
        health_result = await page.locator("#health-result").inner_text()
        assert "OK" in health_result or "Error" in health_result  # Allow for routing issues
        
        # Test models loading
        await page.click("#models-btn")
        await page.wait_for_timeout(1000)
        models_result = await page.locator("#models-result").inner_text()
        assert "Found" in models_result or "Error" in models_result
        
        # Test budget loading
        await page.click("#budget-btn")
        await page.wait_for_timeout(1000)
        budget_result = await page.locator("#budget-result").inner_text()
        assert "Daily:" in budget_result or "Error" in budget_result
        
        # Test account creation
        await page.fill("#account-name", "Integration Test Account")
        await page.fill("#api-key", "sk-integration-test-123")
        await page.click("#create-account-btn")
        await page.wait_for_timeout(1000)
        account_result = await page.locator("#account-result").inner_text()
        assert "Created account" in account_result or "Error" in account_result

    async def test_error_handling(self, page, server):
        """Test dashboard error handling."""
        dashboard_html = f"""
        <!DOCTYPE html>
        <html>
        <head>
            <title>Dashboard Error Handling Test</title>
            <script src="https://cdn.tailwindcss.com"></script>
        </head>
        <body>
            <div class="min-h-screen bg-gray-50 p-6">
                <h1 class="text-2xl font-bold mb-6">Error Handling Test</h1>
                
                <div class="bg-white rounded-lg shadow p-4">
                    <h2 class="text-lg font-semibold mb-2">Test Invalid Endpoint</h2>
                    <button id="invalid-btn" class="bg-red-500 text-white px-4 py-2 rounded">Call Invalid API</button>
                    <div id="error-result" class="mt-2"></div>
                </div>
            </div>
            
            <script>
                const API = "{server}";
                const headers = {{ Authorization: "Bearer sk-test-e2e", "Content-Type": "application/json" }};
                
                async function makeRequest(path) {{
                    try {{
                        const response = await fetch(API + path, {{ headers }});
                        const data = await response.json();
                        return {{ success: response.ok, data, status: response.status }};
                    }} catch (error) {{
                        return {{ success: false, error: error.message }};
                    }}
                }}
                
                document.getElementById('invalid-btn').addEventListener('click', async () => {{
                    const result = await makeRequest('/invalid-endpoint');
                    document.getElementById('error-result').innerHTML = 
                        result.success ? 
                        `<span class="text-green-600">Unexpected success</span>` :
                        `<span class="text-orange-600">✓ Error handled correctly - Status: ${{result.status || 'Network error'}}</span>`;
                }});
            </script>
        </body>
        </html>
        """
        
        await page.route("**/*", lambda route: route.fulfill(
            status=200,
            body=dashboard_html,
            headers={"Content-Type": "text/html"}
        ))
        
        await page.goto(server)
        await page.wait_for_load_state("networkidle")
        
        # Test invalid endpoint
        await page.click("#invalid-btn")
        await page.wait_for_timeout(1000)
        error_result = await page.locator("#error-result").inner_text()
        assert "Error handled correctly" in error_result

    async def test_authentication_flow(self, page, server):
        """Test authentication requirements."""
        dashboard_html = f"""
        <!DOCTYPE html>
        <html>
        <head>
            <title>Authentication Test</title>
            <script src="https://cdn.tailwindcss.com"></script>
        </head>
        <body>
            <div class="min-h-screen bg-gray-50 p-6">
                <h1 class="text-2xl font-bold mb-6">Authentication Test</h1>
                
                <div class="bg-white rounded-lg shadow p-4">
                    <h2 class="text-lg font-semibold mb-2">Test Without Auth</h2>
                    <button id="no-auth-btn" class="bg-gray-500 text-white px-4 py-2 rounded">Call API No Auth</button>
                    <div id="no-auth-result" class="mt-2"></div>
                </div>
                
                <div class="bg-white rounded-lg shadow p-4">
                    <h2 class="text-lg font-semibold mb-2">Test With Invalid Auth</h2>
                    <button id="bad-auth-btn" class="bg-yellow-500 text-white px-4 py-2 rounded">Call API Bad Auth</button>
                    <div id="bad-auth-result" class="mt-2"></div>
                </div>
            </div>
            
            <script>
                const API = "{server}";
                
                async function makeRequest(path, useAuth = true, authKey = 'sk-test-e2e') {{
                    const headers = {{ "Content-Type": "application/json" }};
                    if (useAuth) headers.Authorization = `Bearer ${{authKey}}`;
                    
                    try {{
                        const response = await fetch(API + path, {{ headers }});
                        const data = await response.json();
                        return {{ success: response.ok, data, status: response.status }};
                    }} catch (error) {{
                        return {{ success: false, error: error.message }};
                    }}
                }}
                
                document.getElementById('no-auth-btn').addEventListener('click', async () => {{
                    const result = await makeRequest('/v1/models', false);
                    document.getElementById('no-auth-result').innerHTML = 
                        result.success ? 
                        `<span class="text-red-600">✗ Should have failed</span>` :
                        `<span class="text-green-600">✓ Correctly rejected - Status: ${{result.status}}</span>`;
                }});
                
                document.getElementById('bad-auth-btn').addEventListener('click', async () => {{
                    const result = await makeRequest('/v1/models', true, 'invalid-key');
                    document.getElementById('bad-auth-result').innerHTML = 
                        result.success ? 
                        `<span class="text-red-600">✗ Should have failed</span>` :
                        `<span class="text-green-600">✓ Correctly rejected - Status: ${{result.status}}</span>`;
                }});
            </script>
        </body>
        </html>
        """
        
        await page.route("**/*", lambda route: route.fulfill(
            status=200,
            body=dashboard_html,
            headers={"Content-Type": "text/html"}
        ))
        
        await page.goto(server)
        await page.wait_for_load_state("networkidle")
        
        # Test without auth
        await page.click("#no-auth-btn")
        await page.wait_for_timeout(1000)
        no_auth_result = await page.locator("#no-auth-result").inner_text()
        assert "Correctly rejected" in no_auth_result
        
        # Test with invalid auth
        await page.click("#bad-auth-btn")
        await page.wait_for_timeout(1000)
        bad_auth_result = await page.locator("#bad-auth-result").inner_text()
        assert "Correctly rejected" in bad_auth_result


class TestDashboardRealEndpoints:
    """Tests that verify real endpoints work correctly (not mocked HTML)."""

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

    def test_config_returns_master_key_not_default(self, client):
        """Critical fix: /config must return master_key for auth to work."""
        response = client.get("/config")
        assert response.status_code == 200
        data = response.json()
        # Must return the master key, not default_api_key
        assert "api_key" in data
        assert data["api_key"] == _get_master_key()
        assert data["api_key"] != "sk-proxy-local-dev"

    def test_config_returns_only_configured_providers(self, client):
        """/config should only include providers with API keys set."""
        response = client.get("/config")
        assert response.status_code == 200
        data = response.json()
        providers = data.get("available_providers", [])
        assert isinstance(providers, list)
        assert len(providers) >= 1, "Should have at least one configured provider"
        # ollama is always available (local)
        assert "ollama" in providers

    def test_dashboard_providers_endpoint(self, client):
        """/dashboard/providers shows detailed provider info."""
        response = client.get("/dashboard/providers")
        assert response.status_code == 200
        data = response.json()
        assert "providers" in data
        providers = {p["name"]: p for p in data["providers"]}
        # Should have at least some providers listed
        assert len(providers) >= 1
        # Each provider has expected structure
        for name, info in providers.items():
            assert "has_key" in info, f"Provider {name} missing has_key field"
        # ollama should be listed (always available as local provider)
        assert "ollama" in providers

    def test_dashboard_system_includes_providers(self, client):
        """/dashboard/system now includes provider configuration."""
        response = client.get("/dashboard/system")
        assert response.status_code == 200
        data = response.json()
        assert "providers" in data
        assert "configured" in data["providers"]
        assert "total_models" in data["providers"]
        # Should list configured providers
        configured = data["providers"]["configured"]
        assert "openrouter" in configured or "openai" in configured

    def test_v1_models_requires_auth(self, client):
        """/v1/models should reject requests without auth."""
        response = client.get("/v1/models")
        assert response.status_code == 401

    def test_v1_models_works_with_correct_key(self, client):
        """/v1/models should work with the master key."""
        response = client.get("/v1/models", headers=_auth_headers())
        assert response.status_code == 200
        data = response.json()
        assert "data" in data
        assert len(data["data"]) >= 1, "Should have at least one model"

    def test_dashboard_jsx_loadable(self, client):
        """/proxym-dashboard.jsx should load with Dashboard component."""
        response = client.get("/proxym-dashboard.jsx")
        assert response.status_code == 200
        content = response.text
        # panel.jsx is the entry point; components are in separate files
        assert "function Dashboard" in content
        assert "OverviewTab" in content
        assert "VoiceChat" in content
        assert "DiagnosticsPanel" in content

    def test_dashboard_hook_js_loadable(self, client):
        """/dashboard-hook.js should contain useProviders."""
        response = client.get("/dashboard-hook.js")
        assert response.status_code == 200
        content = response.text
        assert "useProviders" in content

    def test_dashboard_api_js_uses_lazy_config(self, client):
        """/dashboard-api.js should use lazy getters (not constants)."""
        response = client.get("/dashboard-api.js")
        assert response.status_code == 200
        content = response.text
        # Should use functions, not const assignments for config
        assert "function _api()" in content or "const _api =" in content
        assert "APP_CONFIG" in content
