<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SNPSnip - Interactive VCF Filtering</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
    <style>
        body {
            padding-top: 20px;
            padding-bottom: 40px;
        }
        .histogram {
            margin-bottom: 20px;
        }
        .histogram rect {
            fill: steelblue;
        }
        .histogram rect:hover {
            fill: orange;
        }
        .pca-plot {
            border: 2px solid #ccc;
            border-radius: 5px;
            padding: 10px;
            background-color: #f9f9f9;
        }
        .pca-plot circle {
            fill: steelblue;
            opacity: 0.7;
        }
        .pca-plot circle.selected {
            fill: orange;
        }
        .pca-plot circle.highlighted {
            stroke: red;
            stroke-width: 2px;
        }
        .threshold-line {
            stroke: red;
            stroke-width: 2px;
            stroke-dasharray: 5,5;
        }
        .group-container {
            border: 1px solid #ddd;
            border-radius: 5px;
            padding: 15px;
            margin-bottom: 20px;
        }
        .group-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px;
        }
        .loading {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(255, 255, 255, 0.8);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 1000;
        }
        .loading-spinner {
            width: 50px;
            height: 50px;
            border: 5px solid #f3f3f3;
            border-top: 5px solid #3498db;
            border-radius: 50%;
            animation: spin 2s linear infinite;
        }
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
    </style>
</head>
<body>
    <div class="container">
        <h1 class="mb-4">SNPSnip - Interactive VCF Filtering</h1>
        
        <div id="loading" class="loading" style="display: none;">
            <div class="loading-spinner"></div>
            <div class="ms-3">Processing...</div>
        </div>
        
        <div id="stage-indicator" class="alert alert-info mb-4">
            Loading application state...
        </div>
        
        <!-- Sample Filtering Stage -->
        <div id="sample-filtering" style="display: none;">
            <h2>Step 1: Sample Filtering</h2>
            <p>Filter samples based on quality metrics and PCA clustering.</p>
            
            <div class="row mb-4">
                <div class="col-md-6">
                    <div class="card">
                        <div class="card-header">Sample Missingness</div>
                        <div class="card-body">
                            <div id="missingness-histogram" class="histogram"></div>
                            <div class="d-flex justify-content-between">
                                <div>
                                    <label for="missingness-threshold" class="form-label">Threshold:</label>
                                    <input type="range" class="form-range" id="missingness-threshold" min="0" max="1" step="0.01" value="0.1">
                                </div>
                                <div>
                                    <span id="missingness-value">0.1</span>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                
                <div class="col-md-6">
                    <div class="card">
                        <div class="card-header">Sample Mean Depth</div>
                        <div class="card-body">
                            <div id="depth-histogram" class="histogram"></div>
                            <div class="d-flex justify-content-between">
                                <div>
                                    <label for="depth-threshold" class="form-label">Threshold:</label>
                                    <input type="range" class="form-range" id="depth-threshold" min="0" max="100" step="1" value="10">
                                </div>
                                <div>
                                    <span id="depth-value">10</span>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            
            <div class="card mb-4">
                <div class="card-header">Sample PCA</div>
                <div class="card-body">
                    <div id="pca-plot" class="pca-plot"></div>
                    <div class="mt-3">
                        <button id="create-group" class="btn btn-primary">Create Group from Selection</button>
                        <button id="clear-selection" class="btn btn-secondary">Clear Selection</button>
                        <button id="invert-selection" class="btn btn-info">Invert Selection</button>
                        <button id="select-unassigned" class="btn btn-warning">Select Unassigned</button>
                    </div>
                </div>
            </div>
            
            <div id="sample-groups" class="mb-4">
                <h3>Sample Groups</h3>
                <div id="group-list"></div>
            </div>
            
            <button id="submit-sample-filters" class="btn btn-success btn-lg">Continue to Variant Filtering</button>
        </div>
        
        <!-- Variant Filtering Stage -->
        <div id="variant-filtering" style="display: none;">
            <h2>Step 2: Variant Filtering</h2>
            <p>Set filtering thresholds for each sample group.</p>
            
            <div id="variant-groups"></div>
            
            <button id="submit-variant-filters" class="btn btn-success btn-lg mt-4">Apply Filters to Full VCF</button>
        </div>
        
        <!-- Completion Stage -->
        <div id="completion" style="display: none;">
            <h2>Filtering Complete</h2>
            <div class="alert alert-success">
                <p>All filtering steps have been completed successfully.</p>
            </div>
            
            <h3>Output Files:</h3>
            <ul id="output-files" class="list-group mb-4"></ul>
            
            <p>You can close this window and return to the command line.</p>
        </div>
    </div>
    
    <!-- Group Name Modal -->
    <div class="modal fade" id="groupNameModal" tabindex="-1" aria-hidden="true">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title">Create Sample Group</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                </div>
                <div class="modal-body">
                    <div class="mb-3">
                        <label for="group-name" class="form-label">Group Name:</label>
                        <input type="text" class="form-control" id="group-name" placeholder="Enter a name for this group">
                    </div>
                    <p><span id="selected-count">0</span> samples selected</p>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
                    <button type="button" class="btn btn-primary" id="save-group">Save Group</button>
                </div>
            </div>
        </div>
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
    <script>
        // Global state
        const state = {
            sampleStats: [],
            pca: [],
            variantStats: {},
            sampleGroups: {},
            selectedSamples: new Set(),
            thresholds: {
                missingness: 0.1,
                depth: 10
            },
            variantThresholds: {}
        };
        
        // Bootstrap modal
        let groupModal;
        
        // Initialize the application
        document.addEventListener('DOMContentLoaded', async () => {
            // Initialize Bootstrap modal
            groupModal = new bootstrap.Modal(document.getElementById('groupNameModal'));
            
            // Set up event listeners
            document.getElementById('missingness-threshold').addEventListener('input', updateMissingnessThreshold);
            document.getElementById('depth-threshold').addEventListener('input', updateDepthThreshold);
            document.getElementById('create-group').addEventListener('click', showGroupModal);
            document.getElementById('clear-selection').addEventListener('click', clearSelection);
            document.getElementById('invert-selection').addEventListener('click', invertSelection);
            document.getElementById('select-unassigned').addEventListener('click', selectUnassigned);
            document.getElementById('save-group').addEventListener('click', saveGroup);
            document.getElementById('submit-sample-filters').addEventListener('click', submitSampleFilters);
            document.getElementById('submit-variant-filters').addEventListener('click', submitVariantFilters);
            
            // Load application state
            await loadState();
        });
        
        // Load application state from the server
        async function loadState() {
            try {
                showLoading(true);
                const response = await fetch('/api/state');
                const appState = await response.json();
                
                updateStageIndicator(appState.stage);
                
                if (appState.stage === 'ready_for_sample_filtering') {
                    await loadSampleFilteringData();
                    showSampleFiltering();
                } else if (appState.stage === 'ready_for_variant_filtering') {
                    await loadVariantFilteringData();
                    showVariantFiltering();
                } else if (appState.completed) {
                    showCompletion(appState.output_files || []);
                }
            } catch (error) {
                console.error('Error loading state:', error);
                alert('Error loading application state. Please check the console for details.');
            } finally {
                showLoading(false);
            }
        }
        
        // Update the stage indicator
        function updateStageIndicator(stage) {
            const indicator = document.getElementById('stage-indicator');
            
            switch (stage) {
                case 'init':
                    indicator.textContent = 'Initializing...';
                    break;
                case 'ready_for_sample_filtering':
                    indicator.textContent = 'Ready for sample filtering';
                    break;
                case 'sample_filtered':
                    indicator.textContent = 'Processing variant statistics...';
                    break;
                case 'ready_for_variant_filtering':
                    indicator.textContent = 'Ready for variant filtering';
                    break;
                case 'ready_for_final':
                    indicator.textContent = 'Applying final filters...';
                    break;
                default:
                    indicator.textContent = `Current stage: ${stage}`;
            }
        }
        
        // Load data for sample filtering
        async function loadSampleFilteringData() {
            // Load sample stats
            const statsResponse = await fetch('/api/sample_stats');
            state.sampleStats = await statsResponse.json();
            
            // Load PCA data
            const pcaResponse = await fetch('/api/pca');
            state.pca = await pcaResponse.json();
            
            // Render visualizations
            renderMissingnessHistogram();
            renderDepthHistogram();
            renderPcaPlot();
        }
        
        // Load data for variant filtering
        async function loadVariantFilteringData() {
            // Load sample groups
            const stateResponse = await fetch('/api/state');
            const appState = await stateResponse.json();
            state.sampleGroups = appState.sample_groups || {};
            
            // Load variant stats for each group
            for (const groupName in state.sampleGroups) {
                try {
                    const response = await fetch(`/api/variant_stats/${groupName}`);
                    state.variantStats[groupName] = await response.json();
                } catch (error) {
                    console.error(`Error loading variant stats for group ${groupName}:`, error);
                }
            }
            
            // Initialize variant thresholds
            for (const groupName in state.sampleGroups) {
                state.variantThresholds[groupName] = {
                    qual: { min: null, max: null },
                    depth: { min: null, max: null },
                    af: { min: null, max: null },
                    missing: { min: null, max: null }
                };
            }
            
            // Render variant filtering UI
            renderVariantFiltering();
        }
        
        // Show sample filtering UI
        function showSampleFiltering() {
            document.getElementById('sample-filtering').style.display = 'block';
            document.getElementById('variant-filtering').style.display = 'none';
            document.getElementById('completion').style.display = 'none';
        }
        
        // Show variant filtering UI
        function showVariantFiltering() {
            document.getElementById('sample-filtering').style.display = 'none';
            document.getElementById('variant-filtering').style.display = 'block';
            document.getElementById('completion').style.display = 'none';
        }
        
        // Show completion UI
        function showCompletion(outputFiles) {
            document.getElementById('sample-filtering').style.display = 'none';
            document.getElementById('variant-filtering').style.display = 'none';
            document.getElementById('completion').style.display = 'block';
            
            const filesList = document.getElementById('output-files');
            filesList.innerHTML = '';
            
            outputFiles.forEach(file => {
                const li = document.createElement('li');
                li.className = 'list-group-item';
                li.textContent = file;
                filesList.appendChild(li);
            });
        }
        
        // Show/hide loading indicator
        function showLoading(show) {
            document.getElementById('loading').style.display = show ? 'flex' : 'none';
        }
        
        // Render missingness histogram
        function renderMissingnessHistogram() {
            const data = state.sampleStats.map(s => s.missing_rate || 0);
            
            const width = 500;
            const height = 200;
            const margin = { top: 20, right: 30, bottom: 30, left: 40 };
            
            const svg = d3.select('#missingness-histogram')
                .html('')
                .append('svg')
                .attr('width', width)
                .attr('height', height);
            
            const x = d3.scaleLinear()
                .domain([0, d3.max(data) || 1])
                .range([margin.left, width - margin.right]);
            
            const bins = d3.histogram()
                .domain(x.domain())
                .thresholds(x.ticks(20))
                (data);
            
            const y = d3.scaleLinear()
                .domain([0, d3.max(bins, d => d.length)])
                .range([height - margin.bottom, margin.top]);
            
            svg.append('g')
                .attr('transform', `translate(0,${height - margin.bottom})`)
                .call(d3.axisBottom(x));
            
            svg.append('g')
                .attr('transform', `translate(${margin.left},0)`)
                .call(d3.axisLeft(y));
            
            svg.append('g')
                .selectAll('rect')
                .data(bins)
                .join('rect')
                .attr('x', d => x(d.x0) + 1)
                .attr('width', d => Math.max(0, x(d.x1) - x(d.x0) - 1))
                .attr('y', d => y(d.length))
                .attr('height', d => y(0) - y(d.length));
            
            // Add threshold line
            svg.append('line')
                .attr('class', 'threshold-line')
                .attr('x1', x(state.thresholds.missingness))
                .attr('x2', x(state.thresholds.missingness))
                .attr('y1', margin.top)
                .attr('y2', height - margin.bottom);
            
            // Store the x scale for later use
            state.missingnessScale = x;
        }
        
        // Render depth histogram
        function renderDepthHistogram() {
            const data = state.sampleStats.map(s => s.mean_depth || 0);
            
            const width = 500;
            const height = 200;
            const margin = { top: 20, right: 30, bottom: 30, left: 40 };
            
            const svg = d3.select('#depth-histogram')
                .html('')
                .append('svg')
                .attr('width', width)
                .attr('height', height);
            
            const x = d3.scaleLinear()
                .domain([0, d3.max(data) || 100])
                .range([margin.left, width - margin.right]);
            
            const bins = d3.histogram()
                .domain(x.domain())
                .thresholds(x.ticks(20))
                (data);
            
            const y = d3.scaleLinear()
                .domain([0, d3.max(bins, d => d.length)])
                .range([height - margin.bottom, margin.top]);
            
            svg.append('g')
                .attr('transform', `translate(0,${height - margin.bottom})`)
                .call(d3.axisBottom(x));
            
            svg.append('g')
                .attr('transform', `translate(${margin.left},0)`)
                .call(d3.axisLeft(y));
            
            svg.append('g')
                .selectAll('rect')
                .data(bins)
                .join('rect')
                .attr('x', d => x(d.x0) + 1)
                .attr('width', d => Math.max(0, x(d.x1) - x(d.x0) - 1))
                .attr('y', d => y(d.length))
                .attr('height', d => y(0) - y(d.length));
            
            // Add threshold line
            svg.append('line')
                .attr('class', 'threshold-line')
                .attr('x1', x(state.thresholds.depth))
                .attr('x2', x(state.thresholds.depth))
                .attr('y1', margin.top)
                .attr('y2', height - margin.bottom);
            
            // Store the x scale for later use
            state.depthScale = x;
        }
        
        // Render PCA plot
        function renderPcaPlot() {
            const data = state.pca;
            
            const width = 800;
            const height = 600;
            const margin = { top: 20, right: 150, bottom: 30, left: 40 }; // Increased right margin for legend
            
            const svg = d3.select('#pca-plot')
                .html('')
                .append('svg')
                .attr('width', width)
                .attr('height', height);
            
            // Create scales
            const x = d3.scaleLinear()
                .domain(d3.extent(data, d => d.pc1))
                .range([margin.left, width - margin.right]);
            
            const y = d3.scaleLinear()
                .domain(d3.extent(data, d => d.pc2))
                .range([height - margin.bottom, margin.top]);
            
            // Add axes
            svg.append('g')
                .attr('transform', `translate(0,${height - margin.bottom})`)
                .call(d3.axisBottom(x).tickFormat(d3.format('.2f')));
            
            svg.append('g')
                .attr('transform', `translate(${margin.left},0)`)
                .call(d3.axisLeft(y).tickFormat(d3.format('.2f')));
            
            // Add axis labels
            svg.append('text')
                .attr('x', width / 2)
                .attr('y', height - 5)
                .attr('text-anchor', 'middle')
                .text('PC1');
            
            svg.append('text')
                .attr('transform', 'rotate(-90)')
                .attr('x', -height / 2)
                .attr('y', 15)
                .attr('text-anchor', 'middle')
                .text('PC2');
            
            // Check if we have predefined groups
            const hasGroups = data.length > 0 && data[0].hasOwnProperty('group');
            
            // Create color scale for groups if needed
            let colorScale;
            let groups = [];
            
            if (hasGroups) {
                groups = [...new Set(data.map(d => d.group))];
                colorScale = d3.scaleOrdinal()
                    .domain(groups)
                    .range(d3.schemeCategory10);
            }
            
            // Add points
            const points = svg.append('g')
                .selectAll('circle')
                .data(data)
                .join('circle')
                .attr('cx', d => x(d.pc1))
                .attr('cy', d => y(d.pc2))
                .attr('r', 5)
                .attr('fill', d => hasGroups ? colorScale(d.group) : 'steelblue')
                .attr('class', d => state.selectedSamples.has(d.sample) ? 'selected' : '')
                .on('click', function(event, d) {
                    toggleSampleSelection(d.sample);
                    d3.select(this).classed('selected', state.selectedSamples.has(d.sample));
                })
                .append('title')
                .text(d => hasGroups ? `${d.sample} (${d.group})` : d.sample);
            
            // Add legend if we have groups
            if (hasGroups && groups.length > 0) {
                const legend = svg.append('g')
                    .attr('transform', `translate(${width - margin.right + 20}, ${margin.top})`);
                
                legend.append('text')
                    .attr('x', 0)
                    .attr('y', -10)
                    .attr('font-weight', 'bold')
                    .text('Groups');
                
                groups.forEach((group, i) => {
                    const legendRow = legend.append('g')
                        .attr('transform', `translate(0, ${i * 20})`);
                    
                    legendRow.append('rect')
                        .attr('width', 10)
                        .attr('height', 10)
                        .attr('fill', colorScale(group));
                    
                    legendRow.append('text')
                        .attr('x', 15)
                        .attr('y', 10)
                        .text(group);
                });
            }
            
            // Add brush for selection
            const brush = d3.brush()
                .extent([[margin.left, margin.top], [width - margin.right, height - margin.bottom]])
                .on('end', brushed);
            
            svg.append('g')
                .attr('class', 'brush')
                .call(brush);
            
            function brushed(event) {
                if (!event.selection) return;
                
                const [[x0, y0], [x1, y1]] = event.selection;
                
                data.forEach(d => {
                    const cx = x(d.pc1);
                    const cy = y(d.pc2);
                    
                    if (cx >= x0 && cx <= x1 && cy >= y0 && cy <= y1) {
                        state.selectedSamples.add(d.sample);
                    }
                });
                
                // Update points
                svg.selectAll('circle')
                    .classed('selected', d => state.selectedSamples.has(d.sample));
                
                // Clear brush
                svg.select('.brush').call(brush.move, null);
            }
        }
        
        // Toggle sample selection
        function toggleSampleSelection(sample) {
            if (state.selectedSamples.has(sample)) {
                state.selectedSamples.delete(sample);
            } else {
                state.selectedSamples.add(sample);
            }
        }
        
        // Clear selection
        function clearSelection() {
            state.selectedSamples.clear();
            d3.selectAll('#pca-plot circle').classed('selected', false);
        }
        
        // Invert selection
        function invertSelection() {
            const allSamples = state.pca.map(d => d.sample);
            allSamples.forEach(sample => {
                if (state.selectedSamples.has(sample)) {
                    state.selectedSamples.delete(sample);
                } else {
                    state.selectedSamples.add(sample);
                }
            });
            d3.selectAll('#pca-plot circle').classed('selected', d => state.selectedSamples.has(d.sample));
        }
        
        // Select all unassigned samples
        function selectUnassigned() {
            // Get all samples that are already in groups
            const assignedSamples = new Set();
            Object.values(state.sampleGroups).forEach(samples => {
                samples.forEach(sample => assignedSamples.add(sample));
            });
            
            // Select all samples that aren't in any group
            state.selectedSamples.clear();
            state.pca.forEach(d => {
                if (!assignedSamples.has(d.sample)) {
                    state.selectedSamples.add(d.sample);
                }
            });
            
            d3.selectAll('#pca-plot circle').classed('selected', d => state.selectedSamples.has(d.sample));
        }
        
        // Show group modal
        function showGroupModal() {
            document.getElementById('selected-count').textContent = state.selectedSamples.size;
            document.getElementById('group-name').value = '';
            groupModal.show();
        }
        
        // Save group
        function saveGroup() {
            const groupName = document.getElementById('group-name').value.trim();
            
            if (!groupName) {
                alert('Please enter a group name');
                return;
            }
            
            if (state.selectedSamples.size === 0) {
                alert('No samples selected');
                return;
            }
            
            // Save group
            state.sampleGroups[groupName] = Array.from(state.selectedSamples);
            
            // Update UI
            renderSampleGroups();
            
            // Clear selection
            clearSelection();
            
            // Hide modal
            groupModal.hide();
        }
        
        // Render sample groups
        function renderSampleGroups() {
            const container = document.getElementById('group-list');
            container.innerHTML = '';
            
            if (Object.keys(state.sampleGroups).length === 0) {
                container.innerHTML = '<div class="alert alert-info">No groups created yet. Use the PCA plot to select samples and create groups.</div>';
                return;
            }
            
            for (const [groupName, samples] of Object.entries(state.sampleGroups)) {
                const groupDiv = document.createElement('div');
                groupDiv.className = 'group-container';
                
                const header = document.createElement('div');
                header.className = 'group-header';
                
                const title = document.createElement('h4');
                title.textContent = groupName;
                
                const count = document.createElement('span');
                count.className = 'badge bg-primary';
                count.textContent = `${samples.length} samples`;
                
                const deleteBtn = document.createElement('button');
                deleteBtn.className = 'btn btn-sm btn-danger';
                deleteBtn.textContent = 'Delete';
                deleteBtn.onclick = () => deleteGroup(groupName);
                
                header.appendChild(title);
                header.appendChild(count);
                header.appendChild(deleteBtn);
                
                const sampleList = document.createElement('div');
                sampleList.className = 'sample-list';
                sampleList.style.maxHeight = '100px';
                sampleList.style.overflow = 'auto';
                
                const sampleText = document.createElement('small');
                sampleText.className = 'text-muted';
                sampleText.textContent = samples.join(', ');
                
                sampleList.appendChild(sampleText);
                
                groupDiv.appendChild(header);
                groupDiv.appendChild(sampleList);
                
                container.appendChild(groupDiv);
            }
        }
        
        // Delete group
        function deleteGroup(groupName) {
            if (confirm(`Are you sure you want to delete the group "${groupName}"?`)) {
                delete state.sampleGroups[groupName];
                renderSampleGroups();
            }
        }
        
        // Update missingness threshold
        function updateMissingnessThreshold(event) {
            const value = parseFloat(event.target.value);
            state.thresholds.missingness = value;
            document.getElementById('missingness-value').textContent = value.toFixed(2);
            
            // Update threshold line
            d3.select('#missingness-histogram .threshold-line')
                .attr('x1', state.missingnessScale(value))
                .attr('x2', state.missingnessScale(value));
        }
        
        // Update depth threshold
        function updateDepthThreshold(event) {
            const value = parseFloat(event.target.value);
            state.thresholds.depth = value;
            document.getElementById('depth-value').textContent = value.toFixed(0);
            
            // Update threshold line
            d3.select('#depth-histogram .threshold-line')
                .attr('x1', state.depthScale(value))
                .attr('x2', state.depthScale(value));
        }
        
        // Submit sample filters
        async function submitSampleFilters() {
            if (Object.keys(state.sampleGroups).length === 0) {
                alert('Please create at least one sample group before continuing.');
                return;
            }
            
            try {
                showLoading(true);
                
                // Apply missingness and depth thresholds to filter samples
                for (const groupName in state.sampleGroups) {
                    state.sampleGroups[groupName] = state.sampleGroups[groupName].filter(sample => {
                        const sampleData = state.sampleStats.find(s => s.id === sample);
                        if (!sampleData) return false;
                        
                        const missingRate = sampleData.missing_rate || 0;
                        const meanDepth = sampleData.mean_depth || 0;
                        
                        return missingRate <= state.thresholds.missingness && meanDepth >= state.thresholds.depth;
                    });
                }
                
                // Submit to server
                const response = await fetch('/api/submit_sample_filters', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({
                        groups: state.sampleGroups
                    })
                });
                
                const result = await response.json();
                
                if (result.success) {
                    // Reload state and show variant filtering
                    await loadState();
                } else {
                    alert('Error submitting sample filters');
                }
            } catch (error) {
                console.error('Error submitting sample filters:', error);
                alert('Error submitting sample filters. Please check the console for details.');
            } finally {
                showLoading(false);
            }
        }
        
        // Render variant filtering UI
        function renderVariantFiltering() {
            const container = document.getElementById('variant-groups');
            container.innerHTML = '';
            
            for (const groupName in state.sampleGroups) {
                const groupStats = state.variantStats[groupName] || {};
                
                const groupDiv = document.createElement('div');
                groupDiv.className = 'group-container mb-4';
                
                const header = document.createElement('h3');
                header.textContent = `Group: ${groupName}`;
                
                groupDiv.appendChild(header);
                
                // Create metric cards
                const metricsRow = document.createElement('div');
                metricsRow.className = 'row';
                
                // Quality
                if (groupStats.qual) {
                    const qualDiv = createMetricCard(
                        'Quality',
                        groupStats.qual,
                        `${groupName}-qual`,
                        (min, max) => {
                            state.variantThresholds[groupName].qual.min = min;
                            state.variantThresholds[groupName].qual.max = max;
                        }
                    );
                    metricsRow.appendChild(qualDiv);
                }
                
                // Depth
                if (groupStats.depth) {
                    const depthDiv = createMetricCard(
                        'Depth',
                        groupStats.depth,
                        `${groupName}-depth`,
                        (min, max) => {
                            state.variantThresholds[groupName].depth.min = min;
                            state.variantThresholds[groupName].depth.max = max;
                        }
                    );
                    metricsRow.appendChild(depthDiv);
                }
                
                // Allele frequency
                if (groupStats.af) {
                    const afDiv = createMetricCard(
                        'Allele Frequency',
                        groupStats.af,
                        `${groupName}-af`,
                        (min, max) => {
                            state.variantThresholds[groupName].af.min = min;
                            state.variantThresholds[groupName].af.max = max;
                        }
                    );
                    metricsRow.appendChild(afDiv);
                }
                
                // Missing rate
                if (groupStats.missing) {
                    const missingDiv = createMetricCard(
                        'Missing Rate',
                        groupStats.missing,
                        `${groupName}-missing`,
                        (min, max) => {
                            state.variantThresholds[groupName].missing.min = min;
                            state.variantThresholds[groupName].missing.max = max;
                        }
                    );
                    metricsRow.appendChild(missingDiv);
                }
                
                groupDiv.appendChild(metricsRow);
                container.appendChild(groupDiv);
            }
        }
        
        // Create metric card
        function createMetricCard(title, data, id, onThresholdChange) {
            const colDiv = document.createElement('div');
            colDiv.className = 'col-md-6 mb-3';
            
            const cardDiv = document.createElement('div');
            cardDiv.className = 'card';
            
            const cardHeader = document.createElement('div');
            cardHeader.className = 'card-header';
            cardHeader.textContent = title;
            
            const cardBody = document.createElement('div');
            cardBody.className = 'card-body';
            
            // Create histogram
            const histogramDiv = document.createElement('div');
            histogramDiv.id = `${id}-histogram`;
            histogramDiv.className = 'histogram';
            
            // Create threshold controls
            const controlsDiv = document.createElement('div');
            controlsDiv.className = 'd-flex justify-content-between align-items-center mt-2';
            
            const minGroup = document.createElement('div');
            minGroup.className = 'input-group input-group-sm me-2';
            
            const minLabel = document.createElement('span');
            minLabel.className = 'input-group-text';
            minLabel.textContent = 'Min';
            
            const minInput = document.createElement('input');
            minInput.type = 'number';
            minInput.className = 'form-control';
            minInput.id = `${id}-min`;
            minInput.placeholder = 'Min';
            minInput.addEventListener('change', () => {
                const value = minInput.value ? parseFloat(minInput.value) : null;
                onThresholdChange(value, maxInput.value ? parseFloat(maxInput.value) : null);
                updateThresholdLines(id, value, maxInput.value ? parseFloat(maxInput.value) : null);
            });
            
            minGroup.appendChild(minLabel);
            minGroup.appendChild(minInput);
            
            const maxGroup = document.createElement('div');
            maxGroup.className = 'input-group input-group-sm';
            
            const maxLabel = document.createElement('span');
            maxLabel.className = 'input-group-text';
            maxLabel.textContent = 'Max';
            
            const maxInput = document.createElement('input');
            maxInput.type = 'number';
            maxInput.className = 'form-control';
            maxInput.id = `${id}-max`;
            maxInput.placeholder = 'Max';
            maxInput.addEventListener('change', () => {
                const value = maxInput.value ? parseFloat(maxInput.value) : null;
                onThresholdChange(minInput.value ? parseFloat(minInput.value) : null, value);
                updateThresholdLines(id, minInput.value ? parseFloat(minInput.value) : null, value);
            });
            
            maxGroup.appendChild(maxLabel);
            maxGroup.appendChild(maxInput);
            
            controlsDiv.appendChild(minGroup);
            controlsDiv.appendChild(maxGroup);
            
            cardBody.appendChild(histogramDiv);
            cardBody.appendChild(controlsDiv);
            
            cardDiv.appendChild(cardHeader);
            cardDiv.appendChild(cardBody);
            
            colDiv.appendChild(cardDiv);
            
            // Render histogram after adding to DOM
            setTimeout(() => {
                renderVariantHistogram(data, id);
            }, 0);
            
            return colDiv;
        }
        
        // Render variant histogram
        function renderVariantHistogram(data, id) {
            const bins = data.bins;
            const counts = data.counts;
            
            if (!bins || !counts || bins.length === 0 || counts.length === 0) {
                document.getElementById(`${id}-histogram`).innerHTML = '<div class="alert alert-warning">No data available</div>';
                return;
            }
            
            const width = 400;
            const height = 200;
            const margin = { top: 20, right: 30, bottom: 30, left: 40 };
            
            const svg = d3.select(`#${id}-histogram`)
                .html('')
                .append('svg')
                .attr('width', width)
                .attr('height', height);
            
            const x = d3.scaleLinear()
                .domain([bins[0], bins[bins.length - 1]])
                .range([margin.left, width - margin.right]);
            
            const y = d3.scaleLinear()
                .domain([0, d3.max(counts)])
                .range([height - margin.bottom, margin.top]);
            
            svg.append('g')
                .attr('transform', `translate(0,${height - margin.bottom})`)
                .call(d3.axisBottom(x));
            
            svg.append('g')
                .attr('transform', `translate(${margin.left},0)`)
                .call(d3.axisLeft(y));
            
            // Create bars
            for (let i = 0; i < bins.length; i++) {
                svg.append('rect')
                    .attr('x', x(bins[i]))
                    .attr('width', i < bins.length - 1 ? x(bins[i + 1]) - x(bins[i]) : 0)
                    .attr('y', y(counts[i]))
                    .attr('height', y(0) - y(counts[i]));
            }
            
            // Add min threshold line
            svg.append('line')
                .attr('class', 'threshold-line')
                .attr('id', `${id}-min-line`)
                .style('display', 'none')
                .attr('y1', margin.top)
                .attr('y2', height - margin.bottom);
            
            // Add max threshold line
            svg.append('line')
                .attr('class', 'threshold-line')
                .attr('id', `${id}-max-line`)
                .style('display', 'none')
                .attr('y1', margin.top)
                .attr('y2', height - margin.bottom);
            
            // Store the x scale for later use
            window[`${id}Scale`] = x;
        }
        
        // Update threshold lines
        function updateThresholdLines(id, min, max) {
            const scale = window[`${id}Scale`];
            if (!scale) return;
            
            const minLine = d3.select(`#${id}-min-line`);
            const maxLine = d3.select(`#${id}-max-line`);
            
            if (min !== null) {
                minLine
                    .style('display', 'block')
                    .attr('x1', scale(min))
                    .attr('x2', scale(min));
            } else {
                minLine.style('display', 'none');
            }
            
            if (max !== null) {
                maxLine
                    .style('display', 'block')
                    .attr('x1', scale(max))
                    .attr('x2', scale(max));
            } else {
                maxLine.style('display', 'none');
            }
        }
        
        // Submit variant filters
        async function submitVariantFilters() {
            try {
                showLoading(true);
                
                // Submit to server
                const response = await fetch('/api/submit_variant_filters', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({
                        thresholds: state.variantThresholds
                    })
                });
                
                const result = await response.json();
                
                if (result.success) {
                    // Show completion
                    showCompletion(result.output_files || []);
                } else {
                    alert('Error submitting variant filters');
                }
            } catch (error) {
                console.error('Error submitting variant filters:', error);
                alert('Error submitting variant filters. Please check the console for details.');
            } finally {
                showLoading(false);
            }
        }
    </script>
</body>
</html>
