<?php

namespace Linkavista\src\api;

class LinkavistaRest
{
    /**
     * Cette méthode ne rattache le hook qu'une seule fois,
     * quel que soit le nombre d'appels ultérieurs.
     */
    public static function init()
    {
        static $initialized = false;
        if ($initialized) {
            return;
        }
        add_action('rest_api_init', array(__CLASS__, 'set_routes'));
        $initialized = true;
    }

    public static function set_routes()
    {
        self::new_linkavista_route();
    }

    public static function new_linkavista_route()
    {
        $routes = rest_get_server()->get_routes();

        // Enregistrer le nouvel endpoint
        if (!isset($routes['/linkavista/v1/API'])) {
            register_rest_route('linkavista/v1', '/API', array(
                'methods'  => 'POST',
                'callback' => ['Linkavista\src\LinkavistaAPI', 'getAction'],
                'permission_callback' => array(__CLASS__, 'check_permissions'),
            ));
        }

        // Compatibilite avec l'ancien endpoint (linkmania)
        if (!isset($routes['/linkmania/v1/API'])) {
            register_rest_route('linkmania/v1', '/API', array(
                'methods'  => 'POST',
                'callback' => ['Linkavista\src\LinkavistaAPI', 'getAction'],
                'permission_callback' => array(__CLASS__, 'check_permissions'),
            ));
        }
    }

    /**
     * Vérifie les permissions pour accéder à l'API
     */
    public static function check_permissions(\WP_REST_Request $request)
    {
        $apiKey = $request->get_param('apiKey');
        $signature = $request->get_param('signature');

        // Si pas de clés fournies, on vérifie les headers
        if (empty($apiKey)) {
            $apiKey = $request->get_header('X-API-Key');
        }
        if (empty($signature)) {
            $signature = $request->get_header('X-Signature');
        }

        // Si toujours vide, accès refusé
        if (empty($apiKey) || empty($signature)) {
            return false;
        }

        // Timestamp facultatif : si fourni, on l'inclut dans la signature HMAC
        // et on rejette les requêtes hors fenêtre (protection anti-rejeu).
        $timestamp = $request->get_param('timestamp');
        if (empty($timestamp)) {
            $timestamp = $request->get_header('X-Timestamp');
        }

        return self::isApiSecret($apiKey, $signature, $timestamp);
    }

    /**
     * Fenêtre de tolérance (en secondes) pour les requêtes horodatées.
     */
    private const REPLAY_WINDOW = 300;

    public static function isApiSecret($apiKey, $signature, $timestamp = null, $checkNonce = true)
    {
        $storedPublicKey = get_option('lm_public_key');
        $storedSecretKey = get_option('lm_secret_key');

        if (empty($storedPublicKey) || empty($storedSecretKey)) {
            return false;
        }

        $publicKey = preg_replace('/[^a-zA-Z0-9\-]/', '', trim($apiKey));
        $secretKey = preg_replace('/[^a-zA-Z0-9]/', '', trim($storedSecretKey));

        if (!hash_equals($storedPublicKey, $publicKey)) {
            return false;
        }

        // ---- Schéma recommandé : HMAC horodaté + nonce anti-rejeu ----
        if (!empty($timestamp)) {
            // Rejette les requêtes trop anciennes ou trop dans le futur.
            if (abs(time() - (int) $timestamp) > self::REPLAY_WINDOW) {
                return false;
            }

            $payload = $publicKey . '|' . $timestamp;
            $expected = hash_hmac('sha256', $payload, $secretKey);

            if (!hash_equals($expected, (string) $signature)) {
                return false;
            }

            // Nonce : un même couple (signature) ne peut être rejoué qu'une fois
            // pendant la fenêtre de validité. La consommation n'a lieu QUE lors de
            // la validation d'authentification (permission_callback) ; la
            // re-vérification dans getAction passe $checkNonce=false pour ne pas
            // invalider sa propre requête.
            if ($checkNonce) {
                $nonceKey = 'lm_nonce_' . hash('sha256', $signature);
                if (get_transient($nonceKey)) {
                    return false;
                }
                set_transient($nonceKey, 1, self::REPLAY_WINDOW);
            }

            return true;
        }

        // ---- Schéma hérité (déprécié) : conservé À L'IDENTIQUE uniquement
        // pour la compatibilité ascendante avec les versions de la plateforme
        // qui n'envoient pas encore de timestamp. Il reste REJOUABLE.
        //
        // Interrupteur sans redéploiement : une fois la migration serveur vers
        // le HMAC horodaté terminée, ajouter dans wp-config.php :
        //
        //     define('LINKAVISTA_LEGACY_AUTH', false);
        //
        // Cela désactive immédiatement le schéma rejouable ; seule reste alors
        // la voie horodatée + nonce anti-rejeu ci-dessus. Par défaut (constante
        // absente ou true), le schéma hérité reste accepté pour ne rien casser.
        if (defined('LINKAVISTA_LEGACY_AUTH') && !LINKAVISTA_LEGACY_AUTH) {
            return false;
        }

        $salts = ['$linkmania!&Salt', '$linkavista!&Salt'];

        foreach ($salts as $salt) {
            $expectedSignature = hash('sha256', $publicKey . $secretKey . $salt);

            if (hash_equals($expectedSignature, (string) $signature)) {
                return true;
            }
        }

        return false;
    }
}
