<?php
/**
 * NIS2 Agile - Report Service
 *
 * Genera report di compliance NIS2:
 * - Report completo compliance (dati strutturati)
 * - Report esecutivo HTML per CdA (Art. 20 NIS2)
 * - Export CSV (rischi, incidenti, controlli, asset)
 *
 * Tutti i CSV usano separatore punto e virgola e BOM UTF-8
 * per massima compatibilita con Excel in ambiente italiano.
 */

class ReportService
{
    // ═══════════════════════════════════════════════════════════════════════════
    // REPORT COMPLIANCE COMPLETO
    // ═══════════════════════════════════════════════════════════════════════════

    /**
     * Genera il report di compliance NIS2 completo per un'organizzazione.
     *
     * Restituisce un array strutturato con tutti i dati necessari
     * per il rendering HTML lato frontend.
     *
     * @param int $orgId ID dell'organizzazione
     * @return array Dati strutturati del report
     */
    public function generateComplianceReport(int $orgId): array
    {
        // --- Informazioni organizzazione ---
        $organization = Database::fetchOne(
            'SELECT id, name, sector, entity_type, subscription_plan,
                    employee_count, annual_turnover_eur, created_at
             FROM organizations
             WHERE id = ?',
            [$orgId]
        );

        if (!$organization) {
            return ['error' => 'Organizzazione non trovata'];
        }

        // --- Ultimo assessment completato ---
        $lastAssessment = Database::fetchOne(
            'SELECT id, title, overall_score, category_scores, status,
                    completed_at, ai_summary, ai_recommendations
             FROM assessments
             WHERE organization_id = ? AND status = "completed"
             ORDER BY completed_at DESC
             LIMIT 1',
            [$orgId]
        );

        $categoryScores = [];
        if ($lastAssessment && $lastAssessment['category_scores']) {
            $categoryScores = json_decode($lastAssessment['category_scores'], true) ?? [];
        }

        // --- Compliance controls ---
        $controls = Database::fetchAll(
            'SELECT control_code, title, status, implementation_percentage
             FROM compliance_controls
             WHERE organization_id = ?
             ORDER BY control_code',
            [$orgId]
        );

        $controlStats = [
            'total'           => count($controls),
            'implemented'     => 0,
            'in_progress'     => 0,
            'not_started'     => 0,
            'not_applicable'  => 0,
            'verified'        => 0,
        ];

        $totalImplementation = 0;
        foreach ($controls as $c) {
            $status = $c['status'] ?? 'not_started';
            if (isset($controlStats[$status])) {
                $controlStats[$status]++;
            } else {
                $controlStats['not_started']++;
            }
            $totalImplementation += (int) ($c['implementation_percentage'] ?? 0);
        }

        $controlStats['avg_implementation'] = $controlStats['total'] > 0
            ? round($totalImplementation / $controlStats['total'], 1)
            : 0;

        // --- Rischi ---
        $risks = Database::fetchAll(
            'SELECT id, risk_code, title, category, likelihood, impact,
                    inherent_risk_score, residual_risk_score, treatment, status
             FROM risks
             WHERE organization_id = ?
             ORDER BY inherent_risk_score DESC',
            [$orgId]
        );

        $openRisks = array_filter($risks, fn(array $r): bool => ($r['status'] ?? '') !== 'closed');

        $riskByCategory = [];
        foreach ($openRisks as $r) {
            $cat = $r['category'] ?? 'other';
            if (!isset($riskByCategory[$cat])) {
                $riskByCategory[$cat] = 0;
            }
            $riskByCategory[$cat]++;
        }

        $severityDistribution = [
            'critical' => count(array_filter($openRisks, fn(array $r): bool => ($r['inherent_risk_score'] ?? 0) >= 20)),
            'high'     => count(array_filter($openRisks, fn(array $r): bool => ($r['inherent_risk_score'] ?? 0) >= 12 && ($r['inherent_risk_score'] ?? 0) < 20)),
            'medium'   => count(array_filter($openRisks, fn(array $r): bool => ($r['inherent_risk_score'] ?? 0) >= 6 && ($r['inherent_risk_score'] ?? 0) < 12)),
            'low'      => count(array_filter($openRisks, fn(array $r): bool => ($r['inherent_risk_score'] ?? 0) < 6)),
        ];

        $riskSummary = [
            'total_risks'           => count($risks),
            'open_risks'            => count($openRisks),
            'by_category'           => $riskByCategory,
            'severity_distribution' => $severityDistribution,
        ];

        // --- Incidenti aperti ---
        $openIncidents = Database::count(
            'incidents',
            'organization_id = ? AND status NOT IN ("closed", "post_mortem")',
            [$orgId]
        );

        $totalIncidents = Database::count('incidents', 'organization_id = ?', [$orgId]);

        $significantIncidents = Database::count(
            'incidents',
            'organization_id = ? AND is_significant = 1',
            [$orgId]
        );

        $incidentSummary = [
            'total'       => $totalIncidents,
            'open'        => $openIncidents,
            'significant' => $significantIncidents,
        ];

        // --- Policy ---
        $policyStats = Database::fetchAll(
            'SELECT status, COUNT(*) as count
             FROM policies
             WHERE organization_id = ?
             GROUP BY status',
            [$orgId]
        );

        $policyMap = [];
        $totalPolicies = 0;
        foreach ($policyStats as $ps) {
            $policyMap[$ps['status']] = (int) $ps['count'];
            $totalPolicies += (int) $ps['count'];
        }

        $policySummary = [
            'total'    => $totalPolicies,
            'approved' => $policyMap['approved'] ?? 0,
            'draft'    => $policyMap['draft'] ?? 0,
            'review'   => $policyMap['review'] ?? 0,
            'archived' => $policyMap['archived'] ?? 0,
        ];

        // --- Formazione ---
        $trainingStats = Database::fetchOne(
            'SELECT
                COUNT(*) as total,
                SUM(CASE WHEN status = "completed" THEN 1 ELSE 0 END) as completed,
                SUM(CASE WHEN status = "in_progress" THEN 1 ELSE 0 END) as in_progress,
                SUM(CASE WHEN status = "assigned" THEN 1 ELSE 0 END) as assigned,
                SUM(CASE WHEN status = "overdue" THEN 1 ELSE 0 END) as overdue
             FROM training_assignments
             WHERE organization_id = ?',
            [$orgId]
        );

        $trainingTotal = (int) ($trainingStats['total'] ?? 0);
        $trainingCompleted = (int) ($trainingStats['completed'] ?? 0);

        $trainingSummary = [
            'total_assignments' => $trainingTotal,
            'completed'         => $trainingCompleted,
            'in_progress'       => (int) ($trainingStats['in_progress'] ?? 0),
            'assigned'          => (int) ($trainingStats['assigned'] ?? 0),
            'overdue'           => (int) ($trainingStats['overdue'] ?? 0),
            'completion_rate'   => $trainingTotal > 0
                ? round($trainingCompleted / $trainingTotal * 100, 1)
                : 0,
        ];

        // --- Supply chain ---
        $supplierStats = Database::fetchOne(
            'SELECT
                COUNT(*) as total,
                SUM(CASE WHEN criticality IN ("high", "critical") THEN 1 ELSE 0 END) as critical_high,
                SUM(CASE WHEN security_requirements_met = 1 THEN 1 ELSE 0 END) as compliant,
                SUM(CASE WHEN security_requirements_met = 0 THEN 1 ELSE 0 END) as non_compliant,
                AVG(risk_score) as avg_risk_score
             FROM suppliers
             WHERE organization_id = ? AND status = "active"',
            [$orgId]
        );

        $supplyChainSummary = [
            'total_suppliers'       => (int) ($supplierStats['total'] ?? 0),
            'critical_high'         => (int) ($supplierStats['critical_high'] ?? 0),
            'compliant'             => (int) ($supplierStats['compliant'] ?? 0),
            'non_compliant'         => (int) ($supplierStats['non_compliant'] ?? 0),
            'avg_risk_score'        => round((float) ($supplierStats['avg_risk_score'] ?? 0), 1),
        ];

        // --- Composizione report ---
        return [
            'generated_at'      => date('Y-m-d H:i:s'),
            'organization'      => $organization,
            'assessment'        => [
                'last_assessment'  => $lastAssessment,
                'overall_score'    => $lastAssessment ? (float) $lastAssessment['overall_score'] : null,
                'category_scores'  => $categoryScores,
            ],
            'controls'          => [
                'items' => $controls,
                'stats' => $controlStats,
            ],
            'risks'             => $riskSummary,
            'incidents'         => $incidentSummary,
            'policies'          => $policySummary,
            'training'          => $trainingSummary,
            'supply_chain'      => $supplyChainSummary,
        ];
    }

    // ═══════════════════════════════════════════════════════════════════════════
    // REPORT ESECUTIVO HTML (Art. 20 NIS2)
    // ═══════════════════════════════════════════════════════════════════════════

    /**
     * Genera un report esecutivo HTML stampabile per il CdA.
     *
     * Il documento e completamente autonomo (inline CSS) e pensato
     * per essere stampato dal browser o salvato come PDF.
     *
     * Riferimento: Art. 20 Direttiva NIS2 - Governance e responsabilita
     * degli organi di gestione.
     *
     * @param int $orgId ID dell'organizzazione
     * @return string Documento HTML completo
     */
    /**
     * Registro formale "Elenco Sistemi Rilevanti NIS2" (requisito GV.OC-04).
     * HTML stampabile (PDF via stampa browser). Cita le fonti normative certe.
     * Adattato dal mockup docs/nis2/doc-relevant-systems.html.
     */
    public function generateRelevantSystemsRegister(int $orgId): string
    {
        $org = Database::fetchOne('SELECT name, sector, entity_type FROM organizations WHERE id = ?', [$orgId]);
        if (!$org) {
            return $this->buildErrorHtml('Organizzazione non trovata');
        }

        $systems = Database::fetchAll(
            "SELECT a.name, a.asset_type, a.category, a.ip_address, a.location,
                    a.relevance_score, a.relevance_class, a.relevance_assessed_at,
                    u.full_name AS owner_name
             FROM assets a
             LEFT JOIN users u ON u.id = a.owner_user_id
             WHERE a.organization_id = ? AND a.is_nis2_relevant = 1
             ORDER BY a.relevance_score DESC, a.name",
            [$orgId]
        );

        $orgName  = htmlspecialchars($org['name'] ?? '', ENT_QUOTES, 'UTF-8');
        $entity   = htmlspecialchars($org['entity_type'] ?? '', ENT_QUOTES, 'UTF-8');
        $sector   = htmlspecialchars($org['sector'] ?? '', ENT_QUOTES, 'UTF-8');
        $date     = date('d/m/Y');
        $appName  = defined('APP_NAME') ? APP_NAME : 'NIS2 Agile';
        $appVer   = defined('APP_VERSION') ? APP_VERSION : '1.0.0';

        $classColors = ['critico' => '#dc2626', 'alto' => '#ea580c', 'medio' => '#ca8a04'];
        $counts = ['critico' => 0, 'alto' => 0, 'medio' => 0];

        $rowsHtml = '';
        if (empty($systems)) {
            $rowsHtml = '<tr><td colspan="7" style="text-align:center;color:#888;padding:24px;">Nessun sistema classificato come rilevante (score &ge; 40). Eseguire la valutazione di rilevanza dal modulo Inventario Asset.</td></tr>';
        } else {
            $n = 0;
            foreach ($systems as $s) {
                $n++;
                $cls = $s['relevance_class'] ?? 'medio';
                if (isset($counts[$cls])) $counts[$cls]++;
                $color = $classColors[$cls] ?? '#6b7280';
                $rowsHtml .= '<tr>'
                    . '<td>' . $n . '</td>'
                    . '<td><strong>' . htmlspecialchars($s['name'], ENT_QUOTES, 'UTF-8') . '</strong></td>'
                    . '<td>' . htmlspecialchars(($s['asset_type'] ?? '') . ($s['category'] ? ' / ' . $s['category'] : ''), ENT_QUOTES, 'UTF-8') . '</td>'
                    . '<td>' . htmlspecialchars($s['ip_address'] ?: '-', ENT_QUOTES, 'UTF-8') . '</td>'
                    . '<td>' . htmlspecialchars($s['owner_name'] ?: '-', ENT_QUOTES, 'UTF-8') . '</td>'
                    . '<td style="text-align:center;font-weight:700;">' . (int) $s['relevance_score'] . '/100</td>'
                    . '<td style="color:' . $color . ';font-weight:700;text-transform:uppercase;">' . htmlspecialchars($cls, ENT_QUOTES, 'UTF-8') . '</td>'
                    . '</tr>';
            }
        }

        $total = count($systems);

        return <<<HTML
<!DOCTYPE html>
<html lang="it"><head><meta charset="utf-8">
<title>Elenco Sistemi Rilevanti NIS2 - {$orgName}</title>
<style>
  body{font-family:'Segoe UI',Arial,sans-serif;color:#1f2937;margin:40px;font-size:13px;}
  .conf{background:#7f1d1d;color:#fff;text-align:center;padding:8px;font-weight:700;letter-spacing:1px;border-radius:6px;margin-bottom:18px;}
  h1{font-size:22px;margin:0 0 4px;} h2{font-size:15px;border-bottom:2px solid #06b6d4;padding-bottom:4px;margin-top:28px;}
  .meta{color:#6b7280;font-size:12px;margin-bottom:18px;}
  table{width:100%;border-collapse:collapse;margin-top:10px;} th,td{border:1px solid #e5e7eb;padding:7px 9px;text-align:left;}
  th{background:#f0f9ff;font-size:12px;} .stats{display:flex;gap:14px;margin:14px 0;}
  .stat{flex:1;border:1px solid #e5e7eb;border-radius:8px;padding:10px;text-align:center;}
  .stat .v{font-size:22px;font-weight:800;} .src{background:#f9fafb;border-left:4px solid #06b6d4;padding:10px 14px;font-size:11.5px;color:#374151;margin-top:24px;border-radius:0 6px 6px 0;}
  .sign{margin-top:36px;display:flex;justify-content:space-between;} .sign div{width:45%;border-top:1px solid #9ca3af;padding-top:6px;font-size:12px;color:#4b5563;}
  @media print{body{margin:12mm;} .noprint{display:none;}}
</style></head><body>
<div class="conf">&#9888; DOCUMENTO RISERVATO - DISTRIBUZIONE LIMITATA &#9888;</div>
<h1>{$orgName}</h1>
<h1 style="font-size:18px;color:#0e7490;">Elenco Formale dei Sistemi Rilevanti NIS2</h1>
<div class="meta">Documento ai sensi della Direttiva (UE) 2022/2555 e del D.Lgs. 138/2024 &mdash; Requisito GV.OC-04 (NIST CSF 2.0)<br>
Settore: {$sector} &nbsp;|&nbsp; Tipo soggetto: {$entity} &nbsp;|&nbsp; Data emissione: {$date} &nbsp;|&nbsp; Generato da {$appName} v{$appVer}</div>

<h2>1. Premessa e metodologia</h2>
<p>Il presente documento costituisce l'elenco formale dei sistemi informativi e di rete classificati come <strong>rilevanti</strong> ai fini della conformita alla Direttiva NIS2. La classificazione adotta una metodologia di scoring 0-100 su sei criteri (Criticita Operativa, Impatto Interruzione, Dati Trattati, Dipendenze, Esposizione, Obblighi Normativi). <strong>Soglia di rilevanza: punteggio &ge; 40.</strong> I sistemi con punteggio &ge; 80 sono considerati critici e richiedono misure di sicurezza massime e monitoraggio continuo.</p>

<div class="stats">
  <div class="stat"><div class="v">{$total}</div>Sistemi rilevanti</div>
  <div class="stat"><div class="v" style="color:#dc2626;">{$counts['critico']}</div>Critici (&ge;80)</div>
  <div class="stat"><div class="v" style="color:#ea580c;">{$counts['alto']}</div>Alti (60-79)</div>
  <div class="stat"><div class="v" style="color:#ca8a04;">{$counts['medio']}</div>Medi (40-59)</div>
</div>

<h2>2. Elenco sistemi rilevanti</h2>
<table>
  <thead><tr><th>#</th><th>Sistema</th><th>Tipo/Categoria</th><th>IP</th><th>Responsabile</th><th>Punteggio</th><th>Classe</th></tr></thead>
  <tbody>{$rowsHtml}</tbody>
</table>

<div class="src">
  <strong>Fonti normative certe:</strong><br>
  &bull; Direttiva (UE) 2022/2555 (NIS2) &mdash; Parlamento europeo e Consiglio UE<br>
  &bull; D.Lgs. 4 settembre 2024, n. 138 &mdash; recepimento NIS2 (artt. 23, 24)<br>
  &bull; NIST Cybersecurity Framework 2.0 &mdash; controllo GV.OC-04 (elenco sistemi rilevanti)
</div>

<div class="sign">
  <div>Redatto da (CISO / Responsabile Compliance)</div>
  <div>Approvato da (Organo di gestione / Direzione)</div>
</div>

<p class="noprint" style="margin-top:24px;"><button onclick="window.print()">Stampa / Salva PDF</button></p>
</body></html>
HTML;
    }

    public function generateExecutiveReport(int $orgId): string
    {
        $data = $this->generateComplianceReport($orgId);

        if (isset($data['error'])) {
            return $this->buildErrorHtml($data['error']);
        }

        $org          = $data['organization'];
        $assessment   = $data['assessment'];
        $controls     = $data['controls']['stats'];
        $riskSummary  = $data['risks'];
        $incidents    = $data['incidents'];
        $policies     = $data['policies'];
        $training     = $data['training'];
        $supplyChain  = $data['supply_chain'];

        $orgName        = htmlspecialchars($org['name'] ?? '', ENT_QUOTES, 'UTF-8');
        $orgSector      = htmlspecialchars($org['sector'] ?? '', ENT_QUOTES, 'UTF-8');
        $orgEntityType  = htmlspecialchars($org['entity_type'] ?? '', ENT_QUOTES, 'UTF-8');
        $score          = $assessment['overall_score'] ?? 0;
        $scoreFormatted = number_format((float) $score, 1, ',', '.');
        $reportDate     = date('d/m/Y');
        $reportTime     = date('H:i');
        $appName        = defined('APP_NAME') ? APP_NAME : 'NIS2 Agile';
        $appVersion     = defined('APP_VERSION') ? APP_VERSION : '1.0.0';
        $appUrl         = defined('APP_URL') ? APP_URL : '';

        $scoreColor     = $this->getScoreColor((float) $score);
        $scoreLabel     = $this->getScoreLabel((float) $score);

        // Categoria scores per la tabella
        $categoryRows = '';
        if (!empty($assessment['category_scores'])) {
            foreach ($assessment['category_scores'] as $catId => $catData) {
                $catScore = is_array($catData) ? ($catData['score'] ?? 0) : (float) $catData;
                $catColor = $this->getScoreColor($catScore);
                $catLabel = htmlspecialchars(ucfirst(str_replace('_', ' ', $catId)), ENT_QUOTES, 'UTF-8');
                $catScoreFmt = number_format($catScore, 1, ',', '.');
                $categoryRows .= <<<ROW
                <tr>
                    <td style="padding: 8px 12px; border-bottom: 1px solid #e5e7eb;">{$catLabel}</td>
                    <td style="padding: 8px 12px; border-bottom: 1px solid #e5e7eb; text-align: center;">
                        <span style="color: {$catColor}; font-weight: 600;">{$catScoreFmt}%</span>
                    </td>
                    <td style="padding: 8px 12px; border-bottom: 1px solid #e5e7eb;">
                        <div style="background: #e5e7eb; border-radius: 4px; height: 12px; width: 100%;">
                            <div style="background: {$catColor}; border-radius: 4px; height: 12px; width: {$catScore}%;"></div>
                        </div>
                    </td>
                </tr>
                ROW;
            }
        }

        // Risk heatmap summary
        $sevDist = $riskSummary['severity_distribution'] ?? [];
        $riskCritical = (int) ($sevDist['critical'] ?? 0);
        $riskHigh     = (int) ($sevDist['high'] ?? 0);
        $riskMedium   = (int) ($sevDist['medium'] ?? 0);
        $riskLow      = (int) ($sevDist['low'] ?? 0);

        // Risk by category
        $riskCategoryRows = '';
        $riskByCategory = $riskSummary['by_category'] ?? [];
        arsort($riskByCategory);
        foreach ($riskByCategory as $cat => $count) {
            $catLabel = htmlspecialchars(ucfirst(str_replace('_', ' ', $cat)), ENT_QUOTES, 'UTF-8');
            $riskCategoryRows .= <<<ROW
            <tr>
                <td style="padding: 6px 12px; border-bottom: 1px solid #e5e7eb;">{$catLabel}</td>
                <td style="padding: 6px 12px; border-bottom: 1px solid #e5e7eb; text-align: center; font-weight: 600;">{$count}</td>
            </tr>
            ROW;
        }

        // Raccomandazioni
        $recommendations = $this->buildRecommendations($data);
        $recommendationItems = '';
        foreach ($recommendations as $rec) {
            $recText = htmlspecialchars($rec, ENT_QUOTES, 'UTF-8');
            $recommendationItems .= "<li style=\"margin-bottom: 8px; line-height: 1.5;\">{$recText}</li>\n";
        }

        // Assessment info
        $assessmentDate = 'N/D';
        $assessmentTitle = 'Nessun assessment completato';
        if ($assessment['last_assessment']) {
            $assessmentDate = date('d/m/Y', strtotime($assessment['last_assessment']['completed_at']));
            $assessmentTitle = htmlspecialchars($assessment['last_assessment']['title'] ?? '', ENT_QUOTES, 'UTF-8');
        }

        // Training completion
        $trainingRate = number_format($training['completion_rate'] ?? 0, 1, ',', '.');
        $trainingColor = $this->getScoreColor($training['completion_rate'] ?? 0);

        // Supply chain
        $scCompliance = $supplyChain['total_suppliers'] > 0
            ? round($supplyChain['compliant'] / $supplyChain['total_suppliers'] * 100, 1)
            : 0;
        $scComplianceFmt = number_format($scCompliance, 1, ',', '.');

        // Costruzione HTML
        $html = <<<HTML
        <!DOCTYPE html>
        <html lang="it">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Report Esecutivo NIS2 - {$orgName}</title>
            <style>
                @media print {
                    body { margin: 0; padding: 15mm; }
                    .page-break { page-break-before: always; }
                    .no-print { display: none !important; }
                }

                * { box-sizing: border-box; margin: 0; padding: 0; }

                body {
                    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
                    font-size: 13px;
                    line-height: 1.6;
                    color: #1f2937;
                    background: #ffffff;
                    padding: 40px;
                    max-width: 900px;
                    margin: 0 auto;
                }

                .header {
                    background: linear-gradient(135deg, #1e40af 0%, #1e3a8a 100%);
                    color: #ffffff;
                    padding: 30px 35px;
                    border-radius: 8px;
                    margin-bottom: 30px;
                }

                .header h1 {
                    font-size: 22px;
                    font-weight: 700;
                    margin-bottom: 4px;
                }

                .header h2 {
                    font-size: 15px;
                    font-weight: 400;
                    opacity: 0.9;
                    margin-bottom: 12px;
                }

                .header-meta {
                    display: flex;
                    justify-content: space-between;
                    font-size: 12px;
                    opacity: 0.85;
                    border-top: 1px solid rgba(255,255,255,0.25);
                    padding-top: 12px;
                    margin-top: 8px;
                }

                .section {
                    margin-bottom: 28px;
                }

                .section-title {
                    font-size: 16px;
                    font-weight: 700;
                    color: #1e40af;
                    border-bottom: 2px solid #1e40af;
                    padding-bottom: 6px;
                    margin-bottom: 16px;
                }

                .score-gauge {
                    text-align: center;
                    padding: 25px;
                    background: #f8fafc;
                    border-radius: 8px;
                    border: 1px solid #e5e7eb;
                    margin-bottom: 20px;
                }

                .score-value {
                    font-size: 52px;
                    font-weight: 800;
                    line-height: 1.1;
                }

                .score-label {
                    font-size: 14px;
                    font-weight: 600;
                    margin-top: 4px;
                }

                .score-sublabel {
                    font-size: 12px;
                    color: #6b7280;
                    margin-top: 2px;
                }

                .metrics-grid {
                    display: grid;
                    grid-template-columns: repeat(4, 1fr);
                    gap: 12px;
                    margin-bottom: 20px;
                }

                .metric-card {
                    background: #f8fafc;
                    border: 1px solid #e5e7eb;
                    border-radius: 8px;
                    padding: 16px;
                    text-align: center;
                }

                .metric-value {
                    font-size: 26px;
                    font-weight: 800;
                    color: #1e40af;
                    line-height: 1.2;
                }

                .metric-label {
                    font-size: 11px;
                    color: #6b7280;
                    text-transform: uppercase;
                    letter-spacing: 0.5px;
                    margin-top: 4px;
                }

                table {
                    width: 100%;
                    border-collapse: collapse;
                    margin-bottom: 16px;
                }

                table thead th {
                    background: #1e40af;
                    color: #ffffff;
                    padding: 10px 12px;
                    text-align: left;
                    font-size: 12px;
                    font-weight: 600;
                    text-transform: uppercase;
                    letter-spacing: 0.5px;
                }

                table thead th:first-child {
                    border-top-left-radius: 6px;
                }

                table thead th:last-child {
                    border-top-right-radius: 6px;
                }

                table tbody td {
                    padding: 8px 12px;
                    border-bottom: 1px solid #e5e7eb;
                    font-size: 13px;
                }

                table tbody tr:last-child td {
                    border-bottom: none;
                }

                .heatmap-grid {
                    display: grid;
                    grid-template-columns: repeat(4, 1fr);
                    gap: 10px;
                    margin-bottom: 16px;
                }

                .heatmap-cell {
                    border-radius: 8px;
                    padding: 14px 10px;
                    text-align: center;
                    color: #ffffff;
                    font-weight: 700;
                }

                .heatmap-cell .count {
                    font-size: 28px;
                    line-height: 1.1;
                }

                .heatmap-cell .label {
                    font-size: 11px;
                    text-transform: uppercase;
                    letter-spacing: 0.5px;
                    margin-top: 4px;
                    opacity: 0.9;
                }

                .recommendations {
                    background: #f0f9ff;
                    border: 1px solid #bae6fd;
                    border-left: 4px solid #1e40af;
                    border-radius: 0 8px 8px 0;
                    padding: 20px 24px;
                }

                .recommendations ol {
                    padding-left: 20px;
                }

                .footer {
                    margin-top: 40px;
                    padding-top: 20px;
                    border-top: 2px solid #e5e7eb;
                    display: flex;
                    justify-content: space-between;
                    align-items: center;
                    font-size: 11px;
                    color: #9ca3af;
                }

                .footer-brand {
                    font-weight: 700;
                    color: #1e40af;
                    font-size: 13px;
                }

                .two-col {
                    display: grid;
                    grid-template-columns: 1fr 1fr;
                    gap: 20px;
                }

                @media print {
                    .metrics-grid { grid-template-columns: repeat(4, 1fr); }
                    .heatmap-grid { grid-template-columns: repeat(4, 1fr); }
                    .header { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
                    .heatmap-cell { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
                    table thead th { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
                }
            </style>
        </head>
        <body>

        <!-- HEADER -->
        <div class="header">
            <h1>Report Esecutivo Compliance NIS2</h1>
            <h2>{$orgName}</h2>
            <div class="header-meta">
                <span>Settore: {$orgSector} | Tipo entit&agrave;: {$orgEntityType}</span>
                <span>Data: {$reportDate} ore {$reportTime}</span>
            </div>
        </div>

        <!-- COMPLIANCE SCORE GAUGE -->
        <div class="section">
            <div class="section-title">Indice di Compliance NIS2</div>
            <div class="score-gauge">
                <div class="score-value" style="color: {$scoreColor};">{$scoreFormatted}%</div>
                <div class="score-label" style="color: {$scoreColor};">{$scoreLabel}</div>
                <div class="score-sublabel">Ultimo assessment: {$assessmentTitle} ({$assessmentDate})</div>
            </div>
        </div>

        <!-- KEY METRICS -->
        <div class="section">
            <div class="section-title">Metriche Chiave</div>
            <div class="metrics-grid">
                <div class="metric-card">
                    <div class="metric-value">{$controls['total']}</div>
                    <div class="metric-label">Controlli totali</div>
                </div>
                <div class="metric-card">
                    <div class="metric-value">{$controls['avg_implementation']}%</div>
                    <div class="metric-label">Implementazione media</div>
                </div>
                <div class="metric-card">
                    <div class="metric-value">{$riskSummary['open_risks']}</div>
                    <div class="metric-label">Rischi aperti</div>
                </div>
                <div class="metric-card">
                    <div class="metric-value">{$incidents['open']}</div>
                    <div class="metric-label">Incidenti attivi</div>
                </div>
            </div>

            <table>
                <thead>
                    <tr>
                        <th style="width: 50%;">Indicatore</th>
                        <th style="width: 25%; text-align: center;">Valore</th>
                        <th style="width: 25%; text-align: center;">Stato</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>Policy approvate</td>
                        <td style="text-align: center; font-weight: 600;">{$policies['approved']} / {$policies['total']}</td>
                        <td style="text-align: center;">{$this->getStatusBadge($policies['total'] > 0 ? $policies['approved'] / $policies['total'] * 100 : 0)}</td>
                    </tr>
                    <tr>
                        <td>Formazione completata</td>
                        <td style="text-align: center; font-weight: 600;">{$trainingRate}%</td>
                        <td style="text-align: center;">{$this->getStatusBadge($training['completion_rate'] ?? 0)}</td>
                    </tr>
                    <tr>
                        <td>Fornitori conformi (supply chain)</td>
                        <td style="text-align: center; font-weight: 600;">{$supplyChain['compliant']} / {$supplyChain['total_suppliers']}</td>
                        <td style="text-align: center;">{$this->getStatusBadge($scCompliance)}</td>
                    </tr>
                    <tr>
                        <td>Incidenti significativi (NIS2 Art. 23)</td>
                        <td style="text-align: center; font-weight: 600;">{$incidents['significant']}</td>
                        <td style="text-align: center;">{$this->getCountBadge($incidents['significant'])}</td>
                    </tr>
                    <tr>
                        <td>Fornitori critici non conformi</td>
                        <td style="text-align: center; font-weight: 600;">{$supplyChain['non_compliant']}</td>
                        <td style="text-align: center;">{$this->getCountBadge($supplyChain['non_compliant'])}</td>
                    </tr>
                </tbody>
            </table>
        </div>

        <!-- CATEGORY BREAKDOWN -->
        <div class="section">
            <div class="section-title">Dettaglio per Categoria (Art. 21.2 NIS2)</div>
            <table>
                <thead>
                    <tr>
                        <th style="width: 40%;">Categoria</th>
                        <th style="width: 15%; text-align: center;">Score</th>
                        <th style="width: 45%;">Progresso</th>
                    </tr>
                </thead>
                <tbody>
                    {$categoryRows}
                </tbody>
            </table>
        </div>

        <div class="page-break"></div>

        <!-- RISK HEATMAP -->
        <div class="section">
            <div class="section-title">Riepilogo Rischi Cyber</div>

            <div class="heatmap-grid">
                <div class="heatmap-cell" style="background: #dc2626;">
                    <div class="count">{$riskCritical}</div>
                    <div class="label">Critici</div>
                </div>
                <div class="heatmap-cell" style="background: #ea580c;">
                    <div class="count">{$riskHigh}</div>
                    <div class="label">Alti</div>
                </div>
                <div class="heatmap-cell" style="background: #ca8a04;">
                    <div class="count">{$riskMedium}</div>
                    <div class="label">Medi</div>
                </div>
                <div class="heatmap-cell" style="background: #16a34a;">
                    <div class="count">{$riskLow}</div>
                    <div class="label">Bassi</div>
                </div>
            </div>

            <div class="two-col">
                <div>
                    <table>
                        <thead>
                            <tr>
                                <th>Categoria rischio</th>
                                <th style="text-align: center;">N. rischi</th>
                            </tr>
                        </thead>
                        <tbody>
                            {$riskCategoryRows}
                        </tbody>
                    </table>
                </div>
                <div>
                    <table>
                        <thead>
                            <tr>
                                <th>Metrica</th>
                                <th style="text-align: center;">Valore</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr>
                                <td style="padding: 6px 12px; border-bottom: 1px solid #e5e7eb;">Rischi totali registrati</td>
                                <td style="padding: 6px 12px; border-bottom: 1px solid #e5e7eb; text-align: center; font-weight: 600;">{$riskSummary['total_risks']}</td>
                            </tr>
                            <tr>
                                <td style="padding: 6px 12px; border-bottom: 1px solid #e5e7eb;">Rischi aperti</td>
                                <td style="padding: 6px 12px; border-bottom: 1px solid #e5e7eb; text-align: center; font-weight: 600;">{$riskSummary['open_risks']}</td>
                            </tr>
                            <tr>
                                <td style="padding: 6px 12px; border-bottom: 1px solid #e5e7eb;">Critici + Alti</td>
                                <td style="padding: 6px 12px; border-bottom: 1px solid #e5e7eb; text-align: center; font-weight: 600; color: #dc2626;">{$this->sumValues($riskCritical, $riskHigh)}</td>
                            </tr>
                            <tr>
                                <td style="padding: 6px 12px;">Score medio fornitori</td>
                                <td style="padding: 6px 12px; text-align: center; font-weight: 600;">{$supplyChain['avg_risk_score']}</td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>

        <!-- RACCOMANDAZIONI -->
        <div class="section">
            <div class="section-title">Raccomandazioni Prioritarie</div>
            <div class="recommendations">
                <ol>
                    {$recommendationItems}
                </ol>
            </div>
        </div>

        <!-- DISCLAIMER -->
        <div class="section" style="margin-top: 20px;">
            <p style="font-size: 11px; color: #9ca3af; line-height: 1.5; font-style: italic;">
                Il presente report &egrave; generato automaticamente dalla piattaforma {$appName} ed &egrave;
                destinato agli organi di gestione ai sensi dell'Art. 20 della Direttiva NIS2 (UE 2022/2555) e
                del D.Lgs. 138/2024. I dati si riferiscono alla data di generazione e potrebbero non riflettere
                variazioni successive. Si raccomanda una revisione periodica da parte del responsabile compliance.
            </p>
        </div>

        <!-- FOOTER -->
        <div class="footer">
            <div>
                <span class="footer-brand">{$appName}</span>
                <span style="margin-left: 8px;">v{$appVersion}</span>
            </div>
            <div>
                Generato il {$reportDate} alle {$reportTime} | Documento riservato
            </div>
        </div>

        </body>
        </html>
        HTML;

        return $html;
    }

    // ═══════════════════════════════════════════════════════════════════════════
    // EXPORT CSV
    // ═══════════════════════════════════════════════════════════════════════════

    /**
     * Esporta tutti i rischi dell'organizzazione in formato CSV.
     *
     * @param int $orgId ID dell'organizzazione
     * @return string Contenuto CSV con BOM UTF-8
     */
    public function exportRisksCSV(int $orgId): string
    {
        $risks = Database::fetchAll(
            'SELECT r.risk_code, r.title, r.description, r.category,
                    r.threat_source, r.vulnerability,
                    r.likelihood, r.impact, r.inherent_risk_score,
                    r.residual_likelihood, r.residual_impact, r.residual_risk_score,
                    r.treatment, r.status, r.nis2_article, r.review_date,
                    r.created_at,
                    u.full_name AS responsabile
             FROM risks r
             LEFT JOIN users u ON u.id = r.owner_user_id
             WHERE r.organization_id = ?
             ORDER BY r.inherent_risk_score DESC',
            [$orgId]
        );

        $headers = [
            'Codice',
            'Titolo',
            'Descrizione',
            'Categoria',
            'Fonte minaccia',
            'Vulnerabilita',
            'Probabilita',
            'Impatto',
            'Score rischio inerente',
            'Probabilita residua',
            'Impatto residuo',
            'Score rischio residuo',
            'Trattamento',
            'Stato',
            'Articolo NIS2',
            'Data revisione',
            'Data creazione',
            'Responsabile',
        ];

        $rows = [];
        foreach ($risks as $r) {
            $rows[] = [
                $r['risk_code'] ?? '',
                $r['title'] ?? '',
                $r['description'] ?? '',
                $r['category'] ?? '',
                $r['threat_source'] ?? '',
                $r['vulnerability'] ?? '',
                $r['likelihood'] ?? '',
                $r['impact'] ?? '',
                $r['inherent_risk_score'] ?? '',
                $r['residual_likelihood'] ?? '',
                $r['residual_impact'] ?? '',
                $r['residual_risk_score'] ?? '',
                $r['treatment'] ?? '',
                $r['status'] ?? '',
                $r['nis2_article'] ?? '',
                $this->formatDateIT($r['review_date'] ?? ''),
                $this->formatDateTimeIT($r['created_at'] ?? ''),
                $r['responsabile'] ?? '',
            ];
        }

        return $this->buildCSV($headers, $rows);
    }

    /**
     * Esporta tutti gli incidenti dell'organizzazione in formato CSV.
     *
     * @param int $orgId ID dell'organizzazione
     * @return string Contenuto CSV con BOM UTF-8
     */
    public function exportIncidentsCSV(int $orgId): string
    {
        $incidents = Database::fetchAll(
            'SELECT i.incident_code, i.title, i.description, i.classification,
                    i.severity, i.is_significant, i.status,
                    i.detected_at, i.closed_at,
                    i.affected_services, i.affected_users_count,
                    i.cross_border_impact, i.malicious_action,
                    i.root_cause, i.remediation_actions, i.lessons_learned,
                    i.early_warning_due, i.early_warning_sent_at,
                    i.notification_due, i.notification_sent_at,
                    i.final_report_due, i.final_report_sent_at,
                    u1.full_name AS segnalato_da,
                    u2.full_name AS assegnato_a
             FROM incidents i
             LEFT JOIN users u1 ON u1.id = i.reported_by
             LEFT JOIN users u2 ON u2.id = i.assigned_to
             WHERE i.organization_id = ?
             ORDER BY i.detected_at DESC',
            [$orgId]
        );

        $headers = [
            'Codice',
            'Titolo',
            'Descrizione',
            'Classificazione',
            'Severita',
            'Significativo NIS2',
            'Stato',
            'Data rilevamento',
            'Data chiusura',
            'Servizi impattati',
            'Utenti impattati',
            'Impatto transfrontaliero',
            'Azione malevola',
            'Causa radice',
            'Azioni di rimedio',
            'Lezioni apprese',
            'Early warning scadenza',
            'Early warning inviato',
            'Notifica CSIRT scadenza',
            'Notifica CSIRT inviata',
            'Report finale scadenza',
            'Report finale inviato',
            'Segnalato da',
            'Assegnato a',
        ];

        $rows = [];
        foreach ($incidents as $i) {
            $rows[] = [
                $i['incident_code'] ?? '',
                $i['title'] ?? '',
                $i['description'] ?? '',
                $i['classification'] ?? '',
                $i['severity'] ?? '',
                $i['is_significant'] ? 'Si' : 'No',
                $i['status'] ?? '',
                $this->formatDateTimeIT($i['detected_at'] ?? ''),
                $this->formatDateTimeIT($i['closed_at'] ?? ''),
                $i['affected_services'] ?? '',
                $i['affected_users_count'] ?? '',
                $i['cross_border_impact'] ? 'Si' : 'No',
                $i['malicious_action'] ? 'Si' : 'No',
                $i['root_cause'] ?? '',
                $i['remediation_actions'] ?? '',
                $i['lessons_learned'] ?? '',
                $this->formatDateTimeIT($i['early_warning_due'] ?? ''),
                $this->formatDateTimeIT($i['early_warning_sent_at'] ?? ''),
                $this->formatDateTimeIT($i['notification_due'] ?? ''),
                $this->formatDateTimeIT($i['notification_sent_at'] ?? ''),
                $this->formatDateTimeIT($i['final_report_due'] ?? ''),
                $this->formatDateTimeIT($i['final_report_sent_at'] ?? ''),
                $i['segnalato_da'] ?? '',
                $i['assegnato_a'] ?? '',
            ];
        }

        return $this->buildCSV($headers, $rows);
    }

    /**
     * Esporta i controlli di compliance dell'organizzazione in formato CSV.
     *
     * @param int $orgId ID dell'organizzazione
     * @return string Contenuto CSV con BOM UTF-8
     */
    public function exportControlsCSV(int $orgId): string
    {
        $controls = Database::fetchAll(
            'SELECT control_code, title, status, implementation_percentage
             FROM compliance_controls
             WHERE organization_id = ?
             ORDER BY control_code',
            [$orgId]
        );

        $headers = [
            'Codice controllo',
            'Titolo',
            'Stato',
            'Percentuale implementazione',
        ];

        $rows = [];
        foreach ($controls as $c) {
            $rows[] = [
                $c['control_code'] ?? '',
                $c['title'] ?? '',
                $c['status'] ?? '',
                $c['implementation_percentage'] ?? '0',
            ];
        }

        return $this->buildCSV($headers, $rows);
    }

    /**
     * Esporta tutti gli asset dell'organizzazione in formato CSV.
     *
     * @param int $orgId ID dell'organizzazione
     * @return string Contenuto CSV con BOM UTF-8
     */
    public function exportAssetsCSV(int $orgId): string
    {
        $assets = Database::fetchAll(
            'SELECT a.name, a.asset_type, a.category, a.description,
                    a.criticality, a.location, a.ip_address,
                    a.vendor, a.version, a.serial_number,
                    a.purchase_date, a.warranty_expiry, a.status,
                    u.full_name AS responsabile
             FROM assets a
             LEFT JOIN users u ON u.id = a.owner_user_id
             WHERE a.organization_id = ?
             ORDER BY a.criticality DESC, a.name',
            [$orgId]
        );

        $headers = [
            'Nome',
            'Tipo asset',
            'Categoria',
            'Descrizione',
            'Criticita',
            'Ubicazione',
            'Indirizzo IP',
            'Vendor',
            'Versione',
            'Numero seriale',
            'Data acquisto',
            'Scadenza garanzia',
            'Stato',
            'Responsabile',
        ];

        $rows = [];
        foreach ($assets as $a) {
            $rows[] = [
                $a['name'] ?? '',
                $a['asset_type'] ?? '',
                $a['category'] ?? '',
                $a['description'] ?? '',
                $a['criticality'] ?? '',
                $a['location'] ?? '',
                $a['ip_address'] ?? '',
                $a['vendor'] ?? '',
                $a['version'] ?? '',
                $a['serial_number'] ?? '',
                $this->formatDateIT($a['purchase_date'] ?? ''),
                $this->formatDateIT($a['warranty_expiry'] ?? ''),
                $a['status'] ?? '',
                $a['responsabile'] ?? '',
            ];
        }

        return $this->buildCSV($headers, $rows);
    }

    // ═══════════════════════════════════════════════════════════════════════════
    // METODI PRIVATI - COSTRUZIONE CSV
    // ═══════════════════════════════════════════════════════════════════════════

    /**
     * Costruisce il contenuto CSV con BOM UTF-8 e separatore punto e virgola.
     *
     * @param string[] $headers Intestazioni colonne
     * @param array<int, string[]> $rows Righe dati
     * @return string Contenuto CSV completo
     */
    private function buildCSV(array $headers, array $rows): string
    {
        // BOM UTF-8 per compatibilita Excel
        $bom = "\xEF\xBB\xBF";

        $output = fopen('php://temp', 'r+');
        fwrite($output, $bom);

        // Intestazioni
        fputcsv($output, $headers, ';', '"', '\\');

        // Righe dati
        foreach ($rows as $row) {
            fputcsv($output, $row, ';', '"', '\\');
        }

        rewind($output);
        $csv = stream_get_contents($output);
        fclose($output);

        return $csv;
    }

    /**
     * Formatta una data in formato italiano (gg/mm/aaaa).
     */
    private function formatDateIT(string $date): string
    {
        if (empty($date) || $date === '0000-00-00') {
            return '';
        }

        $ts = strtotime($date);
        return $ts ? date('d/m/Y', $ts) : '';
    }

    /**
     * Formatta una data/ora in formato italiano (gg/mm/aaaa HH:ii).
     */
    private function formatDateTimeIT(string $datetime): string
    {
        if (empty($datetime) || str_starts_with($datetime, '0000')) {
            return '';
        }

        $ts = strtotime($datetime);
        return $ts ? date('d/m/Y H:i', $ts) : '';
    }

    // ═══════════════════════════════════════════════════════════════════════════
    // METODI PRIVATI - HTML HELPER
    // ═══════════════════════════════════════════════════════════════════════════

    /**
     * Restituisce il colore corrispondente allo score di compliance.
     */
    private function getScoreColor(float $score): string
    {
        return match (true) {
            $score >= 80 => '#16a34a',   // verde
            $score >= 60 => '#ca8a04',   // giallo scuro
            $score >= 40 => '#ea580c',   // arancione
            default      => '#dc2626',   // rosso
        };
    }

    /**
     * Restituisce l'etichetta testuale dello score di compliance.
     */
    private function getScoreLabel(float $score): string
    {
        return match (true) {
            $score >= 80 => 'Buon livello di compliance',
            $score >= 60 => 'Compliance parziale - interventi necessari',
            $score >= 40 => 'Compliance insufficiente - azioni urgenti',
            $score > 0   => 'Compliance critica - rischio elevato',
            default      => 'Nessun assessment completato',
        };
    }

    /**
     * Genera un badge HTML per una percentuale di raggiungimento.
     */
    private function getStatusBadge(float $percentage): string
    {
        $color = $this->getScoreColor($percentage);
        $label = match (true) {
            $percentage >= 80 => 'Buono',
            $percentage >= 60 => 'Parziale',
            $percentage >= 40 => 'Insufficiente',
            default           => 'Critico',
        };

        return "<span style=\"background: {$color}; color: #fff; padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 600;\">{$label}</span>";
    }

    /**
     * Genera un badge HTML basato su un conteggio (0 = buono, >0 = attenzione).
     */
    private function getCountBadge(int $count): string
    {
        if ($count === 0) {
            return '<span style="background: #16a34a; color: #fff; padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 600;">OK</span>';
        }

        $color = $count >= 3 ? '#dc2626' : '#ea580c';
        return "<span style=\"background: {$color}; color: #fff; padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 600;\">Attenzione</span>";
    }

    /**
     * Somma due valori interi (helper per il template HTML).
     */
    private function sumValues(int $a, int $b): int
    {
        return $a + $b;
    }

    /**
     * Genera la lista di raccomandazioni prioritarie basate sui dati del report.
     *
     * @param array $data Dati completi del report di compliance
     * @return string[] Lista di raccomandazioni
     */
    private function buildRecommendations(array $data): array
    {
        $recs = [];

        $score = $data['assessment']['overall_score'] ?? 0;
        $controls = $data['controls']['stats'];
        $risks = $data['risks'];
        $incidents = $data['incidents'];
        $policies = $data['policies'];
        $training = $data['training'];
        $supplyChain = $data['supply_chain'];

        // Score complessivo basso
        if ($score === null || $score === 0) {
            $recs[] = 'Completare il primo assessment NIS2 per ottenere una baseline di compliance.';
        } elseif ($score < 40) {
            $recs[] = 'Lo score di compliance e critico (' . number_format($score, 1, ',', '.') . '%). Avviare un programma di adeguamento urgente con priorita sui controlli a rischio piu elevato.';
        } elseif ($score < 60) {
            $recs[] = 'Lo score di compliance e insufficiente (' . number_format($score, 1, ',', '.') . '%). Definire un piano di remediation con timeline e responsabili per ogni area carente.';
        }

        // Rischi critici/alti
        $sevDist = $risks['severity_distribution'] ?? [];
        $criticalHigh = ($sevDist['critical'] ?? 0) + ($sevDist['high'] ?? 0);
        if ($criticalHigh > 0) {
            $recs[] = "Sono presenti {$criticalHigh} rischi con severita critica o alta. Prioritizzare il trattamento immediato con piani di mitigazione specifici.";
        }

        // Controlli non avviati
        $notStarted = $controls['not_started'] ?? 0;
        if ($notStarted > 3) {
            $recs[] = "{$notStarted} controlli di sicurezza non sono ancora stati avviati. Assegnare responsabili e definire tempistiche per l'implementazione.";
        }

        // Policy non approvate
        if ($policies['total'] > 0 && $policies['approved'] < $policies['total']) {
            $pending = $policies['total'] - $policies['approved'];
            $recs[] = "{$pending} policy sono in attesa di approvazione. L'Art. 20 NIS2 richiede che gli organi di gestione approvino le misure di sicurezza.";
        } elseif ($policies['total'] === 0) {
            $recs[] = 'Nessuna policy di sicurezza presente. Creare e approvare le policy fondamentali (sicurezza informazioni, gestione incidenti, continuita operativa).';
        }

        // Formazione
        $trainingRate = $training['completion_rate'] ?? 0;
        if ($trainingRate < 50) {
            $recs[] = 'Il tasso di completamento della formazione e basso (' . number_format($trainingRate, 1, ',', '.') . '%). L\'Art. 20 NIS2 richiede formazione obbligatoria per organi di gestione e personale.';
        }

        $overdueTraining = $training['overdue'] ?? 0;
        if ($overdueTraining > 0) {
            $recs[] = "{$overdueTraining} assegnazioni formative sono scadute. Sollecitare il completamento per garantire la conformita.";
        }

        // Supply chain
        if ($supplyChain['non_compliant'] > 0) {
            $recs[] = "{$supplyChain['non_compliant']} fornitori non soddisfano i requisiti di sicurezza (Art. 21.2.d NIS2). Avviare assessment e definire requisiti contrattuali.";
        }

        // Incidenti significativi
        if ($incidents['significant'] > 0 && $incidents['open'] > 0) {
            $recs[] = "Sono presenti {$incidents['open']} incidenti attivi di cui {$incidents['significant']} significativi. Verificare il rispetto delle scadenze di notifica CSIRT (Art. 23 NIS2: 24h/72h/30gg).";
        }

        // Se tutto sembra a posto
        if (empty($recs)) {
            $recs[] = 'Il livello di compliance e complessivamente buono. Continuare con il monitoraggio continuo e le revisioni periodiche.';
            $recs[] = 'Pianificare il prossimo assessment per verificare il mantenimento dello score.';
        }

        return $recs;
    }

    /**
     * Costruisce una pagina HTML di errore minimale.
     */
    private function buildErrorHtml(string $message): string
    {
        $escapedMessage = htmlspecialchars($message, ENT_QUOTES, 'UTF-8');
        $appName = defined('APP_NAME') ? APP_NAME : 'NIS2 Agile';

        return <<<HTML
        <!DOCTYPE html>
        <html lang="it">
        <head>
            <meta charset="UTF-8">
            <title>Errore Report - {$appName}</title>
            <style>
                body {
                    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
                    display: flex; align-items: center; justify-content: center;
                    min-height: 100vh; margin: 0; background: #f8fafc;
                }
                .error-box {
                    background: #fff; border: 1px solid #fecaca; border-left: 4px solid #dc2626;
                    border-radius: 0 8px 8px 0; padding: 24px 32px; max-width: 500px;
                }
                .error-box h2 { color: #dc2626; margin-bottom: 8px; font-size: 18px; }
                .error-box p { color: #374151; }
            </style>
        </head>
        <body>
            <div class="error-box">
                <h2>Errore generazione report</h2>
                <p>{$escapedMessage}</p>
            </div>
        </body>
        </html>
        HTML;
    }
}
