<?php
/**
 * Verify a WebAuthn fingerprint assertion.
 * POST: credential_id (base64url), authenticator_data (base64url),
 *       client_data_json (base64url), signature (base64url)
 * Auth: canteen_staff_authenticated OR valid EduTrack role
 * Rate limit: 5 attempts / 60 seconds / IP
 * Returns same payload format as canteen_validate.php on success.
 */
if (session_status() === PHP_SESSION_NONE) session_start();
header('Content-Type: application/json');
header('Cache-Control: no-store');

require_once '../config.php';
require_once '../includes/canteen_helpers.php';

canteen_require_active();

$is_staff = !empty($_SESSION['canteen_staff_authenticated']);
$is_role  = !empty($_SESSION['user_id']);
if (!$is_staff && !$is_role) {
    http_response_code(403);
    echo json_encode(['success' => false, 'message' => 'Not authenticated.']);
    exit;
}

// Rate limit: 5 attempts / 60s / IP
$ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
if (!canteen_rate_limit('webauthn_verify_' . $ip, 5, 60)) {
    http_response_code(429);
    echo json_encode(['success' => false, 'message' => 'Too many attempts. Please wait.']);
    exit;
}

$credential_id   = trim($_POST['credential_id']      ?? '');
$auth_data_b64   = trim($_POST['authenticator_data'] ?? '');
$client_data_b64 = trim($_POST['client_data_json']   ?? '');
$signature_b64   = trim($_POST['signature']           ?? '');

if (!$credential_id || !$auth_data_b64 || !$client_data_b64 || !$signature_b64) {
    echo json_encode(['success' => false, 'message' => 'Missing required fields.']);
    exit;
}

// ── Consume challenge ──────────────────────────────────────────────────────
$stored_challenge = $_SESSION['webauthn_challenge']         ?? null;
$challenge_expiry = $_SESSION['webauthn_challenge_expires'] ?? 0;
unset($_SESSION['webauthn_challenge'], $_SESSION['webauthn_challenge_expires']);

if (!$stored_challenge || time() > $challenge_expiry) {
    echo json_encode(['success' => false, 'message' => 'Challenge expired. Please try again.']);
    exit;
}

// ── Decode inputs ──────────────────────────────────────────────────────────
$auth_data_raw   = base64url_decode($auth_data_b64);
$client_data_raw = base64url_decode($client_data_b64);
$sig_raw         = base64url_decode($signature_b64);

if ($auth_data_raw === false || $client_data_raw === false || $sig_raw === false) {
    echo json_encode(['success' => false, 'message' => 'Invalid encoding in request.']);
    exit;
}

// ── Verify clientDataJSON ──────────────────────────────────────────────────
$client_data = json_decode($client_data_raw, true);
if (!$client_data) {
    echo json_encode(['success' => false, 'message' => 'Malformed client data.']);
    exit;
}

if (($client_data['type'] ?? '') !== 'webauthn.get') {
    echo json_encode(['success' => false, 'message' => 'Invalid assertion type.']);
    exit;
}

if (!hash_equals($stored_challenge, $client_data['challenge'] ?? '')) {
    echo json_encode(['success' => false, 'message' => 'Challenge mismatch.']);
    exit;
}

$expected_origin = (isset($_SERVER['HTTPS']) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'];
$allowed_origins = [$expected_origin, 'http://localhost', 'http://127.0.0.1'];
if (!in_array($client_data['origin'] ?? '', $allowed_origins, true)) {
    echo json_encode(['success' => false, 'message' => 'Origin mismatch.']);
    exit;
}

// ── Verify rpIdHash in authenticatorData ──────────────────────────────────
if (strlen($auth_data_raw) < 37) {
    echo json_encode(['success' => false, 'message' => 'AuthenticatorData too short.']);
    exit;
}
$rp_id_hash = substr($auth_data_raw, 0, 32);
$expected_rp_id_hash = hash('sha256', $_SERVER['HTTP_HOST'], true);
if (!hash_equals($expected_rp_id_hash, $rp_id_hash)) {
    echo json_encode(['success' => false, 'message' => 'RP ID hash mismatch.']);
    exit;
}

// ── Look up stored credential ──────────────────────────────────────────────
$conn = getDBConnection();

$stmt = $conn->prepare(
    'SELECT bc.student_id, bc.public_key,
            s.first_name, s.last_name, s.class, s.photo_path, s.student_id AS student_code
     FROM canteen_biometric_credentials bc
     JOIN students s ON s.id = bc.student_id
     WHERE bc.credential_id = ? LIMIT 1'
);
$stmt->bind_param('s', $credential_id);
$stmt->execute();
$row = $stmt->get_result()->fetch_assoc();
$stmt->close();

if (!$row) {
    $conn->close();
    echo json_encode(['success' => false, 'message' => 'Credential not found.']);
    exit;
}

$student_id = (int)$row['student_id'];

// ── Verify signature ───────────────────────────────────────────────────────
// Verification data = authenticatorData || SHA-256(clientDataJSON)
$client_data_hash  = hash('sha256', $client_data_raw, true);
$verification_data = $auth_data_raw . $client_data_hash;

// Load public key from stored SPKI DER (base64 encoded)
$public_key_der = base64_decode($row['public_key'], true);
if (!$public_key_der) {
    $conn->close();
    echo json_encode(['success' => false, 'message' => 'Stored key invalid.']);
    exit;
}

// Convert DER to PEM for openssl
$public_key_pem = "-----BEGIN PUBLIC KEY-----\n"
    . chunk_split(base64_encode($public_key_der), 64, "\n")
    . "-----END PUBLIC KEY-----\n";

$pkey = openssl_pkey_get_public($public_key_pem);
if (!$pkey) {
    $conn->close();
    echo json_encode(['success' => false, 'message' => 'Could not load public key.']);
    exit;
}

$verify_result = openssl_verify($verification_data, $sig_raw, $pkey, OPENSSL_ALGO_SHA256);

if ($verify_result !== 1) {
    // Log failure
    $accessed_by = (int)($_SESSION['user_id'] ?? 0);
    $fail_log = $conn->prepare(
        'INSERT INTO canteen_biometric_access_log (student_id, accessed_by, action, method, result)
         VALUES (?, ?, "verify", "fingerprint", "failure")'
    );
    $fail_log->bind_param('ii', $student_id, $accessed_by);
    $fail_log->execute();
    $fail_log->close();
    $conn->close();
    echo json_encode(['success' => false, 'message' => 'Fingerprint not recognised — please use QR code instead.']);
    exit;
}

// ── Success — log and return student info ──────────────────────────────────
$accessed_by = (int)($_SESSION['user_id'] ?? 0);
$log = $conn->prepare(
    'INSERT INTO canteen_biometric_access_log (student_id, accessed_by, action, method, result)
     VALUES (?, ?, "verify", "fingerprint", "success")'
);
$log->bind_param('ii', $student_id, $accessed_by);
$log->execute();
$log->close();

$photo_url = '';
if (!empty($row['photo_path'])) {
    $photo_url = 'uploads/students/' . basename($row['photo_path']);
}

$conn->close();

echo json_encode([
    'success'      => true,
    'student_id'   => $student_id,
    'student_name' => trim($row['first_name'] . ' ' . $row['last_name']),
    'class_name'   => $row['class'],
    'student_code' => $row['student_code'],
    'photo_url'    => $photo_url,
]);
