<?php
/**
 * NIS2 Agile - Dashboard Controller
 *
 * Overview di compliance, score, scadenze, attività recenti.
 */

require_once __DIR__ . '/BaseController.php';

class DashboardController extends BaseController
{
    /**
     * GET /api/dashboard/overview
     */
    public function overview(): void
    {
        $this->requireOrgAccess();
        $orgId = $this->getCurrentOrgId();

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

        // Compliance controls status
        $controlStats = Database::fetchAll(
            'SELECT status, COUNT(*) as count
             FROM compliance_controls
             WHERE organization_id = ?
             GROUP BY status',
            [$orgId]
        );

        // Rischi aperti
        $riskCounts = Database::fetchOne(
            'SELECT
                SUM(CASE WHEN inherent_risk_score >= 20 THEN 1 ELSE 0 END) as critical_high,
                SUM(CASE WHEN inherent_risk_score BETWEEN 10 AND 19 THEN 1 ELSE 0 END) as medium,
                SUM(CASE WHEN inherent_risk_score < 10 THEN 1 ELSE 0 END) as low,
                COUNT(*) as total
             FROM risks
             WHERE organization_id = ? AND status != "closed"',
            [$orgId]
        );

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

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

        // Fornitori critici
        $criticalSuppliers = Database::count(
            'suppliers',
            'organization_id = ? AND criticality IN ("high", "critical") AND security_requirements_met = 0',
            [$orgId]
        );

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

        // Conteggi asset
        $assetCount = Database::count('assets', 'organization_id = ? AND status = "active"', [$orgId]);

        // Organizzazione info
        $org = Database::fetchOne('SELECT name, sector, entity_type, subscription_plan FROM organizations WHERE id = ?', [$orgId]);

        $this->jsonSuccess([
            'organization'      => $org,
            'last_assessment'   => $lastAssessment,
            'compliance_score'  => $lastAssessment ? (float) $lastAssessment['overall_score'] : null,
            'controls'          => $controlStats,
            'risks'             => $riskCounts,
            'active_incidents'  => $activeIncidents,
            'policies'          => $policyStats,
            'critical_suppliers' => $criticalSuppliers,
            'training'          => $trainingStats,
            'asset_count'       => $assetCount,
        ]);
    }

    /**
     * GET /api/dashboard/compliance-score
     */
    public function complianceScore(): void
    {
        $this->requireOrgAccess();
        $orgId = $this->getCurrentOrgId();

        // Score dall'ultimo assessment
        $assessments = Database::fetchAll(
            'SELECT id, title, overall_score, category_scores, completed_at
             FROM assessments
             WHERE organization_id = ? AND status = "completed"
             ORDER BY completed_at DESC
             LIMIT 5',
            [$orgId]
        );

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

        $totalControls = count($controls);
        $implementedControls = 0;
        $avgImplementation = 0;

        foreach ($controls as $c) {
            if ($c['status'] === 'implemented' || $c['status'] === 'verified') {
                $implementedControls++;
            }
            $avgImplementation += (int) $c['implementation_percentage'];
        }

        $avgImplementation = $totalControls > 0 ? round($avgImplementation / $totalControls) : 0;

        $this->jsonSuccess([
            'assessments'             => $assessments,
            'controls'                => $controls,
            'total_controls'          => $totalControls,
            'implemented_controls'    => $implementedControls,
            'avg_implementation'      => $avgImplementation,
        ]);
    }

    /**
     * GET /api/dashboard/sectorBenchmark
     * Benchmark settoriale ANONIMIZZATO (P2): confronta lo score di compliance
     * dell'organizzazione con la media/distribuzione del proprio settore sulla
     * rete multi-tenant. k-anonymity: aggregati solo se >= MIN_PEERS organizzazioni.
     */
    public function sectorBenchmark(): void
    {
        $this->requireOrgAccess();
        $orgId = $this->getCurrentOrgId();
        $MIN_PEERS = 3;

        $org = Database::fetchOne('SELECT sector FROM organizations WHERE id = ?', [$orgId]);
        if (!$org) {
            $this->jsonError('Organizzazione non trovata', 404, 'NOT_FOUND');
        }
        $sector = $org['sector'];

        $mine = Database::fetchOne(
            "SELECT overall_score FROM assessments WHERE organization_id = ? AND status='completed'
             ORDER BY completed_at DESC LIMIT 1",
            [$orgId]
        );
        $myScore = $mine ? (float) $mine['overall_score'] : null;

        // Esattamente UN punteggio per org (ultimo completato, tie-break su id): la subquery
        // correlata evita il duplicato che il JOIN su MAX(completed_at) produceva quando due
        // assessment della stessa org condividono identico timestamp.
        $rows = Database::fetchAll(
            "SELECT a.overall_score
             FROM assessments a
             JOIN organizations o ON o.id = a.organization_id
             WHERE o.is_active = 1 AND o.sector = ? AND a.status='completed' AND a.overall_score IS NOT NULL
               AND a.id = (
                   SELECT a2.id FROM assessments a2
                   WHERE a2.organization_id = a.organization_id
                     AND a2.status='completed' AND a2.overall_score IS NOT NULL
                   ORDER BY a2.completed_at DESC, a2.id DESC LIMIT 1
               )",
            [$sector]
        );
        $scores = array_map(fn($r) => (float) $r['overall_score'], $rows);
        $n = count($scores);

        if ($n < $MIN_PEERS) {
            $this->jsonSuccess([
                'sector'             => $sector,
                'sufficient_data'    => false,
                'min_peers_required' => $MIN_PEERS,
                'peers_available'    => $n,
                'my_score'           => $myScore,
                'message'            => 'Dati insufficienti per un benchmark anonimo (servono almeno ' . $MIN_PEERS . ' organizzazioni nel settore).',
            ]);
            return;
        }

        sort($scores);
        $avg = array_sum($scores) / $n;
        $median = $n % 2 ? $scores[intdiv($n, 2)] : ($scores[$n / 2 - 1] + $scores[$n / 2]) / 2;
        $p25 = $scores[(int) floor(0.25 * ($n - 1))];
        $p75 = $scores[(int) floor(0.75 * ($n - 1))];

        $percentile = null; $position = null;
        if ($myScore !== null) {
            $below = count(array_filter($scores, fn($s) => $s < $myScore));
            $percentile = (int) round($below / $n * 100);
            $position = $myScore >= $p75 ? 'top_quartile'
                : ($myScore >= $median ? 'above_median'
                : ($myScore >= $p25 ? 'below_median' : 'bottom_quartile'));
        }

        $this->jsonSuccess([
            'sector'           => $sector,
            'sufficient_data'  => true,
            'peers'            => $n,
            'my_score'         => $myScore !== null ? round($myScore, 1) : null,
            'sector_average'   => round($avg, 1),
            'sector_median'    => round($median, 1),
            'sector_min'       => round($scores[0], 1),
            'sector_max'       => round($scores[$n - 1], 1),
            'sector_p25'       => round($p25, 1),
            'sector_p75'       => round($p75, 1),
            'my_percentile'    => $percentile,
            'my_position'      => $position,
            'delta_vs_average' => $myScore !== null ? round($myScore - $avg, 1) : null,
        ]);
    }

    /**
     * GET /api/dashboard/upcoming-deadlines
     */
    public function deadlines(): void
    {
        $this->requireOrgAccess();
        $orgId = $this->getCurrentOrgId();

        $deadlines = [];

        // Incidenti con scadenze notifica
        $incidentDeadlines = Database::fetchAll(
            'SELECT id, incident_code, title, severity,
                    early_warning_due, early_warning_sent_at,
                    notification_due, notification_sent_at,
                    final_report_due, final_report_sent_at
             FROM incidents
             WHERE organization_id = ? AND is_significant = 1 AND status NOT IN ("closed", "post_mortem")
             ORDER BY detected_at DESC',
            [$orgId]
        );

        foreach ($incidentDeadlines as $inc) {
            if ($inc['early_warning_due'] && !$inc['early_warning_sent_at']) {
                $deadlines[] = [
                    'type'     => 'incident_early_warning',
                    'title'    => "Early Warning: {$inc['title']}",
                    'due_date' => $inc['early_warning_due'],
                    'severity' => 'critical',
                    'entity_type' => 'incident',
                    'entity_id'   => $inc['id'],
                ];
            }
            if ($inc['notification_due'] && !$inc['notification_sent_at']) {
                $deadlines[] = [
                    'type'     => 'incident_notification',
                    'title'    => "Notifica CSIRT: {$inc['title']}",
                    'due_date' => $inc['notification_due'],
                    'severity' => 'high',
                    'entity_type' => 'incident',
                    'entity_id'   => $inc['id'],
                ];
            }
            if ($inc['final_report_due'] && !$inc['final_report_sent_at']) {
                $deadlines[] = [
                    'type'     => 'incident_final_report',
                    'title'    => "Report finale: {$inc['title']}",
                    'due_date' => $inc['final_report_due'],
                    'severity' => 'medium',
                    'entity_type' => 'incident',
                    'entity_id'   => $inc['id'],
                ];
            }
        }

        // Policy in scadenza revisione
        $policyDeadlines = Database::fetchAll(
            'SELECT id, title, next_review_date
             FROM policies
             WHERE organization_id = ? AND next_review_date IS NOT NULL
                   AND next_review_date <= DATE_ADD(NOW(), INTERVAL 30 DAY)
                   AND status NOT IN ("archived")
             ORDER BY next_review_date',
            [$orgId]
        );

        foreach ($policyDeadlines as $p) {
            $deadlines[] = [
                'type'     => 'policy_review',
                'title'    => "Revisione policy: {$p['title']}",
                'due_date' => $p['next_review_date'],
                'severity' => 'medium',
                'entity_type' => 'policy',
                'entity_id'   => $p['id'],
            ];
        }

        // Risk treatments in scadenza
        $treatmentDeadlines = Database::fetchAll(
            'SELECT rt.id, rt.action_description, rt.due_date, r.title as risk_title
             FROM risk_treatments rt
             JOIN risks r ON r.id = rt.risk_id
             WHERE r.organization_id = ? AND rt.status IN ("planned", "in_progress")
                   AND rt.due_date IS NOT NULL AND rt.due_date <= DATE_ADD(NOW(), INTERVAL 30 DAY)
             ORDER BY rt.due_date',
            [$orgId]
        );

        foreach ($treatmentDeadlines as $t) {
            $deadlines[] = [
                'type'     => 'risk_treatment',
                'title'    => "Trattamento rischio: {$t['risk_title']}",
                'due_date' => $t['due_date'],
                'severity' => 'medium',
                'entity_type' => 'risk_treatment',
                'entity_id'   => $t['id'],
            ];
        }

        // Training in scadenza
        $trainingDeadlines = Database::fetchAll(
            'SELECT ta.id, tc.title, ta.due_date, u.full_name
             FROM training_assignments ta
             JOIN training_courses tc ON tc.id = ta.course_id
             JOIN users u ON u.id = ta.user_id
             WHERE ta.organization_id = ? AND ta.status IN ("assigned", "in_progress")
                   AND ta.due_date IS NOT NULL AND ta.due_date <= DATE_ADD(NOW(), INTERVAL 30 DAY)
             ORDER BY ta.due_date',
            [$orgId]
        );

        foreach ($trainingDeadlines as $t) {
            $deadlines[] = [
                'type'     => 'training_due',
                'title'    => "Formazione: {$t['title']} - {$t['full_name']}",
                'due_date' => $t['due_date'],
                'severity' => 'low',
                'entity_type' => 'training',
                'entity_id'   => $t['id'],
            ];
        }

        // Ordina per data
        usort($deadlines, fn($a, $b) => strcmp($a['due_date'], $b['due_date']));

        $this->jsonSuccess($deadlines);
    }

    /**
     * GET /api/dashboard/recent-activity
     */
    public function recentActivity(): void
    {
        $this->requireOrgAccess();

        $activities = Database::fetchAll(
            'SELECT al.*, u.full_name
             FROM audit_logs al
             LEFT JOIN users u ON u.id = al.user_id
             WHERE al.organization_id = ?
             ORDER BY al.created_at DESC
             LIMIT 20',
            [$this->getCurrentOrgId()]
        );

        $this->jsonSuccess($activities);
    }

    /**
     * GET /api/dashboard/risk-heatmap
     */
    public function riskHeatmap(): void
    {
        $this->requireOrgAccess();

        $risks = Database::fetchAll(
            'SELECT id, title, category, likelihood, impact, inherent_risk_score, status
             FROM risks
             WHERE organization_id = ? AND status != "closed"
             ORDER BY inherent_risk_score DESC',
            [$this->getCurrentOrgId()]
        );

        // Costruisci matrice 5x5
        $matrix = [];
        for ($l = 1; $l <= 5; $l++) {
            for ($i = 1; $i <= 5; $i++) {
                $matrix["{$l}_{$i}"] = [];
            }
        }

        foreach ($risks as $risk) {
            if ($risk['likelihood'] && $risk['impact']) {
                $key = "{$risk['likelihood']}_{$risk['impact']}";
                $matrix[$key][] = [
                    'id'    => $risk['id'],
                    'title' => $risk['title'],
                    'score' => $risk['inherent_risk_score'],
                ];
            }
        }

        $this->jsonSuccess([
            'risks'  => $risks,
            'matrix' => $matrix,
        ]);
    }
}
