<?php

/**

 * Fichier  : includes/push-helper.php

 * Auteur   : KREMER Régis

 * Version  : 2.2.0

 * Date     : 2026-02-21

 * Description : Helper envoi de notifications Web Push (VAPID)

 *

 * Changelog :

 *   v2.1.0 (2026-02-21) — Version initiale auditée et sécurisée

 *   v2.1.1 (2026-02-21) — Corrections sécurité (CSRF, MIME, session, migrateSuppliers)

 *   v2.2.0 (2026-02-21) — Permissions magasins, bons de commande, rapport d'écart,

 *                      import CSV, historique prix, bon de livraison multi-lignes

 */

// ================================================================

// includes/push-helper.php — Helper pour envoyer des notifications push

// À inclure dans les API qui déclenchent des notifications

// ================================================================



/**

 * Envoie une notification push à tous les admins sauf l'initiateur

 * 

 * @param string $type Type de notification (stock_out, stock_in, alert, inventory)

 * @param string $title Titre de la notification

 * @param string $body Corps du message

 * @param array $data Données additionnelles (url, etc.)

 * @param int|null $excludeUserId ID de l'utilisateur à exclure (l'initiateur)

 * @return int Nombre de notifications envoyées

 */

function sendPushToAdmins(string $type, string $title, string $body, array $data = [], ?int $excludeUserId = null): int {

    $subs = loadData('push_subscriptions');

    $users = loadData('users');

    

    if (empty($subs)) {

        return 0;

    }

    

    // Trouver les IDs des admins actifs

    $adminIds = [];

    foreach ($users as $u) {

        if (($u['role'] ?? '') === 'admin' && ($u['active'] ?? true) !== false) {

            $adminIds[] = (int)$u['id'];

        }

    }

    

    if (empty($adminIds)) {

        return 0;

    }

    

    // Préparer le payload

    $payload = [

        'title' => $title,

        'body' => $body,

        'type' => $type,

        'icon' => '/sm/assets/img/icon-192.png',

        'badge' => '/sm/assets/img/badge-72.png',

        'tag' => 'stockmaster-' . $type . '-' . time(),

        'data' => array_merge(['url' => '/sm/'], $data),

        'timestamp' => time() * 1000

    ];

    

    $sent = 0;

    

    foreach ($subs as $sub) {

        $subUserId = (int)($sub['userId'] ?? 0);

        

        // Exclure l'initiateur

        if ($excludeUserId !== null && $subUserId === $excludeUserId) {

            continue;

        }

        

        // Envoyer uniquement aux admins

        if (!in_array($subUserId, $adminIds)) {

            continue;

        }

        

        if (sendSinglePush($sub, $payload)) {

            $sent++;

        }

    }

    

    return $sent;

}



/**

 * Envoie une notification push à une subscription spécifique

 */

function sendSinglePush(array $subscription, array $payload): bool {

    $endpoint = $subscription['endpoint'] ?? '';

    $keys = $subscription['keys'] ?? [];

    

    if (empty($endpoint)) {

        return false;

    }

    

    // Préparer le payload JSON

    $payloadJson = json_encode($payload);

    

    // Headers de base

    $headers = [

        'Content-Type: application/json',

        'Content-Length: ' . strlen($payloadJson),

        'TTL: 86400',

        'Urgency: normal'

    ];

    

    // Envoyer via cURL

    $ch = curl_init($endpoint);

    curl_setopt_array($ch, [

        CURLOPT_POST => true,

        CURLOPT_POSTFIELDS => $payloadJson,

        CURLOPT_HTTPHEADER => $headers,

        CURLOPT_RETURNTRANSFER => true,

        CURLOPT_TIMEOUT => 10,

        CURLOPT_SSL_VERIFYPEER => true

    ]);

    

    $response = curl_exec($ch);

    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

    $error = curl_error($ch);

    curl_close($ch);

    

    // Log pour debug

    if ($httpCode !== 201 && $httpCode !== 200) {

        error_log("[PUSH] Erreur envoi à $endpoint: HTTP $httpCode - $error - $response");

    }

    

    // 410 = Gone → subscription expirée, la supprimer

    if ($httpCode === 410) {

        $subs = loadData('push_subscriptions');

        $subs = array_filter($subs, fn($s) => ($s['endpoint'] ?? '') !== $endpoint);

        saveData('push_subscriptions', array_values($subs));

    }

    

    return $httpCode >= 200 && $httpCode < 300;

}



/**

 * Helpers pour les différents types de notifications

 */



function notifyStockOut(string $productName, string $warehouseName, int $quantity, ?int $excludeUserId = null): int {

    return sendPushToAdmins(

        'stock_out',

        '📤 Sortie de stock',

        "$quantity × $productName sorti(s) de $warehouseName",

        ['url' => '/sm/index.php?page=movements'],

        $excludeUserId

    );

}



function notifyStockIn(string $productName, string $warehouseName, int $quantity, ?int $excludeUserId = null): int {

    return sendPushToAdmins(

        'stock_in',

        '📥 Entrée de stock',

        "$quantity × $productName ajouté(s) à $warehouseName",

        ['url' => '/sm/index.php?page=movements'],

        $excludeUserId

    );

}



function notifyStockAlert(string $productName, string $warehouseName, string $alertType, int $currentQty, int $threshold): int {

    $typeLabel = $alertType === 'min' ? 'Stock bas' : 'Stock élevé';

    $emoji = $alertType === 'min' ? '⚠️' : '📈';

    

    return sendPushToAdmins(

        'alert',

        "$emoji $typeLabel",

        "$productName dans $warehouseName: $currentQty unités (seuil: $threshold)",

        ['url' => '/sm/index.php?page=alerts'],

        null // Alertes envoyées à tous les admins

    );

}



function notifyInventoryComplete(string $warehouseName, int $productCount, int $discrepancies, ?int $excludeUserId = null): int {

    $emoji = $discrepancies > 0 ? '⚠️' : '✅';

    $status = $discrepancies > 0 ? "$discrepancies écart(s) trouvé(s)" : "Aucun écart";

    

    return sendPushToAdmins(

        'inventory',

        "$emoji Inventaire terminé",

        "$warehouseName: $productCount produits vérifiés. $status",

        ['url' => '/sm/index.php?page=inventory'],

        $excludeUserId

    );

}



/**

 * Vérifie les alertes pour un produit/magasin et envoie des notifications si déclenchées

 */

function checkAndNotifyAlerts(int $productId, int $warehouseId): void {

    $alerts = loadData('alerts');

    $stock = loadData('stock');

    $products = loadData('products');

    $warehouses = loadData('warehouses');

    

    // Trouver le stock actuel

    $currentQty = 0;

    foreach ($stock as $s) {

        if ((int)($s['productId'] ?? 0) === $productId && (int)($s['warehouseId'] ?? 0) === $warehouseId) {

            $currentQty = (int)($s['quantity'] ?? 0);

            break;

        }

    }

    

    // Vérifier les alertes

    foreach ($alerts as $alert) {

        if ((int)($alert['productId'] ?? 0) !== $productId) continue;

        if ((int)($alert['warehouseId'] ?? 0) !== $warehouseId) continue;

        

        $threshold = (int)($alert['threshold'] ?? 0);

        $type = $alert['type'] ?? 'min';

        $wasTriggered = $alert['triggered'] ?? false;

        

        $isTriggered = ($type === 'min') ? ($currentQty <= $threshold) : ($currentQty >= $threshold);

        

        // Envoyer notification seulement si l'alerte vient de se déclencher (pas déjà active)

        if ($isTriggered && !$wasTriggered) {

            $product = findById($products, $productId);

            $warehouse = findById($warehouses, $warehouseId);

            

            $prodName = $product ? $product['name'] : 'Produit';

            $whName = $warehouse ? $warehouse['name'] : 'Magasin';

            

            notifyStockAlert($prodName, $whName, $type, $currentQty, $threshold);

        }

    }

}

