<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    {% if domain %}<meta name="fs-domain" content="{{ domain }}">{% endif %}
    <title>Security Progress Report - Finite State Report</title>
    <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2"></script>

    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
            line-height: 1.6; color: #333; background-color: #f8f9fa;
        }
        .container { margin: 0 auto; padding: 20px; background-color: white; border: 1px solid #dee2e6; min-height: 100%; }
        .header { text-align: center; margin-bottom: 30px; padding-bottom: 20px; border-bottom: 2px solid #e9ecef; }
        .company-logo { max-height: 50px; width: auto; margin: 0 auto; display: block; }
        .header h1 { color: #2c3e50; font-size: 2.5em; margin-bottom: 10px; }
        .header .subtitle { color: #6c757d; font-size: 1.1em; }
        .metadata { background-color: #f8f9fa; padding: 15px; border-radius: 8px; margin-bottom: 30px; font-size: 0.9em; color: #6c757d; }

        /* KPI Cards */
        .kpi-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 15px; margin-bottom: 30px; }
        .kpi-card {
            background: white; border: 1px solid #dee2e6; border-radius: 8px;
            padding: 20px; text-align: center;
        }
        .kpi-card .kpi-title { font-size: 0.85em; color: #6c757d; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 8px; }
        .kpi-card .kpi-value { font-size: 2.2em; font-weight: 700; color: #2c3e50; line-height: 1; }
        .kpi-card.border-green  { border-top: 4px solid #388e3c; }
        .kpi-card.border-orange { border-top: 4px solid #f57c00; }
        .kpi-card.border-blue   { border-top: 4px solid #1976d2; }
        .kpi-card.border-red    { border-top: 4px solid #dc2626; }

        /* Chart containers */
        .chart-container { background-color: white; border-radius: 8px; padding: 20px; margin-bottom: 30px; border: 1px solid #dee2e6; }
        .chart-wrapper { position: relative; height: 400px; margin: 20px 0; }
        .chart-title { color: #2c3e50; font-size: 1.5em; margin-bottom: 10px; }
        .chart-description { color: #6c757d; font-size: 0.95em; margin-bottom: 20px; }

        /* Tables */
        .table-container { background-color: white; border-radius: 8px; padding: 20px; margin-bottom: 30px; border: 1px solid #dee2e6; overflow-x: auto; }
        .table-container h2 { color: #2c3e50; font-size: 1.5em; margin-bottom: 10px; }
        .table-container p.description { color: #6c757d; font-size: 0.95em; margin-bottom: 15px; }
        table { width: 100%; border-collapse: collapse; margin-top: 15px; }
        th, td { padding: 10px 12px; text-align: left; border-bottom: 1px solid #dee2e6; }
        th { background-color: #f8f9fa; font-weight: 600; color: #495057; position: sticky; top: 0; z-index: 10; }
        tr:hover { background-color: #f8f9fa; }
        td.num { text-align: right; font-variant-numeric: tabular-nums; }
        th.num { text-align: right; }

        /* Net change coloring */
        .net-improved { color: #388e3c; font-weight: 600; }
        .net-regressed { color: #dc2626; font-weight: 600; }
        .net-neutral { color: #6c757d; font-weight: 600; }

        /* Severity badges */
        .sev-critical { color: #dc2626; font-weight: 600; }
        .sev-high { color: #ea580c; font-weight: 600; }
        .sev-medium { color: #eab308; font-weight: 600; }
        .sev-low { color: #2563eb; font-weight: 600; }

        /* Details / collapsible sections */
        details { margin-bottom: 12px; }
        details summary {
            cursor: pointer; font-weight: 600; color: #2c3e50; font-size: 1.05em;
            padding: 10px 0; border-bottom: 1px solid #e9ecef;
        }
        details summary:hover { color: #007bff; }
        details[open] summary { margin-bottom: 10px; }
        .detail-table { font-size: 0.9em; }
        .detail-table th { font-size: 0.85em; }

        /* Flat snapshot notice */
        .flat-notice {
            background: #fff3cd; border: 1px solid #ffc107; border-radius: 8px;
            padding: 20px; margin-bottom: 30px; color: #856404;
        }
        .flat-notice strong { display: block; margin-bottom: 6px; }

        .footer { text-align: center; margin-top: 40px; padding-top: 20px; border-top: 1px solid #e9ecef; color: #6c757d; font-size: 0.9em; }
        .finite-state-logo { color: #007bff; font-weight: bold; }

        /* Sortable table header indicator */
        th.sortable { cursor: pointer; user-select: none; }
        th.sortable:hover { background-color: #e9ecef; }
        th.sortable::after { content: ' \2195'; font-size: 0.7em; color: #adb5bd; }

        @media print {
            body { background-color: white; }
            .container { max-width: none; border: none; }
            details { open: true; }
            details[open] summary { page-break-after: avoid; }
            .chart-container { break-inside: avoid; }
            .table-container { break-inside: avoid; }
        }
    </style>
</head>
<body>
    <span id="fsConnectionBadge"></span>
    <div class="container">
        <!-- Header -->
        <div class="header">
            {% if logo_path %}
            <div class="logo-container">
                <img src="{{ logo_path }}" alt="Company Logo" class="company-logo">
            </div>
            {% else %}
            <div class="logo-container">
                <img src="data:image/webp;base64,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"
                     alt="Finite State Logo" class="company-logo">
            </div>
            {% endif %}
            <h1>Security Progress Report</h1>
            <div class="subtitle">
                {{ scope_label | default('All Projects') }} — Security Posture Over Time
            </div>
        </div>

        <!-- Metadata -->
        <div class="metadata">
            <strong>Report:</strong> Security Progress |
            <strong>Generated:</strong> {{ generated_at }} |
            <strong>Scope:</strong> {{ scope_label | default('All Projects') }}
            {% if summary is defined and summary.period_start %} | <strong>Period:</strong> {{ summary.period_start }} to {{ summary.period_end }}{% endif %}
            {% if summary is defined and summary.mode == 'version_progression' %} | <strong>Mode:</strong> Version Progression{% endif %}
        </div>

        {% if summary is defined and summary.mode == 'flat_snapshot' %}
        <!-- Flat snapshot fallback notice -->
        <div class="flat-notice">
            <strong>Version progression data is not available.</strong>
            This report requires multiple firmware versions per project to show progress over time.
            The data below reflects a point-in-time snapshot rather than version-to-version trends.
        </div>
        {% endif %}

        <!-- Portfolio KPI Cards -->
        {% if portfolio_kpi %}
        <div class="kpi-row">
            <div class="kpi-card border-green">
                <div class="kpi-title">Total Resolved</div>
                <div class="kpi-value" style="color: #388e3c;">{{ portfolio_kpi.total_resolved | default(0) }}</div>
            </div>
            <div class="kpi-card border-orange">
                <div class="kpi-title">Total New</div>
                <div class="kpi-value" style="color: #f57c00;">{{ portfolio_kpi.total_new | default(0) }}</div>
            </div>
            <div class="kpi-card {% if portfolio_kpi.net_change | default(0) <= 0 %}border-green{% else %}border-red{% endif %}">
                <div class="kpi-title">Net Change</div>
                <div class="kpi-value {% if portfolio_kpi.net_change | default(0) < 0 %}net-improved{% elif portfolio_kpi.net_change | default(0) > 0 %}net-regressed{% else %}net-neutral{% endif %}">
                    {% if portfolio_kpi.net_change | default(0) > 0 %}+{% endif %}{{ portfolio_kpi.net_change | default(0) }}
                </div>
            </div>
            <div class="kpi-card border-blue">
                <div class="kpi-title">Projects Improved</div>
                <div class="kpi-value" style="color: #1976d2;">{{ portfolio_kpi.projects_improved | default(0) }} / {{ portfolio_kpi.total_projects | default(0) }}</div>
            </div>
        </div>
        {% endif %}

        <!-- Open CVE Trend by Severity -->
        {% if portfolio_progression and portfolio_progression | length > 1 %}
        <div class="chart-container">
            <h2 class="chart-title">Open CVE Trend</h2>
            <p class="chart-description">Total open CVEs over time, broken down by severity.</p>
            <div class="chart-wrapper">
                <canvas id="severityTrendChart"></canvas>
            </div>
        </div>
        {% endif %}

        <!-- Per-Project Progress Table -->
        {% if projects and projects | length > 0 %}
        <div class="table-container">
            <h2>Project Progress</h2>
            <p class="description">Per-project vulnerability change between baseline and current versions. Sorted by most improved.</p>
            <table id="projectTable">
                <thead>
                    <tr>
                        <th class="sortable" data-col="0">Project</th>
                        <th class="sortable num" data-col="1">Baseline Open</th>
                        <th class="sortable num" data-col="2">Current Open</th>
                        <th class="sortable num" data-col="3">Resolved</th>
                        <th class="sortable num" data-col="4">New</th>
                        <th class="sortable num" data-col="5">Net Change</th>
                    </tr>
                </thead>
                <tbody>
                    {% set ns = namespace(hidden=0) %}
                    {% for p in projects | sort(attribute='net_change') %}
                    {% if p.net_change | default(0) != 0 or p.baseline_open | default(0) > 0 or p.current_open | default(0) > 0 or p.total_resolved | default(0) > 0 or p.total_new | default(0) > 0 %}
                    <tr>
                        <td>{{ p.project_name }}</td>
                        <td class="num">{{ p.baseline_open | default(0) }}</td>
                        <td class="num">{{ p.current_open | default(0) }}</td>
                        <td class="num" style="color: #388e3c;">{{ p.total_resolved | default(0) }}</td>
                        <td class="num" style="color: #f57c00;">{{ p.total_new | default(0) }}</td>
                        <td class="num {% if p.net_change | default(0) < 0 %}net-improved{% elif p.net_change | default(0) > 0 %}net-regressed{% else %}net-neutral{% endif %}">
                            {% if p.net_change | default(0) > 0 %}+{% endif %}{{ p.net_change | default(0) }}
                        </td>
                    </tr>
                    {% else %}
                    {% set ns.hidden = ns.hidden + 1 %}
                    {% endif %}
                    {% endfor %}
                    {% if ns.hidden > 0 %}
                    <tr style="color: #6c757d; font-style: italic;">
                        <td colspan="6">{{ ns.hidden }} project{{ 's' if ns.hidden != 1 else '' }} with no findings or changes not shown</td>
                    </tr>
                    {% endif %}
                </tbody>
            </table>
        </div>
        {% endif %}

        <!-- CVE Updates Summary Table -->
        {% if cve_update_summary and cve_update_summary.total | default(0) > 0 %}
        <div class="table-container">
            <h2>CVE Updates Summary</h2>
            <p class="description">CVE database changes detected during the reporting window that affect open findings.</p>
            <table>
                <thead>
                    <tr>
                        <th>Category</th>
                        <th class="num">Count</th>
                    </tr>
                </thead>
                <tbody>
                    {% if cve_update_summary.added | default(0) > 0 %}
                    <tr><td>Newly Added CVEs</td><td class="num"><strong>{{ cve_update_summary.added }}</strong></td></tr>
                    {% endif %}
                    {% if cve_update_summary.retracted | default(0) > 0 %}
                    <tr><td>Retracted CVEs</td><td class="num"><strong>{{ cve_update_summary.retracted }}</strong></td></tr>
                    {% endif %}
                    {% if cve_update_summary.severity_escalated | default(0) > 0 %}
                    <tr><td>Severity Escalated</td><td class="num"><strong>{{ cve_update_summary.severity_escalated }}</strong></td></tr>
                    {% endif %}
                    {% if cve_update_summary.severity_downgraded | default(0) > 0 %}
                    <tr><td>Severity Downgraded</td><td class="num"><strong>{{ cve_update_summary.severity_downgraded }}</strong></td></tr>
                    {% endif %}
                    {% if cve_update_summary.exploit_gained | default(0) > 0 %}
                    <tr><td>Exploit Gained</td><td class="num"><strong>{{ cve_update_summary.exploit_gained }}</strong></td></tr>
                    {% endif %}
                    {% if cve_update_summary.exploit_subsided | default(0) > 0 %}
                    <tr><td>Exploit Subsided</td><td class="num"><strong>{{ cve_update_summary.exploit_subsided }}</strong></td></tr>
                    {% endif %}
                    <tr style="border-top: 2px solid #dee2e6; font-weight: 600;">
                        <td>Total</td>
                        <td class="num"><strong>{{ cve_update_summary.total }}</strong></td>
                    </tr>
                </tbody>
            </table>
        </div>
        {% endif %}

        <!-- Per-Project Detail (collapsible) -->
        {% if projects and projects | length > 0 and summary is defined and summary.mode == 'version_progression' %}
        <div class="table-container">
            <h2>Version-by-Version Detail</h2>
            <p class="description">Expand a project to see how vulnerability counts changed across firmware versions.</p>

            {% for p in projects | sort(attribute='net_change') %}
            {% if p.progression and p.progression | length > 0 and (p.net_change | default(0) != 0 or p.baseline_open | default(0) > 0 or p.current_open | default(0) > 0 or p.total_resolved | default(0) > 0 or p.total_new | default(0) > 0) %}
            <details>
                <summary>
                    {{ p.project_name }}
                    <span style="font-weight: 400; font-size: 0.85em; color: #6c757d; margin-left: 8px;">
                        ({{ p.progression | length }} version{{ 's' if p.progression | length != 1 else '' }},
                        net
                        <span class="{% if p.net_change | default(0) < 0 %}net-improved{% elif p.net_change | default(0) > 0 %}net-regressed{% else %}net-neutral{% endif %}">
                            {% if p.net_change | default(0) > 0 %}+{% endif %}{{ p.net_change | default(0) }}
                        </span>)
                    </span>
                </summary>
                <table class="detail-table">
                    <thead>
                        <tr>
                            <th>Version</th>
                            <th>Date</th>
                            <th class="num">Open</th>
                            <th class="num">Resolved</th>
                            <th class="num">New</th>
                            <th class="num sev-critical">Critical</th>
                            <th class="num sev-high">High</th>
                            <th class="num sev-medium">Medium</th>
                            <th class="num sev-low">Low</th>
                        </tr>
                    </thead>
                    <tbody>
                        {% for v in p.progression %}
                        <tr>
                            <td><strong>{{ v.version | default('--') }}</strong></td>
                            <td>{{ v.date_display | default(v.date) | default('--') }}</td>
                            <td class="num">{{ v.open_count | default(0) }}</td>
                            <td class="num" style="color: #388e3c;">{{ v.resolved | default(0) }}</td>
                            <td class="num" style="color: #f57c00;">{{ v.new | default(0) }}</td>
                            <td class="num sev-critical">{{ v.critical | default(0) }}</td>
                            <td class="num sev-high">{{ v.high | default(0) }}</td>
                            <td class="num sev-medium">{{ v.medium | default(0) }}</td>
                            <td class="num sev-low">{{ v.low | default(0) }}</td>
                        </tr>
                        {% endfor %}
                    </tbody>
                </table>
            </details>
            {% endif %}
            {% endfor %}
        </div>
        {% endif %}

        <div class="footer">
            <p>Generated by <span class="finite-state-logo">Finite State</span> | {{ generated_at }}</p>
            <p style="font-size: 0.8em; margin-top: 4px;">Copyright &copy; 2026 Finite State, Inc.</p>
        </div>
    </div>

    <script>
    function copyPrompt(btn) {
        var pre = btn.parentElement.querySelector('pre');
        navigator.clipboard.writeText(pre.textContent).then(function() {
            btn.textContent = 'Copied!';
            setTimeout(function() { btn.textContent = 'Copy'; }, 2000);
        });
    }

    document.addEventListener('DOMContentLoaded', function() {

        /* ── Open CVE Trend by Severity ── */
        {% if portfolio_progression and portfolio_progression | length > 1 %}
        (function() {
            var ctx = document.getElementById('severityTrendChart');
            if (!ctx) return;

            var progression = {{ portfolio_progression | tojson }};
            var labels = progression.map(function(d) { return d.date_display || d.date; });

            var sevDefs = [
                { label: 'Critical', key: 'critical', color: '#dc2626' },
                { label: 'High',     key: 'high',     color: '#ea580c' },
                { label: 'Medium',   key: 'medium',   color: '#eab308' },
                { label: 'Low',      key: 'low',      color: '#2563eb' },
                { label: 'Unknown',  key: 'unknown',  color: '#9ca3af' }
            ];

            var datasets = sevDefs.filter(function(ds) {
                return progression.some(function(d) { return (d[ds.key] || 0) > 0; });
            }).map(function(ds) {
                return {
                    label: ds.label,
                    data: progression.map(function(d) { return d[ds.key] || 0; }),
                    borderColor: ds.color,
                    backgroundColor: ds.color + '33',
                    fill: true,
                    stack: 'severity',
                    tension: 0.3,
                    pointRadius: 3,
                    borderWidth: 2
                };
            });

            datasets.push({
                label: 'Total',
                data: progression.map(function(d) { return d.open_count; }),
                borderColor: '#333',
                backgroundColor: 'transparent',
                fill: false,
                stack: 'total',
                tension: 0.3,
                pointRadius: 4,
                pointBackgroundColor: '#333',
                borderWidth: 2,
                borderDash: [6, 3]
            });

            new Chart(ctx.getContext('2d'), {
                type: 'line',
                data: { labels: labels, datasets: datasets },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    plugins: {
                        legend: { position: 'top' },
                        datalabels: {
                            display: function(context) {
                                if (context.dataset.label !== 'Total') return false;
                                var last = context.dataset.data.length - 1;
                                return context.dataIndex === 0 || context.dataIndex === last;
                            },
                            anchor: 'end',
                            align: 'top',
                            color: '#333',
                            font: { weight: 'bold', size: 11 },
                            formatter: function(value) { return value.toLocaleString(); }
                        }
                    },
                    scales: {
                        x: {
                            title: { display: true, text: 'Version', color: '#6c757d' },
                            ticks: { maxRotation: 45, color: '#6c757d', autoSkip: true, maxTicksLimit: 20 }
                        },
                        y: {
                            stacked: true,
                            beginAtZero: true,
                            title: { display: true, text: 'Open CVEs', color: '#6c757d' },
                            ticks: { precision: 0, color: '#6c757d' }
                        }
                    }
                },
                plugins: [ChartDataLabels]
            });
        })();
        {% endif %}

        /* ── Sortable Table ── */
        (function() {
            var table = document.getElementById('projectTable');
            if (!table) return;
            var headers = table.querySelectorAll('th.sortable');
            var tbody = table.querySelector('tbody');

            headers.forEach(function(th) {
                th.addEventListener('click', function() {
                    var col = parseInt(th.getAttribute('data-col'));
                    var rows = Array.from(tbody.querySelectorAll('tr'));
                    var asc = th.getAttribute('data-sort') !== 'asc';
                    th.setAttribute('data-sort', asc ? 'asc' : 'desc');

                    rows.sort(function(a, b) {
                        var aVal = a.children[col].textContent.trim();
                        var bVal = b.children[col].textContent.trim();
                        var aNum = parseFloat(aVal.replace('+', ''));
                        var bNum = parseFloat(bVal.replace('+', ''));
                        if (!isNaN(aNum) && !isNaN(bNum)) {
                            return asc ? aNum - bNum : bNum - aNum;
                        }
                        return asc ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal);
                    });

                    rows.forEach(function(row) { tbody.appendChild(row); });
                });
            });
        })();
    });
    </script>

    {% include '_action_buttons.html' %}
</body>
</html>
