<?php
/**
 * NIS2 Agile - Cross-Organization AI Analysis Controller (L4)
 *
 * Analisi aggregata multi-organizzazione per consulenti e super admin.
 * PRIVACY: nessun dato identificativo di singole org viene inviato ad Anthropic.
 * K-anonymity: minimo 3 organizzazioni per qualsiasi query.
 *
 * Endpoint:
 *   POST /api/cross-analysis/analyze   — esegue analisi AI
 *   GET  /api/cross-analysis/history   — storico query del consulente
 */

require_once __DIR__ . '/BaseController.php';
require_once APP_PATH . '/services/AIService.php';

class CrossAnalysisController extends BaseController
{
    /** Ruoli autorizzati a usare L4 */
    private const ALLOWED_ROLES = ['consultant', 'super_admin'];

    /** K-anonymity: minimo organizzazioni per permettere l'analisi */
    private const MIN_ORGS = 2; // abbassato a 2 per usabilità iniziale (modificare a 3 in produzione)

    // ─────────────────────────────────────────────────────────────────
    // POST /api/cross-analysis/analyze
    // ─────────────────────────────────────────────────────────────────

    public function analyze(): void
    {
        $this->requireAuth();
        $this->requireCrossRole();

        $question = trim($this->getParam('question', ''));
        if (empty($question)) {
            $this->jsonError('question è obbligatorio', 400, 'MISSING_QUESTION');
        }
        if (strlen($question) > 1000) {
            $this->jsonError('Domanda troppo lunga (max 1000 caratteri)', 400, 'QUESTION_TOO_LONG');
        }

        // Ottieni org_ids accessibili all'utente
        $orgIds = $this->getAccessibleOrgIds();

        if (count($orgIds) < self::MIN_ORGS) {
            $this->jsonError(
                'Analisi cross-org richiede almeno ' . self::MIN_ORGS . ' organizzazioni nel tuo portfolio (hai ' . count($orgIds) . ').',
                422,
                'INSUFFICIENT_ORGS'
            );
        }

        // Costruisci dati aggregati (solo statistiche, nessun dato identificativo)
        $aggregated = $this->buildAggregatedData($orgIds);

        // Chiama AI
        try {
            $ai = new AIService();
            $result = $ai->crossOrgAnalysis(count($orgIds), $aggregated, $question);
        } catch (RuntimeException $e) {
            $this->jsonError('Errore AI: ' . $e->getMessage(), 503, 'AI_ERROR');
        }

        // Audit log obbligatorio (log query, NON i dati restituiti)
        $this->logCrossQuery($orgIds, $question, $result);

        $this->jsonSuccess([
            'result'    => $result,
            'org_count' => count($orgIds),
            'context'   => [
                'sectors'      => array_keys($aggregated['by_sector'] ?? []),
                'entity_types' => array_keys($aggregated['by_entity_type'] ?? []),
            ],
        ], 'Analisi completata');
    }

    // ─────────────────────────────────────────────────────────────────
    // GET /api/cross-analysis/history
    // ─────────────────────────────────────────────────────────────────

    public function history(): void
    {
        $this->requireAuth();
        $this->requireCrossRole();

        $userId = $this->getCurrentUserId();
        $limit  = min((int)$this->getParam('limit', 20), 50);

        $rows = Database::fetchAll(
            "SELECT id, interaction_type, prompt_summary, response_summary, tokens_used, created_at
             FROM ai_interactions
             WHERE user_id = ? AND interaction_type = 'cross_org_analysis'
             ORDER BY created_at DESC
             LIMIT ?",
            [$userId, $limit]
        );

        $this->jsonSuccess(['history' => $rows, 'total' => count($rows)]);
    }

    // ─────────────────────────────────────────────────────────────────
    // GET /api/cross-analysis/portfolio
    // Restituisce statistiche aggregate del portfolio (senza AI)
    // ─────────────────────────────────────────────────────────────────

    public function portfolio(): void
    {
        $this->requireAuth();
        $this->requireCrossRole();

        $orgIds = $this->getAccessibleOrgIds();

        if (empty($orgIds)) {
            $this->jsonSuccess(['orgs' => [], 'aggregated' => null]);
        }

        $aggregated = $this->buildAggregatedData($orgIds);

        $this->jsonSuccess([
            'org_count'  => count($orgIds),
            'aggregated' => $aggregated,
        ]);
    }

    // ─────────────────────────────────────────────────────────────────
    // PRIVATE — autorizzazione
    // ─────────────────────────────────────────────────────────────────

    private function requireCrossRole(): void
    {
        $user = $this->getCurrentUser();
        if (!$user) {
            $this->jsonError('Non autenticato', 401, 'UNAUTHENTICATED');
        }

        $role = $user['role'] ?? '';
        if (!in_array($role, self::ALLOWED_ROLES, true)) {
            $this->jsonError(
                'Funzione riservata a Consulenti e Super Admin',
                403,
                'INSUFFICIENT_ROLE'
            );
        }
    }

    // ─────────────────────────────────────────────────────────────────
    // PRIVATE — org IDs accessibili
    // ─────────────────────────────────────────────────────────────────

    private function getAccessibleOrgIds(): array
    {
        $user = $this->getCurrentUser();
        $userId = (int)$user['id'];
        $role   = $user['role'] ?? '';

        if ($role === 'super_admin') {
            // Super admin vede tutte le org attive
            $rows = Database::fetchAll(
                'SELECT id FROM organizations WHERE deleted_at IS NULL ORDER BY id',
                []
            );
        } else {
            // Consultant: solo org in cui è membro
            $rows = Database::fetchAll(
                'SELECT uo.organization_id AS id
                 FROM user_organizations uo
                 JOIN organizations o ON o.id = uo.organization_id
                 WHERE uo.user_id = ? AND o.deleted_at IS NULL',
                [$userId]
            );
        }

        return array_column($rows, 'id');
    }

    // ─────────────────────────────────────────────────────────────────
    // PRIVATE — aggregazione dati (NESSUN NOME ORG, NESSUN PII)
    // ─────────────────────────────────────────────────────────────────

    private function buildAggregatedData(array $orgIds): array
    {
        $in = implode(',', array_fill(0, count($orgIds), '?'));

        // 1. Distribuzione settoriale
        $sectorRows = Database::fetchAll(
            "SELECT sector, COUNT(*) as cnt FROM organizations WHERE id IN ({$in}) GROUP BY sector",
            $orgIds
        );
        $bySector = [];
        foreach ($sectorRows as $r) {
            $bySector[$r['sector'] ?: 'Non specificato'] = (int)$r['cnt'];
        }

        // 2. Classificazione NIS2
        $entityRows = Database::fetchAll(
            "SELECT entity_type, COUNT(*) as cnt FROM organizations WHERE id IN ({$in}) GROUP BY entity_type",
            $orgIds
        );
        $byEntityType = [];
        foreach ($entityRows as $r) {
            $byEntityType[$r['entity_type'] ?: 'Non classificato'] = (int)$r['cnt'];
        }

        // 3. Compliance score medio (da assessments completati, uno per org)
        $scoreRows = Database::fetchAll(
            "SELECT a.organization_id, AVG(ar.compliance_level) as avg_score
             FROM assessments a
             JOIN assessment_responses ar ON ar.assessment_id = a.id
             WHERE a.organization_id IN ({$in}) AND a.status = 'completed'
             GROUP BY a.organization_id",
            $orgIds
        );
        $scores = array_column($scoreRows, 'avg_score');
        $avgScore = count($scores) > 0 ? round(array_sum($scores) / count($scores) * 100, 1) : null;

        // Distribuzione score
        $scoreDist = ['0-20%' => 0, '21-40%' => 0, '41-60%' => 0, '61-80%' => 0, '81-100%' => 0];
        foreach ($scores as $s) {
            $pct = $s * 100;
            if ($pct <= 20)      $scoreDist['0-20%']++;
            elseif ($pct <= 40)  $scoreDist['21-40%']++;
            elseif ($pct <= 60)  $scoreDist['41-60%']++;
            elseif ($pct <= 80)  $scoreDist['61-80%']++;
            else                 $scoreDist['81-100%']++;
        }

        // 4. Assessment: org con assessment completato + score medio per categoria
        $completedOrgs = count($scoreRows);
        $catRows = Database::fetchAll(
            "SELECT ar.category,
                    AVG(CASE ar.response_value
                        WHEN 'implemented'     THEN 100
                        WHEN 'partial'         THEN 50
                        WHEN 'not_implemented' THEN 0
                        ELSE NULL END) AS avg_score
             FROM assessments a
             JOIN assessment_responses ar ON ar.assessment_id = a.id
             WHERE a.organization_id IN ({$in}) AND a.status = 'completed'
               AND ar.response_value != 'not_applicable'
             GROUP BY ar.category",
            $orgIds
        );
        $avgByCategory = [];
        foreach ($catRows as $r) {
            if ($r['category']) {
                $avgByCategory[$r['category']] = round((float)$r['avg_score'], 1);
            }
        }
        asort($avgByCategory); // ordinati dal più basso

        // 5. Rischi
        $riskRows = Database::fetchAll(
            "SELECT severity, COUNT(*) as cnt
             FROM risks
             WHERE organization_id IN ({$in}) AND deleted_at IS NULL AND status != 'closed'
             GROUP BY severity",
            $orgIds
        );
        $bySeverity = [];
        $totalRisks = 0;
        foreach ($riskRows as $r) {
            $bySeverity[$r['severity']] = (int)$r['cnt'];
            $totalRisks += (int)$r['cnt'];
        }

        $risksWithTreat = Database::fetchOne(
            "SELECT COUNT(DISTINCT r.id) as cnt
             FROM risks r
             JOIN risk_treatments rt ON rt.risk_id = r.id
             WHERE r.organization_id IN ({$in}) AND r.deleted_at IS NULL",
            $orgIds
        );
        $pctWithTreatment = $totalRisks > 0
            ? round((int)($risksWithTreat['cnt'] ?? 0) / $totalRisks * 100)
            : 0;

        // 6. Policy
        $polRows = Database::fetchAll(
            "SELECT organization_id, COUNT(*) as approved_count
             FROM policies
             WHERE organization_id IN ({$in}) AND status IN ('approved','published') AND deleted_at IS NULL
             GROUP BY organization_id",
            $orgIds
        );
        $approvedCounts = array_column($polRows, 'approved_count');
        $avgApproved = count($approvedCounts) > 0
            ? round(array_sum($approvedCounts) / count($orgIds), 1)
            : 0;
        $withoutApproved = count($orgIds) - count($approvedCounts);

        // 7. Formazione
        $trainRows = Database::fetchAll(
            "SELECT organization_id,
                    SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as done,
                    COUNT(*) as total
             FROM training_assignments
             WHERE organization_id IN ({$in})
             GROUP BY organization_id",
            $orgIds
        );
        $completionRates = [];
        $below50 = 0;
        foreach ($trainRows as $r) {
            $rate = $r['total'] > 0 ? round($r['done'] / $r['total'] * 100) : 0;
            $completionRates[] = $rate;
            if ($rate < 50) $below50++;
        }
        $avgTraining = count($completionRates) > 0
            ? round(array_sum($completionRates) / count($completionRates))
            : 0;

        // 8. Controlli
        $ctrlRows = Database::fetchAll(
            "SELECT organization_id,
                    SUM(CASE WHEN implementation_status = 'implemented' THEN 1 ELSE 0 END) as impl,
                    COUNT(*) as total
             FROM compliance_controls
             WHERE organization_id IN ({$in})
             GROUP BY organization_id",
            $orgIds
        );
        $ctrlRates = [];
        $weakestCats = [];
        foreach ($ctrlRows as $r) {
            $ctrlRates[] = $r['total'] > 0 ? round($r['impl'] / $r['total'] * 100) : 0;
        }
        $avgCtrl = count($ctrlRates) > 0
            ? round(array_sum($ctrlRates) / count($ctrlRates))
            : 0;

        // Categorie controlli più deboli (aggregate)
        $weakRows = Database::fetchAll(
            "SELECT category,
                    AVG(CASE WHEN implementation_status = 'implemented' THEN 100 ELSE 0 END) as rate
             FROM compliance_controls
             WHERE organization_id IN ({$in})
             GROUP BY category
             ORDER BY rate ASC
             LIMIT 3",
            $orgIds
        );
        $weakestCats = array_column($weakRows, 'category');

        // 9. Incidenti (ultimi 12 mesi)
        $incRows = Database::fetchAll(
            "SELECT severity, COUNT(*) as cnt
             FROM incidents
             WHERE organization_id IN ({$in})
               AND created_at >= DATE_SUB(NOW(), INTERVAL 12 MONTH)
             GROUP BY severity",
            $orgIds
        );
        $incBySeverity = [];
        $totalInc = 0;
        foreach ($incRows as $r) {
            $incBySeverity[$r['severity']] = (int)$r['cnt'];
            $totalInc += (int)$r['cnt'];
        }
        $orgsWithInc = Database::fetchOne(
            "SELECT COUNT(DISTINCT organization_id) as cnt
             FROM incidents
             WHERE organization_id IN ({$in})
               AND created_at >= DATE_SUB(NOW(), INTERVAL 12 MONTH)",
            $orgIds
        );

        return [
            'by_sector'           => $bySector,
            'by_entity_type'      => $byEntityType,
            'avg_compliance_score'=> $avgScore,
            'score_distribution'  => $scoreDist,
            'assessments' => [
                'with_completed'  => $completedOrgs,
                'avg_by_category' => $avgByCategory,
            ],
            'risks' => [
                'total_open'         => $totalRisks,
                'avg_per_org'        => $orgIds ? round($totalRisks / count($orgIds), 1) : 0,
                'by_severity'        => $bySeverity,
                'pct_with_treatment' => $pctWithTreatment,
            ],
            'policies' => [
                'avg_approved'    => $avgApproved,
                'without_approved'=> $withoutApproved,
            ],
            'training' => [
                'avg_completion_rate' => $avgTraining,
                'below_50pct'         => $below50,
            ],
            'controls' => [
                'avg_implementation'  => $avgCtrl,
                'weakest_categories'  => $weakestCats,
            ],
            'incidents' => [
                'total'              => $totalInc,
                'orgs_with_incidents'=> (int)($orgsWithInc['cnt'] ?? 0),
                'by_severity'        => $incBySeverity,
            ],
        ];
    }

    // ─────────────────────────────────────────────────────────────────
    // PRIVATE — audit log
    // ─────────────────────────────────────────────────────────────────

    private function logCrossQuery(array $orgIds, string $question, array $result): void
    {
        // Log nella tabella ai_interactions
        // org_id = 0 (query cross-org, non legata a singola org)
        // prompt_summary contiene la domanda (non i dati aggregati per sicurezza)
        // response_summary contiene un estratto della risposta
        $responseSummary = isset($result['answer'])
            ? substr($result['answer'], 0, 500)
            : substr(json_encode($result), 0, 500);

        $orgIdsStr = implode(',', $orgIds);

        Database::insert('ai_interactions', [
            'organization_id'  => 0,
            'user_id'          => $this->getCurrentUserId(),
            'interaction_type' => 'cross_org_analysis',
            'prompt_summary'   => "CROSS_ORG [{$orgIdsStr}]: " . substr($question, 0, 400),
            'response_summary' => $responseSummary,
            'tokens_used'      => 0,
            'model_used'       => ANTHROPIC_MODEL,
        ]);
    }
}
