<?php
/**
 * NIS2 Agile - Normative Controller
 *
 * Feed aggiornamenti normativi NIS2/ACN/DORA con ACK tracciato per audit.
 * Le organizzazioni devono documentare la presa visione degli aggiornamenti
 * normativi per dimostrare compliance continuativa.
 *
 * Endpoint:
 *   GET    /api/normative/list          → lista aggiornamenti (filtrabili)
 *   GET    /api/normative/{id}          → dettaglio aggiornamento
 *   POST   /api/normative/{id}/ack      → conferma presa visione (con note)
 *   GET    /api/normative/pending       → aggiornamenti non ancora ACK dall'org
 *   GET    /api/normative/stats         → statistiche ACK per dashboard
 *   POST   /api/normative/create        → crea aggiornamento (solo super_admin)
 */

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

class NormativeController extends BaseController
{
    /**
     * GET /api/normative/list
     */
    public function list(): void
    {
        $this->requireOrgAccess();
        $orgId = $this->getCurrentOrgId();

        $conditions = ['nu.is_published = 1'];
        $params = [];

        if ($this->hasParam('source')) {
            $conditions[] = 'nu.source = ?';
            $params[] = $this->getParam('source');
        }
        if ($this->hasParam('impact')) {
            $conditions[] = 'nu.impact_level = ?';
            $params[] = $this->getParam('impact');
        }
        if ($this->hasParam('action_required')) {
            $conditions[] = 'nu.action_required = ?';
            $params[] = (int)$this->getParam('action_required');
        }

        $where = implode(' AND ', $conditions);
        $updates = Database::fetchAll(
            "SELECT nu.*,
                    na.acknowledged_at,
                    na.acknowledged_by,
                    u.full_name as ack_by_name
             FROM normative_updates nu
             LEFT JOIN normative_ack na ON na.normative_update_id = nu.id AND na.organization_id = ?
             LEFT JOIN users u ON u.id = na.acknowledged_by
             WHERE {$where}
             ORDER BY
                CASE nu.impact_level WHEN 'critical' THEN 1 WHEN 'high' THEN 2 WHEN 'medium' THEN 3 WHEN 'low' THEN 4 ELSE 5 END,
                nu.published_at DESC",
            array_merge([$orgId], $params)
        );

        // Decodifica affected_domains
        foreach ($updates as &$u) {
            $u['affected_domains'] = json_decode($u['affected_domains'] ?? '[]', true) ?? [];
            $u['is_acknowledged']  = !empty($u['acknowledged_at']);
        }
        unset($u);

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

    /**
     * GET /api/normative/{id}
     */
    public function get(int $id): void
    {
        $this->requireOrgAccess();
        $orgId = $this->getCurrentOrgId();

        $update = Database::fetchOne(
            'SELECT nu.*,
                    na.acknowledged_at, na.acknowledged_by, na.notes as ack_notes,
                    u.full_name as ack_by_name
             FROM normative_updates nu
             LEFT JOIN normative_ack na ON na.normative_update_id = nu.id AND na.organization_id = ?
             LEFT JOIN users u ON u.id = na.acknowledged_by
             WHERE nu.id = ? AND nu.is_published = 1',
            [$orgId, $id]
        );

        if (!$update) {
            $this->jsonError('Aggiornamento non trovato', 404, 'NOT_FOUND');
        }

        $update['affected_domains'] = json_decode($update['affected_domains'] ?? '[]', true) ?? [];
        $update['is_acknowledged']  = !empty($update['acknowledged_at']);

        $this->jsonSuccess($update);
    }

    /**
     * POST /api/normative/{id}/ack
     * Documenta la presa visione dell'aggiornamento normativo.
     */
    public function acknowledge(int $id): void
    {
        $this->requireOrgRole(['org_admin', 'compliance_manager']);
        $orgId = $this->getCurrentOrgId();

        $update = Database::fetchOne(
            'SELECT id, title, impact_level FROM normative_updates WHERE id = ? AND is_published = 1',
            [$id]
        );
        if (!$update) { $this->jsonError('Aggiornamento non trovato', 404, 'NOT_FOUND'); }

        // Verifica se già ACK
        $existing = Database::fetchOne(
            'SELECT id FROM normative_ack WHERE normative_update_id = ? AND organization_id = ?',
            [$id, $orgId]
        );

        $notes = trim($this->getParam('notes', ''));

        if ($existing) {
            // Aggiorna note se già ACK
            Database::query(
                'UPDATE normative_ack SET notes = ?, acknowledged_by = ?, acknowledged_at = NOW()
                 WHERE normative_update_id = ? AND organization_id = ?',
                [$notes ?: null, $this->getCurrentUserId(), $id, $orgId]
            );
        } else {
            Database::insert('normative_ack', [
                'normative_update_id' => $id,
                'organization_id'     => $orgId,
                'acknowledged_by'     => $this->getCurrentUserId(),
                'notes'               => $notes ?: null,
            ]);
        }

        $this->logAudit('normative_acknowledged', 'normative_update', $id, [
            'title'  => $update['title'],
            'impact' => $update['impact_level'],
        ]);

        // Dispatch webhook
        try {
            (new WebhookService())->dispatch($orgId, 'normative.update', [
                'id'             => $update['id'],
                'title'          => $update['title'],
                'impact_level'   => $update['impact_level'],
                'acknowledged'   => true,
                'acknowledged_at' => date('c'),
            ]);
        } catch (Throwable $e) {
            error_log('[WEBHOOK] dispatch error: ' . $e->getMessage());
        }

        $this->jsonSuccess(['acknowledged_at' => date('c')], 'Presa visione registrata');
    }

    /**
     * GET /api/normative/pending
     * Aggiornamenti non ancora ACK dall'organizzazione corrente.
     */
    public function pending(): void
    {
        $this->requireOrgAccess();
        $orgId = $this->getCurrentOrgId();

        $pending = Database::fetchAll(
            'SELECT nu.id, nu.title, nu.source, nu.impact_level, nu.action_required,
                    nu.effective_date, nu.published_at
             FROM normative_updates nu
             LEFT JOIN normative_ack na ON na.normative_update_id = nu.id AND na.organization_id = ?
             WHERE nu.is_published = 1 AND na.id IS NULL
             ORDER BY
                CASE nu.impact_level WHEN \'critical\' THEN 1 WHEN \'high\' THEN 2 WHEN \'medium\' THEN 3 ELSE 4 END,
                nu.published_at DESC',
            [$orgId]
        );

        $this->jsonSuccess([
            'pending' => $pending,
            'count'   => count($pending),
            'critical_count' => count(array_filter($pending, fn($u) => $u['impact_level'] === 'critical')),
        ]);
    }

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

        $total = Database::fetchOne('SELECT COUNT(*) as n FROM normative_updates WHERE is_published = 1');
        $acked = Database::fetchOne(
            'SELECT COUNT(*) as n FROM normative_ack WHERE organization_id = ?',
            [$orgId]
        );

        $ackRate = $total['n'] > 0 ? round(($acked['n'] / $total['n']) * 100) : 0;

        $bySource = Database::fetchAll(
            'SELECT nu.source,
                    COUNT(nu.id) as total,
                    COUNT(na.id) as acknowledged
             FROM normative_updates nu
             LEFT JOIN normative_ack na ON na.normative_update_id = nu.id AND na.organization_id = ?
             WHERE nu.is_published = 1
             GROUP BY nu.source',
            [$orgId]
        );

        $this->jsonSuccess([
            'total_updates'  => (int)$total['n'],
            'acknowledged'   => (int)$acked['n'],
            'pending'        => (int)$total['n'] - (int)$acked['n'],
            'ack_rate'       => $ackRate,
            'by_source'      => $bySource,
        ]);
    }

    /**
     * POST /api/normative/create
     * Solo super_admin può pubblicare nuovi aggiornamenti normativi.
     */
    public function create(): void
    {
        $this->requireSuperAdmin();
        $this->validateRequired(['title', 'source', 'summary', 'impact_level']);

        $domains = $this->getParam('affected_domains', []);
        if (is_string($domains)) $domains = json_decode($domains, true) ?? [];

        $id = Database::insert('normative_updates', [
            'title'           => trim($this->getParam('title')),
            'source'          => $this->getParam('source'),
            'source_label'    => $this->getParam('source_label'),
            'reference'       => $this->getParam('reference'),
            'summary'         => trim($this->getParam('summary')),
            'content'         => $this->getParam('content'),
            'impact_level'    => $this->getParam('impact_level'),
            'affected_domains'=> json_encode(array_values($domains)),
            'action_required' => $this->getParam('action_required', 0) ? 1 : 0,
            'effective_date'  => $this->getParam('effective_date') ?: null,
            'url'             => $this->getParam('url') ?: null,
            'is_published'    => 1,
        ]);

        $this->jsonSuccess(['id' => $id], 'Aggiornamento normativo pubblicato', 201);
    }
}
