<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Contract Tests - Ward</title>
    <style>
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: #111827;
            color: #e5e7eb;
            min-height: 100vh;
        }

        .container {
            max-width: 1400px;
            margin: 0 auto;
            padding: 20px;
        }

        header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
            padding-bottom: 20px;
            border-bottom: 1px solid #374151;
        }

        h1 {
            font-size: 1.5rem;
            font-weight: 600;
            color: #f9fafb;
        }

        .config-info {
            font-size: 0.875rem;
            color: #9ca3af;
        }

        .config-info strong {
            color: #60a5fa;
        }

        .toolbar {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
            flex-wrap: wrap;
            align-items: center;
        }

        button {
            padding: 8px 16px;
            border: none;
            border-radius: 6px;
            font-size: 0.875rem;
            cursor: pointer;
            transition: all 0.2s;
        }

        .btn-primary {
            background: #2563eb;
            color: white;
        }

        .btn-primary:hover {
            background: #1d4ed8;
        }

        .btn-primary:disabled {
            background: #4b5563;
            cursor: not-allowed;
        }

        .btn-secondary {
            background: #374151;
            color: #e5e7eb;
        }

        .btn-secondary:hover {
            background: #4b5563;
        }

        .main-content {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 20px;
        }

        @media (max-width: 900px) {
            .main-content {
                grid-template-columns: 1fr;
            }
        }

        .panel {
            background: #1f2937;
            border-radius: 8px;
            overflow: hidden;
        }

        .panel-header {
            padding: 12px 16px;
            background: #374151;
            font-weight: 600;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .panel-body {
            padding: 16px;
            max-height: 600px;
            overflow-y: auto;
        }

        /* Test Tree */
        .folder {
            margin-bottom: 8px;
        }

        .folder-header {
            display: flex;
            align-items: center;
            padding: 6px 8px;
            border-radius: 4px;
            cursor: pointer;
            user-select: none;
        }

        .folder-header:hover {
            background: #374151;
        }

        .folder-header input {
            margin-right: 12px;
        }

        .folder-name {
            font-weight: 500;
            color: #f9fafb;
        }

        .test-count {
            margin-left: auto;
            font-size: 0.75rem;
            color: #9ca3af;
            background: #374151;
            padding: 2px 8px;
            border-radius: 10px;
        }

        .folder-content {
            margin-left: 20px;
        }

        .module {
            margin: 4px 0;
        }

        .module-header {
            display: flex;
            align-items: center;
            padding: 4px 8px;
            border-radius: 4px;
            cursor: pointer;
        }

        .module-header:hover {
            background: #374151;
        }

        .module-header input {
            margin-right: 12px;
        }

        .module-name {
            color: #93c5fd;
            font-size: 1rem;
        }

        .class-block {
            margin-left: 20px;
        }

        .class-header {
            display: flex;
            align-items: center;
            padding: 4px 8px;
            font-size: 1rem;
            color: #a78bfa;
            cursor: pointer;
        }

        .class-header:hover {
            background: #374151;
            border-radius: 4px;
        }

        .class-header input {
            margin-right: 12px;
        }

        .test-list {
            margin-left: 20px;
        }

        .test-item {
            display: flex;
            align-items: center;
            padding: 6px 8px;
            font-size: 0.95rem;
            border-radius: 4px;
        }

        .test-item:hover {
            background: #374151;
        }

        .test-item input {
            margin-right: 12px;
        }

        .test-name {
            color: #d1d5db;
        }

        /* Results */
        .summary {
            display: flex;
            gap: 16px;
            margin-bottom: 16px;
            flex-wrap: wrap;
        }

        .stat {
            text-align: center;
        }

        .stat-value {
            font-size: 1.5rem;
            font-weight: 700;
        }

        .stat-label {
            font-size: 0.75rem;
            color: #9ca3af;
            text-transform: uppercase;
        }

        .stat-passed .stat-value { color: #34d399; }
        .stat-failed .stat-value { color: #f87171; }
        .stat-skipped .stat-value { color: #fbbf24; }
        .stat-running .stat-value { color: #60a5fa; }

        .result-item {
            padding: 8px 12px;
            margin-bottom: 4px;
            border-radius: 4px;
            background: #374151;
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .result-icon {
            width: 20px;
            height: 20px;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 0.75rem;
            flex-shrink: 0;
        }

        .result-passed .result-icon {
            background: #065f46;
            color: #34d399;
        }

        .result-failed .result-icon,
        .result-error .result-icon {
            background: #7f1d1d;
            color: #f87171;
        }

        .result-skipped .result-icon {
            background: #78350f;
            color: #fbbf24;
        }

        .result-running .result-icon {
            background: #1e3a8a;
            color: #60a5fa;
            animation: pulse 1s infinite;
        }

        @keyframes pulse {
            0%, 100% { opacity: 1; }
            50% { opacity: 0.5; }
        }

        .result-info {
            flex: 1;
            min-width: 0;
        }

        .result-name {
            font-size: 0.875rem;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        .result-test-id {
            font-size: 0.75rem;
            color: #6b7280;
        }

        .result-duration {
            font-size: 0.75rem;
            color: #9ca3af;
        }

        .result-error {
            margin-top: 8px;
            padding: 8px;
            background: #1f2937;
            border-radius: 4px;
            font-size: 0.75rem;
            font-family: monospace;
            white-space: pre-wrap;
            color: #f87171;
            max-height: 200px;
            overflow-y: auto;
        }

        .empty-state {
            text-align: center;
            padding: 40px;
            color: #6b7280;
        }

        .progress-bar {
            height: 4px;
            background: #374151;
            border-radius: 2px;
            margin-bottom: 16px;
            overflow: hidden;
        }

        .progress-fill {
            height: 100%;
            background: #2563eb;
            transition: width 0.3s;
        }

        .current-test {
            font-size: 0.75rem;
            color: #60a5fa;
            margin-bottom: 8px;
            font-style: italic;
        }

        /* Collapsible */
        .collapsed .folder-content,
        .collapsed .module-content,
        .collapsed .class-content {
            display: none;
        }

        .toggle-icon {
            margin-right: 4px;
            transition: transform 0.2s;
        }

        .collapsed .toggle-icon {
            transform: rotate(-90deg);
        }

        a {
            color: #60a5fa;
            text-decoration: none;
        }

        a:hover {
            text-decoration: underline;
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <div>
                <h1>Contract HTTP Tests</h1>
                <div style="display: flex; gap: 12px; margin-top: 8px; font-size: 0.875rem;">
                    <a href="/tools/tester/" style="color: #60a5fa; text-decoration: none; font-weight: 600;">Runner</a>
                    <a href="/tools/tester/filters" style="color: #60a5fa; text-decoration: none;">Filters</a>
                </div>
            </div>
            <div class="config-info">
                <div style="display: flex; align-items: center; gap: 12px;">
                    <span>Target:</span>
                    <select id="environmentSelector" style="background: #374151; color: #e5e7eb; border: 1px solid #4b5563; border-radius: 4px; padding: 4px 8px; font-size: 0.875rem; cursor: pointer;">
                        <option value="">Loading...</option>
                    </select>
                    <strong id="currentUrl">{{ config.CONTRACT_TEST_URL }}</strong>
                </div>
            </div>
        </header>

        <div class="toolbar">
            <button class="btn-primary" id="runAllBtn" onclick="runAll()">Run All</button>
            <button class="btn-secondary" id="runSelectedBtn" onclick="runSelected()">Run Selected</button>
            <button class="btn-secondary" onclick="clearResults()">Clear Results</button>
            <span style="margin-left: auto; color: #6b7280;">{{ total_tests }} tests discovered</span>
        </div>

        <div class="main-content">
            <div class="panel">
                <div class="panel-header">
                    <span>Tests</span>
                    <button class="btn-secondary" onclick="toggleAll()" style="padding: 4px 8px; font-size: 0.75rem;">Toggle All</button>
                </div>
                <div class="panel-body" id="testsPanel">
                    {% for folder_name, folder in tests_tree.items() %}
                    <div class="folder" data-folder="{{ folder_name }}">
                        <div class="folder-header" onclick="toggleFolder(this)">
                            <span class="toggle-icon">&#9660;</span>
                            <input type="checkbox" onclick="event.stopPropagation(); toggleFolderCheckbox(this)" checked>
                            <span class="folder-name">{{ folder_name }}/</span>
                            <span class="test-count">{{ folder.test_count }}</span>
                        </div>
                        <div class="folder-content">
                            {% for module_name, module in folder.modules.items() %}
                            <div class="module" data-module="{{ folder_name }}.{{ module_name }}">
                                <div class="module-header" onclick="toggleModule(this)">
                                    <span class="toggle-icon">&#9660;</span>
                                    <input type="checkbox" onclick="event.stopPropagation(); toggleModuleCheckbox(this)" checked>
                                    <span class="module-name">{{ module_name }}.py</span>
                                    <span class="test-count">{{ module.test_count }}</span>
                                </div>
                                <div class="module-content">
                                    {% for class_name, cls in module.classes.items() %}
                                    <div class="class-block" data-class="{{ folder_name }}.{{ module_name }}.{{ class_name }}">
                                        <div class="class-header" onclick="toggleClass(this)">
                                            <span class="toggle-icon">&#9660;</span>
                                            <input type="checkbox" onclick="event.stopPropagation(); toggleClassCheckbox(this)" checked>
                                            <span>{{ class_name }}</span>
                                            <span class="test-count">{{ cls.test_count }}</span>
                                        </div>
                                        <div class="class-content test-list">
                                            {% for test in cls.tests %}
                                            <div class="test-item">
                                                <input type="checkbox" data-test-id="{{ test.id }}" checked>
                                                <span class="test-name" title="{{ test.doc or '' }}">{{ test.name }}</span>
                                            </div>
                                            {% endfor %}
                                        </div>
                                    </div>
                                    {% endfor %}
                                </div>
                            </div>
                            {% endfor %}
                        </div>
                    </div>
                    {% endfor %}
                </div>
            </div>

            <div class="panel">
                <div class="panel-header">
                    <span>Results</span>
                    <span id="runDuration" style="font-size: 0.75rem; color: #9ca3af;"></span>
                </div>
                <div class="panel-body" id="resultsPanel">
                    <div class="summary" id="summary" style="display: none;">
                        <div class="stat stat-passed">
                            <div class="stat-value" id="passedCount">0</div>
                            <div class="stat-label">Passed</div>
                        </div>
                        <div class="stat stat-failed">
                            <div class="stat-value" id="failedCount">0</div>
                            <div class="stat-label">Failed</div>
                        </div>
                        <div class="stat stat-skipped">
                            <div class="stat-value" id="skippedCount">0</div>
                            <div class="stat-label">Skipped</div>
                        </div>
                        <div class="stat stat-running">
                            <div class="stat-value" id="runningCount">0</div>
                            <div class="stat-label">Running</div>
                        </div>
                    </div>
                    <div class="progress-bar" id="progressBar" style="display: none;">
                        <div class="progress-fill" id="progressFill" style="width: 0%;"></div>
                    </div>
                    <div class="current-test" id="currentTest" style="display: none;"></div>
                    <div id="resultsList">
                        <div class="empty-state">
                            Run tests to see results
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script>
        let currentRunId = null;
        let pollInterval = null;

        // Parse URL parameters for filters
        const urlParams = new URLSearchParams(window.location.search);
        const filterParams = {
            search: urlParams.get('search') || '',
            domains: urlParams.get('domains') ? new Set(urlParams.get('domains').split(',')) : new Set(),
            modules: urlParams.get('modules') ? new Set(urlParams.get('modules').split(',')) : new Set(),
            status: urlParams.get('status') ? new Set(urlParams.get('status').split(',')) : new Set(),
        };

        // Check if there's a run ID in URL
        const autoRunId = urlParams.get('run');

        // Format "TestCoverageCheck" -> "Coverage Check"
        function formatClassName(name) {
            // Remove "Test" prefix
            let formatted = name.replace(/^Test/, '');
            // Add space before each capital letter
            formatted = formatted.replace(/([A-Z])/g, ' $1').trim();
            return formatted;
        }

        // Format "test_returns_coverage_boolean" -> "returns coverage boolean"
        function formatTestName(name) {
            // Remove "test_" prefix
            let formatted = name.replace(/^test_/, '');
            // Replace underscores with spaces
            formatted = formatted.replace(/_/g, ' ');
            return formatted;
        }

        // Apply filters to test tree
        function applyFilters() {
            const folders = document.querySelectorAll('.folder');

            folders.forEach(folder => {
                const folderName = folder.dataset.folder;
                let hasVisibleTests = false;

                // Check domain filter
                if (filterParams.domains.size > 0 && !filterParams.domains.has(folderName)) {
                    folder.style.display = 'none';
                    return;
                }

                // Check modules
                const modules = folder.querySelectorAll('.module');
                modules.forEach(module => {
                    const moduleName = module.dataset.module.split('.')[1];
                    let moduleVisible = true;

                    if (filterParams.modules.size > 0 && !filterParams.modules.has(moduleName)) {
                        moduleVisible = false;
                    }

                    // Check search filter on test names
                    if (filterParams.search && moduleVisible) {
                        const tests = module.querySelectorAll('.test-item');
                        let hasMatchingTest = false;
                        tests.forEach(test => {
                            const testName = test.querySelector('.test-name').textContent.toLowerCase();
                            if (testName.includes(filterParams.search.toLowerCase())) {
                                hasMatchingTest = true;
                            }
                        });
                        if (!hasMatchingTest) {
                            moduleVisible = false;
                        }
                    }

                    if (moduleVisible) {
                        module.style.display = '';
                        hasVisibleTests = true;
                    } else {
                        module.style.display = 'none';
                    }
                });

                folder.style.display = hasVisibleTests ? '' : 'none';
            });
        }

        // Load environments
        async function loadEnvironments() {
            try {
                const response = await fetch('/tools/tester/api/environments');
                const data = await response.json();
                const selector = document.getElementById('environmentSelector');
                const currentUrl = document.getElementById('currentUrl');

                // Get saved environment from localStorage
                const savedEnvId = localStorage.getItem('selectedEnvironment');
                let selectedEnv = null;

                // Populate selector
                selector.innerHTML = data.environments.map(env => {
                    const isDefault = env.default || env.id === savedEnvId;
                    if (isDefault) selectedEnv = env;
                    return `<option value="${env.id}" ${isDefault ? 'selected' : ''}>${env.name} ${env.has_api_key ? '🔑' : ''}</option>`;
                }).join('');

                // Update URL display
                if (selectedEnv) {
                    currentUrl.textContent = selectedEnv.url;
                }

                // Handle environment changes
                selector.addEventListener('change', async (e) => {
                    const envId = e.target.value;
                    try {
                        const response = await fetch(`/tools/tester/api/environment/select?env_id=${envId}`, {
                            method: 'POST'
                        });
                        const data = await response.json();

                        if (data.success) {
                            currentUrl.textContent = data.environment.url;
                            localStorage.setItem('selectedEnvironment', envId);

                            // Show notification
                            const notification = document.createElement('div');
                            notification.textContent = `Switched to ${data.environment.name}`;
                            notification.style.cssText = 'position: fixed; top: 20px; right: 20px; background: #2563eb; color: white; padding: 12px 20px; border-radius: 6px; z-index: 1000; animation: fadeIn 0.3s;';
                            document.body.appendChild(notification);
                            setTimeout(() => notification.remove(), 3000);
                        }
                    } catch (error) {
                        console.error('Failed to switch environment:', error);
                        alert('Failed to switch environment');
                    }
                });
            } catch (error) {
                console.error('Failed to load environments:', error);
            }
        }

        // Apply formatting and filters on page load
        document.addEventListener('DOMContentLoaded', function() {
            // Load environments
            loadEnvironments();
            // Format class names
            document.querySelectorAll('.class-header > span:not(.toggle-icon):not(.test-count)').forEach(el => {
                if (!el.querySelector('input')) {
                    el.textContent = formatClassName(el.textContent);
                }
            });
            // Format test names
            document.querySelectorAll('.test-name').forEach(el => {
                el.textContent = formatTestName(el.textContent);
            });

            // Apply filters from URL
            if (filterParams.domains.size > 0 || filterParams.modules.size > 0 || filterParams.search) {
                applyFilters();
            }

            // Auto-start run if run ID in URL
            if (autoRunId) {
                currentRunId = autoRunId;
                document.getElementById('summary').style.display = 'flex';
                document.getElementById('progressBar').style.display = 'block';
                pollInterval = setInterval(pollStatus, 1000);
                pollStatus();
            }
        });

        function getSelectedTestIds() {
            const checkboxes = document.querySelectorAll('.test-item input[type="checkbox"]:checked');
            return Array.from(checkboxes).map(cb => cb.dataset.testId);
        }

        async function runAll() {
            await startRun(null);
        }

        async function runSelected() {
            const testIds = getSelectedTestIds();
            if (testIds.length === 0) {
                alert('No tests selected');
                return;
            }
            await startRun(testIds);
        }

        async function startRun(testIds) {
            document.getElementById('runAllBtn').disabled = true;
            document.getElementById('runSelectedBtn').disabled = true;
            document.getElementById('summary').style.display = 'flex';
            document.getElementById('progressBar').style.display = 'block';
            document.getElementById('resultsList').innerHTML = '';

            try {
                const response = await fetch('/tools/tester/api/run', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ test_ids: testIds }),
                });
                const data = await response.json();
                currentRunId = data.run_id;

                // Start polling
                pollInterval = setInterval(pollStatus, 1000);
                pollStatus(); // Immediate first poll
            } catch (error) {
                console.error('Failed to start run:', error);
                document.getElementById('runAllBtn').disabled = false;
                document.getElementById('runSelectedBtn').disabled = false;
            }
        }

        async function pollStatus() {
            if (!currentRunId) return;

            try {
                const response = await fetch(`/tools/tester/api/run/${currentRunId}`);
                const data = await response.json();

                updateUI(data);

                if (data.status === 'completed' || data.status === 'failed') {
                    clearInterval(pollInterval);
                    pollInterval = null;
                    document.getElementById('runAllBtn').disabled = false;
                    document.getElementById('runSelectedBtn').disabled = false;
                }
            } catch (error) {
                console.error('Poll failed:', error);
            }
        }

        function updateUI(data) {
            // Update counts
            document.getElementById('passedCount').textContent = data.passed;
            document.getElementById('failedCount').textContent = data.failed + data.errors;
            document.getElementById('skippedCount').textContent = data.skipped;
            document.getElementById('runningCount').textContent = data.total - data.completed;

            // Update progress
            const progress = data.total > 0 ? (data.completed / data.total * 100) : 0;
            document.getElementById('progressFill').style.width = progress + '%';

            // Update duration
            if (data.duration) {
                document.getElementById('runDuration').textContent = data.duration.toFixed(1) + 's';
            }

            // Current test
            const currentTestEl = document.getElementById('currentTest');
            if (data.current_test) {
                currentTestEl.textContent = 'Running: ' + data.current_test;
                currentTestEl.style.display = 'block';
            } else {
                currentTestEl.style.display = 'none';
            }

            // Results list
            const resultsList = document.getElementById('resultsList');
            resultsList.innerHTML = data.results.map(r => renderResult(r)).join('');
        }

        function renderResult(result) {
            const icons = {
                passed: '&#10003;',
                failed: '&#10007;',
                error: '&#10007;',
                skipped: '&#8722;',
                running: '&#9679;',
            };

            let errorHtml = '';
            if (result.error_message) {
                errorHtml = `<div class="result-error">${escapeHtml(result.error_message)}</div>`;
            }

            // Render artifacts (videos, screenshots)
            let artifactsHtml = '';
            if (result.artifacts && result.artifacts.length > 0) {
                const artifactItems = result.artifacts.map(artifact => {
                    if (artifact.type === 'video') {
                        return `
                            <div style="margin-top: 8px;">
                                <div style="font-size: 0.75rem; color: #9ca3af; margin-bottom: 4px;">
                                    📹 ${artifact.filename} (${formatBytes(artifact.size)})
                                </div>
                                <video controls style="max-width: 100%; border-radius: 4px; background: #000;">
                                    <source src="${artifact.url}" type="video/webm">
                                    Your browser does not support video playback.
                                </video>
                            </div>
                        `;
                    } else if (artifact.type === 'screenshot') {
                        return `
                            <div style="margin-top: 8px;">
                                <div style="font-size: 0.75rem; color: #9ca3af; margin-bottom: 4px;">
                                    📸 ${artifact.filename} (${formatBytes(artifact.size)})
                                </div>
                                <img src="${artifact.url}" style="max-width: 100%; border-radius: 4px; border: 1px solid #374151;">
                            </div>
                        `;
                    } else {
                        return `
                            <div style="margin-top: 8px; font-size: 0.75rem; color: #9ca3af;">
                                📎 <a href="${artifact.url}" style="color: #60a5fa;">${artifact.filename}</a> (${formatBytes(artifact.size)})
                            </div>
                        `;
                    }
                }).join('');
                artifactsHtml = `<div class="result-artifacts">${artifactItems}</div>`;
            }

            return `
                <div class="result-item result-${result.status}">
                    <div class="result-icon">${icons[result.status] || '?'}</div>
                    <div class="result-info">
                        <div class="result-name">${escapeHtml(result.name)}</div>
                        <div class="result-test-id">${escapeHtml(result.test_id)}</div>
                        ${errorHtml}
                        ${artifactsHtml}
                    </div>
                    <div class="result-duration">${result.duration.toFixed(3)}s</div>
                </div>
            `;
        }

        function formatBytes(bytes) {
            if (bytes < 1024) return bytes + ' B';
            if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
            return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
        }

        function escapeHtml(text) {
            const div = document.createElement('div');
            div.textContent = text;
            return div.innerHTML;
        }

        function clearResults() {
            document.getElementById('summary').style.display = 'none';
            document.getElementById('progressBar').style.display = 'none';
            document.getElementById('currentTest').style.display = 'none';
            document.getElementById('runDuration').textContent = '';
            document.getElementById('resultsList').innerHTML = '<div class="empty-state">Run tests to see results</div>';
        }

        // Toggle functions
        function toggleFolder(header) {
            header.parentElement.classList.toggle('collapsed');
        }

        function toggleModule(header) {
            header.parentElement.classList.toggle('collapsed');
        }

        function toggleClass(header) {
            header.parentElement.classList.toggle('collapsed');
        }

        function toggleAll() {
            const folders = document.querySelectorAll('.folder');
            const allCollapsed = Array.from(folders).every(f => f.classList.contains('collapsed'));

            folders.forEach(folder => {
                if (allCollapsed) {
                    folder.classList.remove('collapsed');
                } else {
                    folder.classList.add('collapsed');
                }
            });
        }

        function toggleFolderCheckbox(checkbox) {
            const folder = checkbox.closest('.folder');
            const childCheckboxes = folder.querySelectorAll('input[type="checkbox"]');
            childCheckboxes.forEach(cb => cb.checked = checkbox.checked);
        }

        function toggleModuleCheckbox(checkbox) {
            const module = checkbox.closest('.module');
            const childCheckboxes = module.querySelectorAll('.test-item input[type="checkbox"]');
            childCheckboxes.forEach(cb => cb.checked = checkbox.checked);
        }

        function toggleClassCheckbox(checkbox) {
            const classBlock = checkbox.closest('.class-block');
            const childCheckboxes = classBlock.querySelectorAll('.test-item input[type="checkbox"]');
            childCheckboxes.forEach(cb => cb.checked = checkbox.checked);
        }
    </script>
</body>
</html>
