<?php
/**
 * NIS2 Agile - Supply Chain Controller
 *
 * Gestione fornitori, assessment cybersecurity, risk scoring.
 */

require_once __DIR__ . '/BaseController.php';

class SupplyChainController extends BaseController
{
    public function list(): void
    {
        $this->requireOrgAccess();

        $suppliers = Database::fetchAll(
            'SELECT * FROM suppliers WHERE organization_id = ? ORDER BY criticality DESC, name',
            [$this->getCurrentOrgId()]
        );

        $this->jsonSuccess($suppliers);
    }

    public function create(): void
    {
        $this->requireOrgRole(['org_admin', 'compliance_manager']);
        $this->validateRequired(['name', 'service_type']);

        $supplierId = Database::insert('suppliers', [
            'organization_id'     => $this->getCurrentOrgId(),
            'name'                => trim($this->getParam('name')),
            'vat_number'          => $this->getParam('vat_number'),
            'contact_email'       => $this->getParam('contact_email'),
            'contact_name'        => $this->getParam('contact_name'),
            'service_type'        => $this->getParam('service_type'),
            'service_description' => $this->getParam('service_description'),
            'criticality'         => $this->getParam('criticality', 'medium'),
            'contract_start_date' => $this->getParam('contract_start_date'),
            'contract_expiry_date' => $this->getParam('contract_expiry_date'),
            'notes'               => $this->getParam('notes'),
        ]);

        $this->logAudit('supplier_created', 'supplier', $supplierId);
        $this->jsonSuccess(['id' => $supplierId], 'Fornitore aggiunto', 201);
    }

    public function get(int $id): void
    {
        $this->requireOrgAccess();

        $supplier = Database::fetchOne(
            'SELECT * FROM suppliers WHERE id = ? AND organization_id = ?',
            [$id, $this->getCurrentOrgId()]
        );

        if (!$supplier) {
            $this->jsonError('Fornitore non trovato', 404, 'SUPPLIER_NOT_FOUND');
        }

        $this->jsonSuccess($supplier);
    }

    public function update(int $id): void
    {
        $this->requireOrgRole(['org_admin', 'compliance_manager']);

        $updates = [];
        $fields = ['name', 'vat_number', 'contact_email', 'contact_name', 'service_type',
                    'service_description', 'criticality', 'contract_start_date', 'contract_expiry_date',
                    'security_requirements_met', 'notes', 'status'];

        foreach ($fields as $field) {
            if ($this->hasParam($field)) {
                $updates[$field] = $this->getParam($field);
            }
        }

        if (!empty($updates)) {
            Database::update('suppliers', $updates, 'id = ? AND organization_id = ?', [$id, $this->getCurrentOrgId()]);
            $this->logAudit('supplier_updated', 'supplier', $id, $updates);
        }

        $this->jsonSuccess($updates, 'Fornitore aggiornato');
    }

    public function delete(int $id): void
    {
        $this->requireOrgRole(['org_admin']);
        $deleted = Database::delete('suppliers', 'id = ? AND organization_id = ?', [$id, $this->getCurrentOrgId()]);
        if ($deleted === 0) {
            $this->jsonError('Fornitore non trovato', 404, 'SUPPLIER_NOT_FOUND');
        }
        $this->logAudit('supplier_deleted', 'supplier', $id);
        $this->jsonSuccess(null, 'Fornitore eliminato');
    }

    public function assessSupplier(int $id): void
    {
        $this->requireOrgRole(['org_admin', 'compliance_manager']);
        $this->validateRequired(['assessment_responses']);

        $responses = $this->getParam('assessment_responses');
        $riskScore = $this->calculateSupplierRiskScore($responses);

        Database::update('suppliers', [
            'assessment_responses'  => json_encode($responses),
            'risk_score'            => $riskScore,
            'last_assessment_date'  => date('Y-m-d'),
            'next_assessment_date'  => date('Y-m-d', strtotime('+6 months')),
            'security_requirements_met' => $riskScore >= 70 ? 1 : 0,
        ], 'id = ? AND organization_id = ?', [$id, $this->getCurrentOrgId()]);

        $this->logAudit('supplier_assessed', 'supplier', $id, ['risk_score' => $riskScore]);
        $this->jsonSuccess(['risk_score' => $riskScore], 'Assessment fornitore completato');
    }

    public function riskOverview(): void
    {
        $this->requireOrgAccess();

        $overview = Database::fetchAll(
            'SELECT criticality, status,
                    COUNT(*) as count,
                    AVG(risk_score) as avg_risk_score,
                    SUM(CASE WHEN security_requirements_met = 0 THEN 1 ELSE 0 END) as non_compliant
             FROM suppliers
             WHERE organization_id = ?
             GROUP BY criticality, status',
            [$this->getCurrentOrgId()]
        );

        $expiring = Database::fetchAll(
            'SELECT id, name, contract_expiry_date, criticality
             FROM suppliers
             WHERE organization_id = ? AND contract_expiry_date IS NOT NULL
                   AND contract_expiry_date <= DATE_ADD(NOW(), INTERVAL 90 DAY)
                   AND status = "active"
             ORDER BY contract_expiry_date',
            [$this->getCurrentOrgId()]
        );

        $this->jsonSuccess([
            'overview'  => $overview,
            'expiring_contracts' => $expiring,
        ]);
    }

    private function calculateSupplierRiskScore(array $responses): int
    {
        if (empty($responses)) return 0;

        $totalScore = 0;
        $totalWeight = 0;

        foreach ($responses as $resp) {
            $weight = $resp['weight'] ?? 1;
            $value = match ($resp['value'] ?? '') {
                'yes', 'implemented' => 100,
                'partial'            => 50,
                default              => 0,
            };
            $totalScore += $value * $weight;
            $totalWeight += 100 * $weight;
        }

        return $totalWeight > 0 ? (int) round($totalScore / $totalWeight * 100) : 0;
    }

    // ══════════════════════════════════════════════════════════════════════
    // SELF-ASSESSMENT FORNITORI (P3) — questionario sicurezza Art.21.2.d
    // ══════════════════════════════════════════════════════════════════════

    private const QUESTIONNAIRE = [
        ['key' => 'iso27001',      'q' => 'Disponete di una certificazione ISO/IEC 27001 valida?', 'weight' => 15],
        ['key' => 'mfa',           'q' => 'Imponete autenticazione a piu fattori (MFA) per gli accessi ai sistemi che trattano dati del cliente?', 'weight' => 15],
        ['key' => 'patching',      'q' => 'Avete un processo formale di patch management e gestione vulnerabilita?', 'weight' => 12],
        ['key' => 'backup',        'q' => 'Eseguite backup regolari con test di ripristino documentati?', 'weight' => 12],
        ['key' => 'incident',      'q' => 'Disponete di un piano di gestione degli incidenti con notifica al cliente?', 'weight' => 14],
        ['key' => 'access_review',  'q' => 'Effettuate revisioni periodiche degli accessi e revoca tempestiva?', 'weight' => 10],
        ['key' => 'encryption',    'q' => 'I dati del cliente sono cifrati a riposo e in transito?', 'weight' => 12],
        ['key' => 'subcontractor', 'q' => 'Valutate la sicurezza dei vostri sub-fornitori (quarta parte)?', 'weight' => 10],
    ];

    /** POST /api/supply-chain/{id}/send-questionnaire (JWT): genera token + link pubblico. */
    public function sendQuestionnaire(int $id): void
    {
        $this->requireOrgRole(['org_admin', 'compliance_manager']);
        $supplier = Database::fetchOne(
            'SELECT id, name, contact_email FROM suppliers WHERE id = ? AND organization_id = ? AND deleted_at IS NULL',
            [$id, $this->getCurrentOrgId()]
        );
        if (!$supplier) {
            $this->jsonError('Fornitore non trovato', 404, 'NOT_FOUND');
        }

        $rawToken = 'sq_' . bin2hex(random_bytes(20));
        $email    = $this->getParam('email') ?: $supplier['contact_email'];
        $expires  = date('Y-m-d H:i:s', time() + 30 * 86400);

        Database::insert('supplier_questionnaires', [
            'organization_id' => $this->getCurrentOrgId(),
            'supplier_id'     => $id,
            'token_hash'      => hash('sha256', $rawToken),
            'status'          => 'sent',
            'sent_to_email'   => $email,
            'expires_at'      => $expires,
        ]);

        $link = 'https://nis2.agile.software/supplier-assessment.html?token=' . $rawToken;

        try {
            if ($email && class_exists('EmailService')) {
                $svc = new EmailService();
                if (method_exists($svc, 'send')) {
                    $svc->send($email, 'Questionario di sicurezza fornitore - NIS2',
                        "<p>Gentile {$supplier['name']},</p><p>vi chiediamo di compilare il questionario di sicurezza al seguente link (valido 30 giorni):</p><p><a href=\"{$link}\">{$link}</a></p>");
                }
            }
        } catch (Throwable $e) {
            error_log('[SUPPLIER_Q] email: ' . $e->getMessage());
        }

        $this->logAudit('supplier_questionnaire_sent', 'supplier', $id, ['email' => $email]);
        $this->jsonSuccess(['supplier_id' => $id, 'sent_to' => $email, 'link' => $link, 'expires_at' => $expires], 'Questionario generato', 201);
    }

    /** GET /api/supply-chain/public-questionnaire?token=xxx (NO AUTH). */
    public function publicQuestionnaire(): void
    {
        $q = $this->resolveQuestionnaire($_GET['token'] ?? '');
        if ($q['status'] === 'completed') {
            $this->jsonError('Questionario gia compilato', 409, 'ALREADY_COMPLETED');
        }
        $supplier = Database::fetchOne('SELECT name FROM suppliers WHERE id = ?', [$q['supplier_id']]);
        $this->jsonSuccess([
            'supplier_name' => $supplier['name'] ?? 'Fornitore',
            'questions'     => array_map(fn($x) => ['key' => $x['key'], 'question' => $x['q']], self::QUESTIONNAIRE),
            'answer_scale'  => ['yes' => 'Si', 'partial' => 'Parzialmente', 'no' => 'No'],
        ]);
    }

    /** POST /api/supply-chain/submit-public-questionnaire (NO AUTH). Body: {token, answers}. */
    public function submitPublicQuestionnaire(): void
    {
        $body = $this->getJsonBody();
        $q = $this->resolveQuestionnaire($body['token'] ?? '');
        if ($q['status'] === 'completed') {
            $this->jsonError('Questionario gia compilato', 409, 'ALREADY_COMPLETED');
        }

        $answers = is_array($body['answers'] ?? null) ? $body['answers'] : [];
        $scoreMap = ['yes' => 1.0, 'partial' => 0.5, 'no' => 0.0];
        $earned = 0; $maxScore = 0; $clean = [];
        foreach (self::QUESTIONNAIRE as $item) {
            $maxScore += $item['weight'];
            $a = strtolower((string) ($answers[$item['key']] ?? 'no'));
            if (!isset($scoreMap[$a])) $a = 'no';
            $clean[$item['key']] = $a;
            $earned += $item['weight'] * $scoreMap[$a];
        }
        $score = $maxScore > 0 ? (int) round($earned / $maxScore * 100) : 0;
        $riskLevel = $score >= 80 ? 'low' : ($score >= 60 ? 'medium' : ($score >= 40 ? 'high' : 'critical'));

        // Completamento ATOMICO: vincola lo UPDATE a status='sent' così due submit
        // concorrenti con lo stesso token non possono completare due volte (la seconda
        // tocca 0 righe -> 409). Previene il doppio-submit senza transazione esplicita.
        $upd = Database::query(
            'UPDATE supplier_questionnaires SET status=?, answers=?, score=?, risk_level=?, completed_at=NOW()
             WHERE id=? AND status=?',
            ['completed', json_encode($clean, JSON_UNESCAPED_UNICODE), $score, $riskLevel, $q['id'], 'sent']
        );
        if ($upd->rowCount() === 0) {
            $this->jsonError('Questionario gia compilato', 409, 'ALREADY_COMPLETED');
        }

        // Coerenza con assessSupplier: suppliers.risk_score = punteggio di COMPLIANCE
        // (alto = buono), e security_requirements_met soglia 70. NON sovrascriviamo
        // suppliers.criticality (è la criticità del fornitore, non l'esito del questionario).
        Database::query(
            'UPDATE suppliers SET risk_score=?, security_requirements_met=?, last_assessment_date=CURDATE()
             WHERE id=? AND organization_id=?',
            [$score, $score >= 70 ? 1 : 0, $q['supplier_id'], $q['organization_id']]
        );

        $this->jsonSuccess(['score' => $score, 'risk_level' => $riskLevel], 'Questionario inviato. Grazie.', 201);
    }

    /** GET /api/supply-chain/{id}/questionnaire-status (JWT). */
    public function questionnaireStatus(int $id): void
    {
        $this->requireOrgAccess();
        $rows = Database::fetchAll(
            'SELECT id, status, score, risk_level, sent_to_email, sent_at, completed_at, expires_at
             FROM supplier_questionnaires WHERE supplier_id = ? AND organization_id = ? ORDER BY sent_at DESC',
            [$id, $this->getCurrentOrgId()]
        );
        $this->jsonSuccess(['questionnaires' => $rows]);
    }

    /** Risolve un token grezzo nel record questionario, validando scadenza. */
    private function resolveQuestionnaire(string $rawToken): array
    {
        if (!preg_match('/^sq_[a-f0-9]{40}$/', $rawToken)) {
            $this->jsonError('Token non valido', 404, 'INVALID_TOKEN');
        }
        $q = Database::fetchOne('SELECT * FROM supplier_questionnaires WHERE token_hash = ?', [hash('sha256', $rawToken)]);
        if (!$q) {
            $this->jsonError('Questionario non trovato', 404, 'NOT_FOUND');
        }
        if ($q['expires_at'] && strtotime($q['expires_at']) < time() && $q['status'] !== 'completed') {
            Database::query('UPDATE supplier_questionnaires SET status=? WHERE id=?', ['expired', $q['id']]);
            $this->jsonError('Questionario scaduto', 410, 'EXPIRED');
        }
        return $q;
    }
}
