/*
 * Artifact Browser JavaScript
 * Handles tree navigation, attachment display, preview, and ZIP download
 */

(function($) {
    'use strict';

    var currentRunId = null;
    var currentBugId = null;
    var currentRunData = null;
    var selectedAttachments = {};

    var CHART_COLORS = [
        '#0088CE', '#3F9C35', '#EC7A08', '#CC0000',
        '#703FEC', '#F0AB00', '#009596', '#5752D1'
    ];

    $(document).ready(function() {
        initTreeToggle();
        initToolbarFilters();
        initGlobalSearch();
        initStatsPanel();
        initExportButtons();
        initSelectAll();
        initZipDownload();
        loadStatistics();
    });

    // ==========================================
    // Tree Navigation
    // ==========================================

    function initTreeToggle() {
        // Product/Plan node toggle
        $('#tree-view').on('click', '.tree-item[data-type="product"] .tree-toggle, .tree-item[data-type="product"]', function(e) {
            if ($(e.target).hasClass('tree-toggle') || !$(e.target).hasClass('tree-toggle')) {
                var $item = $(this).closest('.tree-item');
                var $node = $item.closest('.tree-node');
                var $children = $node.find('> .tree-children');
                toggleNode($item, $children);
            }
        });

        $('#tree-view').on('click', '.tree-item[data-type="plan"] .tree-toggle, .tree-item[data-type="plan"]', function(e) {
            var $item = $(this).closest('.tree-item');
            var $node = $item.closest('.tree-node');
            var $children = $node.find('> .tree-children');
            toggleNode($item, $children);
        });

        // Click on run to load attachments
        $('#tree-view').on('click', '.tree-item[data-type="run"]', function() {
            var runId = $(this).data('id');
            $('.tree-item').removeClass('active');
            $(this).addClass('active');
            loadRunAttachments(runId);
        });

        // Bug tree panel collapse toggle
        $('#bug-tree-heading').on('click', function() {
            var $body = $('#bug-tree-body');
            var $icon = $(this).find('.bug-tree-toggle');
            $body.slideToggle(200);
            $icon.toggleClass('fa-angle-down fa-angle-right');
        });

        // Bug tree product toggle
        $('#bug-tree-view').on('click', '.tree-item[data-type="bug-product"]', function() {
            var $item = $(this).closest('.tree-item');
            var $node = $item.closest('.tree-node');
            var $children = $node.find('> .tree-children');
            toggleNode($item, $children);
        });

        // Click on bug to load attachments
        $('#bug-tree-view').on('click', '.tree-item[data-type="bug"]', function() {
            var bugId = $(this).data('id');
            $('.tree-item').removeClass('active');
            $(this).addClass('active');
            loadBugAttachments(bugId);
        });

        // Product filter (applies to both trees)
        $('#filter-product').on('change', function() {
            var productId = $(this).val();
            if (productId) {
                // Filter run tree
                $('#tree-view .product-node').hide();
                $('#tree-view .product-node[data-product-id="' + productId + '"]').show();
                // Filter bug tree
                $('#bug-tree-view .bug-product-node').hide();
                $('#bug-tree-view .bug-product-node[data-product-id="' + productId + '"]').show();
            } else {
                $('#tree-view .product-node').show();
                $('#bug-tree-view .bug-product-node').show();
            }
        });
    }

    function toggleNode($item, $children) {
        var isExpanded = $children.is(':visible');
        if (isExpanded) {
            $children.slideUp(200);
        } else {
            $children.slideDown(200);
        }
        var $toggle = $item.find('.tree-toggle');
        var $folder = $item.find('.pficon-folder-close, .pficon-folder-open');
        if (!isExpanded) {
            $toggle.removeClass('fa-angle-right').addClass('fa-angle-down');
            $folder.removeClass('pficon-folder-close').addClass('pficon-folder-open');
        } else {
            $toggle.removeClass('fa-angle-down').addClass('fa-angle-right');
            $folder.removeClass('pficon-folder-open').addClass('pficon-folder-close');
        }
    }

    // ==========================================
    // Attachment Loading
    // ==========================================

    function loadRunAttachments(runId) {
        currentRunId = runId;
        currentBugId = null;
        selectedAttachments = {};
        updateZipButton();

        var params = {};
        var fileType = $('#filter-file-type').val();
        var filename = $('#filter-filename').val();
        if (fileType) params.file_type = fileType;
        if (filename) params.search = filename;

        $('#detail-placeholder').hide();
        $('#search-results').hide();
        $('#attachment-empty').hide();
        $('#attachment-groups').hide();
        $('#bug-linked-panel').hide();
        $('#attachment-toolbar').show();
        $('#attachment-groups').html(
            '<div class="text-center" style="padding: 30px;">' +
            '<span class="spinner spinner-lg"></span> Loading artifacts...</div>'
        ).show();

        $.ajax({
            url: '/tcms_artifact_browser/api/run/' + runId + '/attachments/',
            data: params,
            dataType: 'json',
            success: function(data) {
                currentRunData = data;
                renderAttachmentGroups(data);
            },
            error: function() {
                $('#attachment-groups').html(
                    '<div class="text-center text-danger" style="padding: 30px;">' +
                    '<span class="fa fa-exclamation-circle"></span> Failed to load attachments</div>'
                );
            }
        });
    }

    function loadBugAttachments(bugId) {
        currentBugId = bugId;
        currentRunId = null;
        selectedAttachments = {};
        updateZipButton();

        var params = {};
        var fileType = $('#filter-file-type').val();
        var filename = $('#filter-filename').val();
        if (fileType) params.file_type = fileType;
        if (filename) params.search = filename;

        $('#detail-placeholder').hide();
        $('#search-results').hide();
        $('#attachment-empty').hide();
        $('#attachment-groups').hide();
        $('#bug-linked-panel').hide();
        $('#attachment-toolbar').show();
        $('#attachment-groups').html(
            '<div class="text-center" style="padding: 30px;">' +
            '<span class="spinner spinner-lg"></span> Loading artifacts...</div>'
        ).show();

        $.ajax({
            url: '/tcms_artifact_browser/api/bug/' + bugId + '/attachments/',
            data: params,
            dataType: 'json',
            success: function(data) {
                currentRunData = data;
                renderAttachmentGroups(data);
            },
            error: function() {
                $('#attachment-groups').html(
                    '<div class="text-center text-danger" style="padding: 30px;">' +
                    '<span class="fa fa-exclamation-circle"></span> Failed to load attachments</div>'
                );
            }
        });
    }

    function renderAttachmentGroups(data) {
        var $container = $('#attachment-groups');
        $container.empty();

        if (data.bug_id) {
            $('#detail-title').html(
                '<span class="fa fa-bug" style="color: #cc0000;"></span> ' +
                'Artifacts for <a href="/bugs/' + data.bug_id + '/" target="_blank">BUG-' + data.bug_id + '</a>: ' +
                escapeHtml(data.bug_summary)
            );
        } else {
            $('#detail-title').html(
                '<span class="fa fa-paperclip"></span> ' +
                'Artifacts for TR-' + data.run_id + ': ' + escapeHtml(data.run_summary)
            );
        }

        var summaryHtml = '<strong>' + data.total_attachments + '</strong> artifact(s), ' +
            '<strong>' + data.total_size_display + '</strong> total';
        $('#attachment-summary').html(summaryHtml);

        // Show linked plans, runs, and cases for bug views
        var $linkedPanel = $('#bug-linked-panel');
        $linkedPanel.hide().empty();

        if (data.bug_id) {
            var hasPlans = data.linked_plans && data.linked_plans.length > 0;
            var hasRuns = data.linked_runs && data.linked_runs.length > 0;
            var hasCases = data.linked_cases && data.linked_cases.length > 0;

            if (hasPlans || hasRuns || hasCases) {
                var innerHtml = '<div class="panel-heading">' +
                    '<span class="fa fa-link"></span> Linked Test Entities</div>';
                innerHtml += '<div class="panel-body">';

                if (hasPlans) {
                    innerHtml += '<div class="bug-linked-row">';
                    innerHtml += '<span class="bug-linked-label">Test Plans:</span> ';
                    for (var p = 0; p < data.linked_plans.length; p++) {
                        var lp = data.linked_plans[p];
                        if (p > 0) innerHtml += ', ';
                        innerHtml += '<a href="/plan/' + lp.plan_id + '/" target="_blank">' +
                            '<span class="pficon pficon-folder-open" style="color: #f0ab00;"></span> TP-' +
                            lp.plan_id + ': ' + escapeHtml(lp.plan_name) + '</a>';
                    }
                    innerHtml += '</div>';
                }

                if (hasRuns) {
                    innerHtml += '<div class="bug-linked-row">';
                    innerHtml += '<span class="bug-linked-label">Test Runs:</span> ';
                    for (var r = 0; r < data.linked_runs.length; r++) {
                        var lr = data.linked_runs[r];
                        if (r > 0) innerHtml += ', ';
                        innerHtml += '<a href="/runs/' + lr.run_id + '/" target="_blank">' +
                            '<span class="pficon pficon-running" style="color: #3f9c35;"></span> TR-' +
                            lr.run_id + ': ' + escapeHtml(lr.run_summary) + '</a>';
                    }
                    innerHtml += '</div>';
                }

                if (hasCases) {
                    innerHtml += '<div class="bug-linked-row">';
                    innerHtml += '<span class="bug-linked-label">Test Cases:</span> ';
                    for (var c = 0; c < data.linked_cases.length; c++) {
                        var lc = data.linked_cases[c];
                        if (c > 0) innerHtml += ', ';
                        innerHtml += '<a href="/case/' + lc.case_id + '/" target="_blank">' +
                            '<span class="pficon pficon-catalog" style="color: #0088ce;"></span> TC-' +
                            lc.case_id + ': ' + escapeHtml(lc.case_summary) + '</a>';
                    }
                    innerHtml += '</div>';
                }

                innerHtml += '</div>';
                $linkedPanel.html(innerHtml).show();
            } else {
                // Bug has no linked executions
                $linkedPanel.html(
                    '<div class="panel-heading">' +
                    '<span class="fa fa-link"></span> Linked Test Entities</div>' +
                    '<div class="panel-body text-muted">' +
                    'This bug is not linked to any test executions.</div>'
                ).show();
            }
        }

        if (data.groups.length === 0) {
            $container.hide();
            $('#attachment-empty').show();
            return;
        }

        $('#attachment-empty').hide();

        for (var i = 0; i < data.groups.length; i++) {
            var group = data.groups[i];
            var groupHtml = buildGroupPanel(group, i);
            $container.append(groupHtml);
        }

        $container.show();

        // Bind group toggle
        $container.on('click', '.group-heading', function(e) {
            // Don't toggle panel when clicking links inside heading
            if ($(e.target).closest('a').length) return;
            var $panel = $(this).closest('.attachment-group');
            var $body = $panel.find('.panel-body');
            var $toggle = $(this).find('.group-toggle');
            $body.slideToggle(200);
            $toggle.toggleClass('fa-angle-down fa-angle-right');
        });

        // Bind individual checkboxes
        $container.on('change', '.att-checkbox-input', function() {
            var attId = $(this).data('id');
            if ($(this).is(':checked')) {
                selectedAttachments[attId] = true;
            } else {
                delete selectedAttachments[attId];
            }
            updateZipButton();
            updateGroupSelectAll($(this).closest('.attachment-group'));
        });

        // Bind group select all
        $container.on('change', '.group-select-all-input', function() {
            var $group = $(this).closest('.attachment-group');
            var checked = $(this).is(':checked');
            $group.find('.att-checkbox-input').each(function() {
                $(this).prop('checked', checked);
                var attId = $(this).data('id');
                if (checked) {
                    selectedAttachments[attId] = true;
                } else {
                    delete selectedAttachments[attId];
                }
            });
            updateZipButton();
        });

        // Bind preview clicks
        $container.on('click', '.btn-preview', function(e) {
            e.preventDefault();
            var attId = $(this).data('id');
            openPreview(attId);
        });
    }

    function buildGroupPanel(group, index) {
        var title, icon;
        if (group.source === 'run') {
            title = group.source_label;
            icon = '<span class="pficon pficon-running" style="color: #3f9c35;"></span>';
        } else if (group.source === 'bug') {
            title = '<a href="/bugs/' + group.bug_id + '/" target="_blank">BUG-' + group.bug_id + '</a>: ' + escapeHtml(group.bug_summary);
            icon = '<span class="pficon pficon-warning-triangle-o" style="color: #cc0000;"></span>';
        } else {
            title = 'TC-' + group.case_id + ': ' + escapeHtml(group.case_summary);
            icon = '<span class="pficon pficon-catalog" style="color: #0088ce;"></span>';
        }

        var statusBadge = '';
        if (group.status) {
            statusBadge = '<span class="status-badge" style="background-color: ' +
                escapeHtml(group.status_color) + ';">' +
                escapeHtml(group.status) + '</span>';
        }

        var html = '<div class="attachment-group panel panel-default" data-group-index="' + index + '">';
        html += '<div class="panel-heading group-heading">';
        html += '<span class="fa fa-angle-down group-toggle"></span>';
        html += icon + ' ';
        html += '<span class="group-title">' + title + '</span>';
        html += statusBadge;
        html += '<span class="badge group-count">' + group.attachments.length + '</span>';
        html += '<label class="group-select-all pull-right">';
        html += '<input type="checkbox" class="group-select-all-input"> Select All';
        html += '</label>';
        html += '</div>';

        html += '<div class="panel-body" style="padding: 0;">';
        html += '<table class="table table-striped table-condensed attachment-table">';
        html += '<thead><tr>';
        html += '<th class="att-checkbox"></th>';
        html += '<th>Filename</th>';
        html += '<th>Type</th>';
        html += '<th>Size</th>';
        html += '<th>Uploader</th>';
        html += '<th>Date</th>';
        html += '<th>Source</th>';
        html += '<th>Actions</th>';
        html += '</tr></thead><tbody>';

        for (var j = 0; j < group.attachments.length; j++) {
            var att = group.attachments[j];
            html += '<tr>';
            html += '<td class="att-checkbox"><input type="checkbox" class="att-checkbox-input" data-id="' + att.id + '"></td>';
            html += '<td class="att-filename">';
            html += '<span class="att-icon ' + escapeHtml(att.file_icon) + '"></span>';
            html += '<a class="btn-preview" data-id="' + att.id + '" title="Preview">' + escapeHtml(att.filename) + '</a>';
            html += '</td>';
            html += '<td><span class="label label-default">' + escapeHtml(att.file_type || 'n/a') + '</span></td>';
            html += '<td>' + escapeHtml(att.size_display) + '</td>';
            html += '<td>' + escapeHtml(att.creator) + '</td>';
            html += '<td>' + escapeHtml(att.created) + '</td>';
            if (att.source_type === 'bug' && att.bug_id) {
                html += '<td class="att-source"><a href="/bugs/' + att.bug_id + '/" target="_blank" title="' + escapeHtml(att.bug_summary) + '">BUG-' + att.bug_id + '</a></td>';
            } else {
                html += '<td class="att-source">' + escapeHtml(att.source_type) + '</td>';
            }
            html += '<td class="att-actions">';
            html += '<a href="' + escapeHtml(att.url) + '" class="btn btn-default btn-xs" download title="Download">';
            html += '<span class="fa fa-download"></span></a> ';
            html += '<button class="btn btn-default btn-xs btn-preview" data-id="' + att.id + '" title="Preview">';
            html += '<span class="fa fa-eye"></span></button>';
            html += '</td>';
            html += '</tr>';
        }

        html += '</tbody></table></div></div>';
        return html;
    }

    // ==========================================
    // Preview Modal
    // ==========================================

    function openPreview(attId) {
        var $modal = $('#preview-modal');

        // Reset all preview sections
        $('#preview-loading').show();
        $('#preview-image, #preview-text, #preview-pdf, #preview-video, #preview-audio, #preview-document, #preview-none').hide();
        $('#preview-title').text('File Preview');

        $modal.modal('show');

        $.ajax({
            url: '/tcms_artifact_browser/api/attachment/' + attId + '/preview/',
            dataType: 'json',
            success: function(data) {
                $('#preview-loading').hide();
                $('#preview-title').text(data.filename);

                // Fill info
                $('#preview-info-filename').text(data.filename);
                $('#preview-info-type').text(data.file_type + ' (' + data.file_category + ')');
                $('#preview-info-size').text(data.size_display);
                $('#preview-info-creator').text(data.creator);
                $('#preview-info-created').text(data.created);
                $('#preview-download-btn').attr('href', data.url);

                // Show appropriate preview
                switch (data.file_category) {
                    case 'image':
                        $('#preview-image-el').attr('src', data.url);
                        $('#preview-image').show();
                        break;
                    case 'text':
                        $('#preview-text-el').text(data.content || '(No content available)');
                        $('#preview-text').show();
                        break;
                    case 'pdf':
                        $('#preview-pdf-el').attr('src', data.serve_url || data.url);
                        $('#preview-pdf').show();
                        break;
                    case 'markdown':
                        if (data.content) {
                            $('#preview-document-el').html(data.content);
                            $('#preview-document').show();
                        } else {
                            $('#preview-none').show();
                        }
                        break;
                    case 'document':
                        if (data.content) {
                            $('#preview-document-el').html(data.content);
                            $('#preview-document').show();
                        } else {
                            $('#preview-none').show();
                        }
                        break;
                    case 'video':
                        var $video = $('#preview-video-el');
                        $video.find('source').attr('src', data.url);
                        $video[0].load();
                        $('#preview-video').show();
                        break;
                    case 'audio':
                        var $audio = $('#preview-audio-el');
                        $audio.find('source').attr('src', data.url);
                        $audio[0].load();
                        $('#preview-audio').show();
                        break;
                    default:
                        $('#preview-none').show();
                        break;
                }
            },
            error: function() {
                $('#preview-loading').hide();
                $('#preview-none').show();
            }
        });
    }

    // Clean up media on modal close
    $(document).on('hidden.bs.modal', '#preview-modal', function() {
        var video = $('#preview-video-el')[0];
        var audio = $('#preview-audio-el')[0];
        if (video) video.pause();
        if (audio) audio.pause();
        $('#preview-image-el').attr('src', '');
        $('#preview-pdf-el').attr('src', '');
    });

    // ==========================================
    // Checkbox Selection & ZIP Download
    // ==========================================

    function initSelectAll() {
        $('#select-all-attachments').on('change', function() {
            var checked = $(this).is(':checked');
            $('.att-checkbox-input').each(function() {
                $(this).prop('checked', checked);
                var attId = $(this).data('id');
                if (checked) {
                    selectedAttachments[attId] = true;
                } else {
                    delete selectedAttachments[attId];
                }
            });
            $('.group-select-all-input').prop('checked', checked);
            updateZipButton();
        });
    }

    function updateGroupSelectAll($group) {
        var total = $group.find('.att-checkbox-input').length;
        var checked = $group.find('.att-checkbox-input:checked').length;
        $group.find('.group-select-all-input').prop('checked', total === checked && total > 0);
    }

    function updateZipButton() {
        var count = Object.keys(selectedAttachments).length;
        var $btn = $('#btn-download-zip');
        var $badge = $('#zip-count');
        if (count > 0) {
            $btn.prop('disabled', false);
            $badge.text(count).show();
        } else {
            $btn.prop('disabled', true);
            $badge.hide();
        }
    }

    function initZipDownload() {
        $('#btn-download-zip').on('click', function() {
            var ids = Object.keys(selectedAttachments).map(Number);
            if (ids.length === 0) return;

            var csrfToken = $('input[name="csrfmiddlewaretoken"]').val();
            var payload = JSON.stringify({
                attachment_ids: ids,
                run_id: currentRunId
            });

            // Use XMLHttpRequest to trigger browser download
            var xhr = new XMLHttpRequest();
            xhr.open('POST', '/tcms_artifact_browser/api/download/zip/', true);
            xhr.responseType = 'blob';
            xhr.setRequestHeader('Content-Type', 'application/json');
            xhr.setRequestHeader('X-CSRFToken', csrfToken);

            xhr.onload = function() {
                if (xhr.status === 200) {
                    var blob = new Blob([xhr.response], {type: 'application/zip'});
                    var url = window.URL.createObjectURL(blob);
                    var a = document.createElement('a');
                    a.href = url;
                    // Extract filename from Content-Disposition header
                    var disposition = xhr.getResponseHeader('Content-Disposition');
                    var filename = 'artifacts.zip';
                    if (disposition) {
                        var match = disposition.match(/filename="(.+)"/);
                        if (match) filename = match[1];
                    }
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                    window.URL.revokeObjectURL(url);
                    document.body.removeChild(a);
                } else {
                    alert('Failed to download ZIP file');
                }
            };

            xhr.send(payload);
        });
    }

    // ==========================================
    // Toolbar Filters
    // ==========================================

    function reloadCurrentAttachments() {
        if (currentRunId) {
            loadRunAttachments(currentRunId);
        } else if (currentBugId) {
            loadBugAttachments(currentBugId);
        }
    }

    function initToolbarFilters() {
        $('#filter-file-type').on('change', function() {
            reloadCurrentAttachments();
        });

        $('#btn-filter-filename').on('click', function() {
            reloadCurrentAttachments();
        });

        $('#filter-filename').on('keypress', function(e) {
            if (e.which === 13) {
                reloadCurrentAttachments();
            }
        });
    }

    // ==========================================
    // Global Search
    // ==========================================

    function initGlobalSearch() {
        $('#btn-global-search').on('click', function() {
            performGlobalSearch();
        });

        $('#tree-search').on('keypress', function(e) {
            if (e.which === 13) {
                performGlobalSearch();
            }
        });
    }

    function performGlobalSearch() {
        var query = $('#tree-search').val().trim();
        if (query.length < 2) return;

        // Hide other panels, show search results
        $('#detail-placeholder').hide();
        $('#attachment-toolbar').hide();
        $('#attachment-groups').hide();
        $('#attachment-empty').hide();
        $('#search-results').show();

        var $tbody = $('#search-results-body');
        $tbody.html('<tr><td colspan="7" class="text-center"><span class="spinner spinner-lg"></span> Searching...</td></tr>');

        $.ajax({
            url: '/tcms_artifact_browser/api/search/',
            data: { q: query },
            dataType: 'json',
            success: function(data) {
                $tbody.empty();
                if (data.results.length === 0) {
                    $tbody.html('<tr><td colspan="7" class="text-center text-muted">No results found for "' + escapeHtml(query) + '"</td></tr>');
                    return;
                }

                for (var i = 0; i < data.results.length; i++) {
                    var r = data.results[i];
                    var row = '<tr>';
                    row += '<td><span class="' + escapeHtml(r.file_icon) + '"></span> ';
                    row += '<a class="btn-preview" data-id="' + r.id + '" style="cursor:pointer;">' + escapeHtml(r.filename) + '</a></td>';
                    row += '<td><span class="label label-default">' + escapeHtml(r.file_type || 'n/a') + '</span></td>';
                    row += '<td>' + escapeHtml(r.size_display) + '</td>';
                    row += '<td>' + escapeHtml(r.context) + '</td>';
                    row += '<td>' + escapeHtml(r.creator) + '</td>';
                    row += '<td>' + escapeHtml(r.created) + '</td>';
                    row += '<td><a href="' + escapeHtml(r.url) + '" class="btn btn-default btn-xs" download>';
                    row += '<span class="fa fa-download"></span></a> ';
                    row += '<button class="btn btn-default btn-xs btn-preview" data-id="' + r.id + '">';
                    row += '<span class="fa fa-eye"></span></button></td>';
                    row += '</tr>';
                    $tbody.append(row);
                }

                // Bind preview clicks in search results
                $tbody.on('click', '.btn-preview', function(e) {
                    e.preventDefault();
                    openPreview($(this).data('id'));
                });
            },
            error: function() {
                $tbody.html('<tr><td colspan="7" class="text-center text-danger">Search failed</td></tr>');
            }
        });
    }

    // ==========================================
    // Statistics & Charts
    // ==========================================

    function initStatsPanel() {
        $('#btn-toggle-stats').on('click', function() {
            var $body = $('#stats-body');
            var $icon = $(this).find('.fa');
            $body.slideToggle(200);
            $icon.toggleClass('fa-chevron-up fa-chevron-down');
        });
    }

    function loadStatistics() {
        var productId = $('#filter-product').val();
        var params = {};
        if (productId) params.product = productId;

        $.ajax({
            url: '/tcms_artifact_browser/api/statistics/',
            data: params,
            dataType: 'json',
            success: function(data) {
                $('#stat-total-count').text(data.total);
                $('#stat-total-size').text(data.total_size_display);

                // File type pie chart
                if (data.by_file_type.length > 0) {
                    renderPieChart('#chart-file-type', data.by_file_type, 'By File Type');
                } else {
                    $('#chart-file-type').html('<div class="text-muted text-center" style="padding-top: 60px;">No data</div>');
                }

                // Source pie chart
                if (data.by_source.length > 0) {
                    renderPieChart('#chart-source', data.by_source, 'By Source');
                } else {
                    $('#chart-source').html('<div class="text-muted text-center" style="padding-top: 60px;">No data</div>');
                }
            }
        });
    }

    function renderPieChart(selector, data, title) {
        var columns = [];
        var colors = {};
        for (var i = 0; i < data.length; i++) {
            columns.push([data[i].name, data[i].count]);
            colors[data[i].name] = CHART_COLORS[i % CHART_COLORS.length];
        }

        c3.generate({
            bindto: selector,
            data: {
                columns: columns,
                type: 'donut',
                colors: colors
            },
            donut: {
                title: title,
                label: {
                    format: function(value) { return value; }
                }
            },
            legend: {
                position: 'right'
            },
            size: {
                height: 180
            },
            tooltip: {
                format: {
                    value: function(value, ratio) {
                        return value + ' (' + (ratio * 100).toFixed(1) + '%)';
                    }
                }
            }
        });
    }

    // ==========================================
    // Export Buttons
    // ==========================================

    function initExportButtons() {
        $(document).on('click', '.btn-export', function(e) {
            e.preventDefault();
            var format = $(this).data('format');
            var productId = $('#filter-product').val();
            var params = '';
            if (productId) params = '?product=' + productId;
            if (currentRunId) {
                params = params ? params + '&run_id=' + currentRunId : '?run_id=' + currentRunId;
            }

            var urls = {
                csv: '/tcms_artifact_browser/api/report/' + params,
                excel: '/tcms_artifact_browser/api/report/excel/' + params,
                docx: '/tcms_artifact_browser/api/report/docx/' + params,
                pdf: '/tcms_artifact_browser/api/report/pdf/' + params
            };

            if (urls[format]) {
                window.location.href = urls[format];
            }
        });
    }

    // ==========================================
    // Utilities
    // ==========================================

    function escapeHtml(str) {
        if (!str) return '';
        return String(str)
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#039;');
    }

})($);
