<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Component Vulnerability Analysis - 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-adapter-date-fns"></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;
        }
        
        .logo-container {
            margin-bottom: 20px;
        }
        
        .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;
        }
        
        .chart-container {
            background-color: white;
            border-radius: 8px;
            padding: 20px;
            margin-bottom: 30px;
            border: 1px solid #dee2e6;
        }
        
        .chart-wrapper {
            position: relative;
            height: 500px;
            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;
        }
        
        .narrative-section {
            background-color: white;
            border-radius: 8px;
            padding: 20px;
            margin-bottom: 30px;
            border: 1px solid #dee2e6;
        }
        
        .narrative-section h3 {
            color: #2c3e50;
            margin-bottom: 15px;
        }
        
        .narrative-section h4 {
            color: #495057;
            margin: 20px 0 10px 0;
        }
        
        .narrative-section ul {
            margin-left: 20px;
            margin-bottom: 15px;
        }
        
        .narrative-section li {
            margin-bottom: 8px;
        }
        
        .table-container {
            background-color: white;
            border-radius: 8px;
            padding: 20px;
            margin-bottom: 30px;
            border: 1px solid #dee2e6;
        }
        
        table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 15px;
            min-width: 600px;
            table-layout: fixed;
        }
        
        th, td {
            padding: 12px;
            text-align: left;
            border-bottom: 1px solid #dee2e6;
            word-wrap: break-word;
            overflow-wrap: break-word;
            max-width: 300px;
            min-width: 100px;
        }
        
        th {
            background-color: #f8f9fa;
            font-weight: 600;
            color: #495057;
            z-index: 10;
        }
        
        tr:hover {
            background-color: #f8f9fa;
        }
        
        .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;
        }
        
        .severity-critical { color: #dc3545; font-weight: bold; }
        .severity-high { color: #fd7e14; font-weight: bold; }
        .severity-medium { color: #ffc107; font-weight: bold; }
        .severity-low { color: #28a745; font-weight: bold; }
        
        .kev-marker { color: #dc3545; }
        .exploit-marker { color: #6f42c1; }
        
        .no-data {
            text-align: center;
            color: #6c757d;
            font-style: italic;
            padding: 40px;
        }
        
        .error-message {
            background-color: #f8d7da;
            color: #721c24;
            padding: 15px;
            border-radius: 8px;
            margin-bottom: 20px;
            border: 1px solid #f5c6cb;
        }
        
        @media print {
            body {
                background-color: white;
            }
            
            .container {
                max-width: none;
            }
            
            .chart-container, .narrative-section, .table-container {
                border: 1px solid #dee2e6;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <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>Component Vulnerability Analysis</h1>
            <div class="subtitle">Finite State Security Report</div>
        </div>
        
        <div class="metadata">
            <strong>Report:</strong> {{ recipe_name }} | 
            <strong>Generated:</strong> {{ generated_at }} | 
            {% if metadata and metadata.start_date and metadata.end_date %}
            <strong>Period:</strong> {{ metadata.start_date }} to {{ metadata.end_date }} | 
            {% endif %}
            <strong>Records:</strong> {{ metadata.transformed_count if metadata and metadata.transformed_count else 'N/A' }}
        </div>
        
        <!-- Charts Section -->
        {% if chart_data %}
            <!-- 1. Pareto Chart - Top Risk Components -->
            {% if chart_data.pareto_chart is mapping and chart_data.pareto_chart|length > 0 %}
            <div class="chart-container">
                <h2 class="chart-title">🔢 Top Risk Components - Pareto Analysis</h2>
                <p class="chart-description">Components sorted by composite risk score with cumulative risk percentage. Shows which components contribute disproportionately to total risk.</p>
                <div class="chart-wrapper">
                    <canvas id="paretoChart"></canvas>
                </div>
            </div>
            {% endif %}

            <!-- 2. Bubble Matrix - Scope vs Severity -->
            {% if chart_data.bubble_matrix is mapping and chart_data.bubble_matrix|length > 0 %}
            <div class="chart-container">
                <h2 class="chart-title">🎯 Component Risk vs Project Scope</h2>
                <p class="chart-description">Bubble chart showing risk score vs number of affected projects. Bubble size represents finding count.</p>
                <div class="chart-wrapper">
                    <canvas id="bubbleMatrixChart"></canvas>
                </div>
            </div>
            {% endif %}




        {% else %}
            <div class="no-data">
                <p>No chart data available for visualization.</p>
            </div>
        {% endif %}

        <div class="narrative-section">
            <h3>Executive Summary</h3>
            <p>The Component Vulnerability Analysis provides a strategic portfolio-level view of the most problematic components across your entire organization. This report identifies which component versions pose the highest risk and have the broadest impact, enabling portfolio-wide component risk prioritization.</p>
            
            <h4>Key Insights from Visualizations</h4>
            <ul>
                <li><strong>Pareto Analysis</strong>: Shows which components contribute disproportionately to total risk, with each version treated uniquely for precise risk assessment.</li>
                <li><strong>Risk vs. Scope</strong>: Identifies components with both high risk and broad project impact for maximum remediation efficiency.</li>
                <li><strong>Normalized Risk Scoring</strong>: Risk scores are normalized by project count to provide fair comparison across components used in different numbers of projects.</li>
            </ul>
            
            <h4>Strategic Recommendations</h4>
            <ul>
                <li><strong>High-Risk Components</strong>: Focus immediate remediation efforts on components with the highest normalized risk scores.</li>
                <li><strong>High-Impact Components</strong>: Prioritize components affecting multiple projects for maximum portfolio-wide risk reduction.</li>
                <li><strong>KEV and Exploit Flags</strong>: Components with 🔒 (KEV) or 💥 (exploit) markers require immediate attention due to federal guidance and active threats.</li>
                <li><strong>Portfolio Management</strong>: Use this report to inform strategic component selection and vendor partnerships.</li>
            </ul>
        </div>



        <!-- Portfolio-Level Component Risk Data -->
        {% if portfolio_table_data and portfolio_table_data.rows %}
        <div class="table-container">
            <h2>Portfolio-Level Component Risk Data</h2>
            <table>
                <thead>
                    <tr>
                        <th>Component Name</th>
                        <th>Version</th>
                        <th>Projects Affected</th>
                        <th>Portfolio Composite Risk</th>
                        <th>Normalized Risk Score</th>
                        <th>Findings Count</th>
                        <th>Has KEV</th>
                        <th>Has Exploits</th>
                    </tr>
                </thead>
                <tbody>
                    {% for row in portfolio_table_data.rows %}
                    <tr>
                        <td>{{ row.name }}</td>
                        <td>{{ row.version }}</td>
                        <td>{{ row.project_count }}</td>
                        <td class="{% if row.portfolio_composite_risk is defined and row.portfolio_composite_risk|default(0) >= 200 %}severity-critical{% elif row.portfolio_composite_risk is defined and row.portfolio_composite_risk|default(0) >= 100 %}severity-high{% elif row.portfolio_composite_risk is defined and row.portfolio_composite_risk|default(0) >= 50 %}severity-medium{% elif row.portfolio_composite_risk is defined and row.portfolio_composite_risk|default(0) >= 25 %}severity-low{% endif %}">{{ row.portfolio_composite_risk|default(0) }}</td>
                        <td class="{% if row.normalized_risk_score is defined and row.normalized_risk_score|default(0) >= 200 %}severity-critical{% elif row.normalized_risk_score is defined and row.normalized_risk_score|default(0) >= 100 %}severity-high{% elif row.normalized_risk_score is defined and row.normalized_risk_score|default(0) >= 50 %}severity-medium{% elif row.normalized_risk_score is defined and row.normalized_risk_score|default(0) >= 25 %}severity-low{% endif %}">{{ row.normalized_risk_score|default(0) }}</td>
                        <td>{{ row.findings_count }}</td>
                        <td>{% if row.has_kev %}🔒{% else %}-{% endif %}</td>
                        <td>{% if row.has_exploits %}💥{% else %}-{% endif %}</td>
                    </tr>
                    {% endfor %}
                </tbody>
            </table>
        </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>
        document.addEventListener('DOMContentLoaded', function() {
            // Comprehensive color palette for broad analysis
            const colors = {
                primary: '#2E86AB',      // Ocean Blue
                secondary: '#A23B72',    // Deep Rose
                tertiary: '#F18F01',     // Vibrant Orange
                quaternary: '#C73E1D',   // Rich Red
                quinary: '#8B5A2B',      // Saddle Brown
                senary: '#228B22',       // Forest Green
                septenary: '#9370DB',    // Medium Purple
                octonary: '#FF6347',     // Tomato Red
                nonary: '#20B2AA',       // Light Sea Green
                denary: '#FFD700',       // Gold
                eleventh: '#DC143C',     // Crimson
                twelfth: '#32CD32',      // Lime Green
                thirteenth: '#FF69B4',   // Hot Pink
                fourteenth: '#4169E1',   // Royal Blue
                fifteenth: '#FF4500'     // Orange Red
            };

            // Helper function to safely parse JSON
            function safeParseJSON(jsonString, fallback = {}) {
                try {
                    return JSON.parse(jsonString);
                } catch (e) {
                    console.warn('Failed to parse JSON:', e);
                    return fallback;
                }
            }

            // Helper function to create chart with error handling
            function createChart(canvasId, chartData, chartType = 'bar') {
                const canvas = document.getElementById(canvasId);
                if (!canvas) {
                    console.warn(`Canvas with id '${canvasId}' not found`);
                    return;
                }

                if (!chartData || !chartData.labels || !chartData.datasets) {
                    console.warn(`Invalid chart data for ${canvasId}`);
                    return;
                }

                const ctx = canvas.getContext('2d');
                return new Chart(ctx, {
                    type: chartType,
                    data: chartData,
                    options: {
                        responsive: true,
                        maintainAspectRatio: false,
                        interaction: {
                            mode: 'index',
                            intersect: false,
                        },
                        plugins: {
                            title: {
                                display: false
                            },
                            legend: {
                                display: true,
                                position: 'top'
                            },
                            tooltip: {
                                callbacks: {
                                    label: function(context) {
                                        return context.dataset.label + ': ' + context.parsed.y;
                                    }
                                }
                            }
                        },
                        scales: {
                            x: {
                                title: {
                                    display: true,
                                    text: 'Components'
                                }
                            },
                            y: {
                                title: {
                                    display: true,
                                    text: 'Risk Score'
                                }
                            }
                        }
                    }
                });
            }



            // Create Pareto Chart
            {% if chart_data.pareto_chart is mapping and chart_data.pareto_chart|length > 0 %}
            const paretoData = safeParseJSON('{{ chart_data_json.pareto_chart | safe }}');
            if (paretoData.labels && paretoData.labels.length > 0) {
                const paretoCtx = document.getElementById('paretoChart');
                if (paretoCtx) {
                    new Chart(paretoCtx.getContext('2d'), {
                        type: 'bar',
                        data: {
                            labels: paretoData.labels,
                            datasets: paretoData.datasets
                        },
                        options: {
                            responsive: true,
                            maintainAspectRatio: false,
                            interaction: {
                                mode: 'index',
                                intersect: false,
                            },
                            plugins: {
                                title: { display: false },
                                legend: { 
                                    display: true, 
                                    position: 'top',
                                    labels: {
                                        generateLabels: function(chart) {
                                            return [
                                                {
                                                    text: '🔒💥 KEV + Exploit',
                                                    fillStyle: '#ffffff',
                                                    strokeStyle: '#000000',
                                                    lineWidth: 4,
                                                    hidden: false,
                                                    index: 0
                                                },
                                                {
                                                    text: '💥 Has Exploit',
                                                    fillStyle: '#ffffff',
                                                    strokeStyle: '#FF0000',
                                                    lineWidth: 3,
                                                    hidden: false,
                                                    index: 1
                                                },
                                                {
                                                    text: 'Standard Risk',
                                                    fillStyle: '#FFA500',
                                                    strokeStyle: '#FFA500',
                                                    lineWidth: 1,
                                                    hidden: false,
                                                    index: 2
                                                }
                                            ];
                                        }
                                    }
                                },
                                tooltip: {
                                    callbacks: {
                                        afterLabel: function(context) {
                                            const dataIndex = context.dataIndex;
                                            const kevMarker = paretoData.markers && paretoData.markers.kev && paretoData.markers.kev[dataIndex] ? ' 🔒' : '';
                                            const exploitMarker = paretoData.markers && paretoData.markers.exploit && paretoData.markers.exploit[dataIndex] ? ' 💥' : '';
                                            return kevMarker + exploitMarker;
                                        }
                                    }
                                }
                            },
                            scales: {
                                x: {
                                    title: { display: true, text: 'Components (sorted by risk)' }
                                },
                                y: {
                                    type: 'logarithmic',
                                    display: true,
                                    position: 'left',
                                    title: { display: true, text: 'Composite Risk Score (log scale)' },
                                    min: 1
                                },
                                y1: {
                                    type: 'linear',
                                    display: true,
                                    position: 'right',
                                    title: { display: true, text: 'Cumulative Percentage (%)' },
                                    grid: { drawOnChartArea: false },
                                    min: 0,
                                    max: 100
                                }
                            },
                            elements: {
                                bar: {
                                    z: 1
                                },
                                line: {
                                    z: 2
                                },
                                point: {
                                    z: 3
                                }
                            }
                        }
                    });
                }
            }
            {% endif %}

            // Create Bubble Matrix Chart
            {% if chart_data.bubble_matrix is mapping and chart_data.bubble_matrix|length > 0 %}
            const bubbleData = safeParseJSON('{{ chart_data_json.bubble_matrix | safe }}');
            if (bubbleData.data && bubbleData.data.length > 0) {
                const bubbleCtx = document.getElementById('bubbleMatrixChart');
                if (bubbleCtx) {
                    // Find max risk score for relative coloring
                    const maxRiskScore = Math.max(...bubbleData.data.map(p => p.x));
                    const minRiskScore = Math.min(...bubbleData.data.map(p => p.x));
                    
                    // Create datasets with visual indicators and relative coloring
                    const datasets = [];
                    const allPoints = bubbleData.data;
                    
                    // Group by pattern type
                    const exploitOnlyPoints = allPoints.filter(p => !p.inKev && p.hasExploit);
                    const bothPoints = allPoints.filter(p => p.inKev && p.hasExploit);
                    const normalPoints = allPoints.filter(p => !p.inKev && !p.hasExploit);
                    
                    // Helper function to get color based on risk ratio using log scale
                    function getRiskColor(riskScore, maxRisk, minRisk) {
                        // Use logarithmic scale for color mapping to match the axis
                        const logMin = Math.log(Math.max(1, minRisk));
                        const logMax = Math.log(Math.max(1, maxRisk));
                        const logValue = Math.log(Math.max(1, riskScore));
                        const range = logMax - logMin;
                        const ratio = range > 0 ? Math.max(0, Math.min(1, (logValue - logMin) / range)) : 0;
                        return `hsl(${120 - ratio * 120}, 70%, 50%)`; // Green to red
                    }
                    
                    // Add datasets with visual indicators
                    if (exploitOnlyPoints.length > 0) {
                        datasets.push({
                            label: '💥 Has Exploit',
                            data: exploitOnlyPoints,
                            backgroundColor: exploitOnlyPoints.map(p => getRiskColor(p.x, maxRiskScore, minRiskScore)),
                            borderColor: '#FF0000',
                            borderWidth: 3,
                            fill: true
                        });
                    }
                    
                    if (bothPoints.length > 0) {
                        datasets.push({
                            label: '🔒💥 KEV + Exploit',
                            data: bothPoints,
                            backgroundColor: bothPoints.map(p => getRiskColor(p.x, maxRiskScore, minRiskScore)),
                            borderColor: '#000000',
                            borderWidth: 4,
                            fill: true
                        });
                    }
                    
                    if (normalPoints.length > 0) {
                        datasets.push({
                            label: 'Standard Risk',
                            data: normalPoints,
                            backgroundColor: normalPoints.map(p => getRiskColor(p.x, maxRiskScore, minRiskScore)),
                            borderColor: normalPoints.map(p => getRiskColor(p.x, maxRiskScore, minRiskScore)),
                            borderWidth: 1,
                            fill: true
                        });
                    }
                    
                    // Find top N by risk score for labeling
                    const N = 10;
                    const topPoints = [...allPoints].sort((a, b) => b.x - a.x).slice(0, N);
                    
                    // Compute y max for headroom
                    const maxY = Math.max(...allPoints.map(p => p.y));
                    const yMax = Math.ceil(maxY + 1);
                    
                    new Chart(bubbleCtx.getContext('2d'), {
                        type: 'bubble',
                        data: { datasets: datasets },
                        options: {
                            responsive: true,
                            maintainAspectRatio: false,
                            plugins: {
                                title: { display: false },
                                legend: { 
                                    display: true, 
                                    position: 'top',
                                    labels: {
                                        generateLabels: function(chart) {
                                            const datasets = chart.data.datasets;
                                            return datasets.map((dataset, i) => {
                                                const isExploit = dataset.label.includes('💥') && !dataset.label.includes('🔒');
                                                const isBoth = dataset.label.includes('🔒💥');
                                                const isStandard = dataset.label === 'Standard Risk';
                                                
                                                return {
                                                    text: dataset.label,
                                                    fillStyle: isStandard ? '#FFA500' : 
                                                             (isExploit || isBoth) ? '#ffffff' : 
                                                             (dataset.backgroundColor[0] || dataset.backgroundColor),
                                                    strokeStyle: isBoth ? '#000000' : 
                                                                isExploit ? '#FF0000' : 
                                                                (dataset.borderColor[0] || dataset.borderColor),
                                                    lineWidth: isBoth ? 4 : 
                                                              isExploit ? 3 : 
                                                              (dataset.borderWidth || 1),
                                                    hidden: !chart.isDatasetVisible(i),
                                                    index: i
                                                };
                                            });
                                        }
                                    }
                                },
                                tooltip: {
                                    callbacks: {
                                        label: function(context) {
                                            const point = context.raw;
                                            let label = [
                                                `Component: ${point.component}`,
                                                `Risk Score: ${point.x.toLocaleString()}`,
                                                `Projects Affected: ${point.y}`,
                                                `Finding Count: ${point.findingCount}`
                                            ];
                                            if (point.hasExploit && point.inKev) label.push('🔒💥 KEV + Exploit');
                                            else if (point.hasExploit) label.push('💥 Has Known Exploit');
                                            else if (point.inKev) label.push('🔒 In KEV');
                                            return label.filter(Boolean);
                                        }
                                    }
                                },
                                datalabels: {
                                    display: function(context) {
                                        const point = context.raw;
                                        return topPoints.includes(point);
                                    },
                                    align: 'top',
                                    anchor: 'end',
                                    font: { weight: 'bold', size: 10 },
                                    color: '#222',
                                    backgroundColor: 'rgba(255,255,255,0.7)',
                                    borderRadius: 4,
                                    padding: 2,
                                    formatter: function(value, context) {
                                        let name = value.component || '';
                                        if (name.length > 18) name = name.slice(0, 15) + '...';
                                        return name;
                                    }
                                }
                            },
                            scales: {
                                x: {
                                    type: 'logarithmic',
                                    title: { display: true, text: 'Composite Risk Score (log scale)' },
                                    min: Math.max(1, minRiskScore * 0.1),
                                    max: maxRiskScore * 1.1
                                },
                                y: {
                                    title: { display: true, text: 'Number of Projects Affected' },
                                    ticks: {
                                        stepSize: 1,
                                        precision: 0
                                    },
                                    min: 0,
                                    max: yMax
                                }
                            }
                        },
                        plugins: [ChartDataLabels]
                    });
                }
            }
            {% endif %}


        });
    </script>
</body>
</html> 