<?php

function og_timing_safe_equals($a, $b) {
    if (!is_string($a) || !is_string($b)) return false;
    if (function_exists('hash_equals')) {
        return hash_equals($a, $b);
    }
    if (strlen($a) !== strlen($b)) return false;
    $res = 0;
    for ($i = 0; $i < strlen($a); $i++) {
        $res |= ord($a[$i]) ^ ord($b[$i]);
    }
    return $res === 0;
}

function og_hmac_sha256_hex($secret, $message) {
    return hash_hmac('sha256', $message, $secret);
}

function og_require_node_signature($secret, $rawBody, $maxSkewMs = 60000) {
    $ts = $_SERVER['HTTP_X_OG_TIMESTAMP'] ?? null;
    $sigHeader = $_SERVER['HTTP_X_OG_SIGNATURE'] ?? '';

    if (!$ts || !preg_match('/^\d{13,}$/', $ts)) {
        return ['ok' => false, 'error' => 'missing_or_invalid_timestamp'];
    }

    if (!preg_match('/^sha256=([0-9a-f]{64})$/', $sigHeader, $m)) {
        return ['ok' => false, 'error' => 'missing_or_invalid_signature'];
    }

    $now = (int) round(microtime(true) * 1000);
    $tsInt = (int) $ts;
    if (abs($now - $tsInt) > $maxSkewMs) {
        return ['ok' => false, 'error' => 'timestamp_out_of_range'];
    }

    $msg = $ts . '.' . $rawBody;
    $expected = og_hmac_sha256_hex($secret, $msg);
    $provided = $m[1];

    if (!og_timing_safe_equals($expected, $provided)) {
        return ['ok' => false, 'error' => 'signature_mismatch'];
    }

    return ['ok' => true];
}
