<?php
require_once __DIR__ . '/auth.php';
require_once __DIR__ . '/lib/face.php';

if (current_user()) {
    header('Location: dashboard.php');
    exit;
}

// JSON match endpoint
if ($_SERVER['REQUEST_METHOD'] === 'POST' && str_contains($_SERVER['CONTENT_TYPE'] ?? '', 'application/json')) {
    header('Content-Type: application/json; charset=utf-8');
    if (!csrf_check()) {
        http_response_code(403);
        echo json_encode(['ok' => false, 'error' => 'csrf']);
        exit;
    }

    $ip = $_SERVER['REMOTE_ADDR'] ?? '';
    // Reuse the same login-attempts table; we don't have a username yet so
    // restrict purely on IP for face login.
    $stmt = $pdo->prepare("
        SELECT COUNT(*) c FROM login_attempts
        WHERE success = 0
          AND attempted_at > (NOW() - INTERVAL ? MINUTE)
          AND ip_address = ?
    ");
    $stmt->execute([login_lockout_minutes(), $ip]);
    $n = (int)($stmt->fetch()['c'] ?? 0);
    if ($n >= login_max_attempts()) {
        http_response_code(429);
        echo json_encode(['ok' => false, 'error' => 'locked', 'minutes' => login_lockout_minutes()]);
        audit_log($pdo, 'face_login_locked', ['ip' => $ip]);
        exit;
    }

    $body = json_decode(file_get_contents('php://input') ?: '[]', true) ?: [];
    $probe = $body['descriptor'] ?? null;

    if (!face_descriptor_is_valid($probe)) {
        http_response_code(422);
        echo json_encode(['ok' => false, 'error' => 'invalid_descriptor']);
        exit;
    }

    $match = face_find_match($pdo, $probe);
    if (!$match) {
        login_attempt_log($pdo, 'face:unknown', $ip, false);
        audit_log($pdo, 'face_login_failed', ['ip' => $ip]);
        echo json_encode(['ok' => false, 'error' => 'no_match']);
        exit;
    }

    $u = $match['user'];
    login_attempt_success($pdo, $u['username'], $ip);
    auth_login_user($pdo, $u);
    audit_log($pdo, 'face_login_success', [
        'user_id'  => (int)$u['id'],
        'distance' => round((float)$match['distance'], 4),
    ]);
    echo json_encode(['ok' => true, 'distance' => round((float)$match['distance'], 4), 'next' => 'dashboard.php']);
    exit;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="<?php echo htmlspecialchars(csrf_token(), ENT_QUOTES); ?>">
<title>OSCE Nexus™ – Face Sign-in</title>
<link rel="stylesheet" href="assets/css/app.css">
<style>
.face-shell  { min-height: 90vh; display:flex; align-items:center; justify-content:center; padding:16px; }
.face-card   { width:100%; max-width:520px; background:#0f172a; color:#f1f5f9; padding:24px; border-radius:10px; box-shadow:var(--shadow-lg); }
.face-card h1 { margin:0 0 4px; }
.face-card .sub { color:#cbd5e1; margin:0 0 18px; font-size:14px; }
#face-video { width:100%; border-radius:8px; background:#000; aspect-ratio:4/3; }
#face-status { margin-top:10px; font-size:14px; min-height:22px; }
</style>
</head>
<body>
<div class="face-shell">
    <div class="face-card">
        <h1>Face Sign-in</h1>
        <p class="sub">Look into the camera. We'll match against enrolled examiners.</p>

        <video id="face-video" autoplay muted playsinline></video>
        <div id="face-status">Loading face model…</div>
        <div id="face-error" class="alert alert-danger" style="display:none; margin-top:10px;"></div>

        <div class="flex" style="margin-top:14px;">
            <button id="match-btn" class="btn btn-primary btn-lg" disabled>Sign in with face</button>
            <a href="login.php" class="btn" style="background:transparent; color:#cbd5e1; border-color:#475569;">Use password instead</a>
        </div>

        <small style="display:block; margin-top:18px; color:#94a3b8;">
            Your photos never leave this device; only a 128-number descriptor is sent for matching.
        </small>
    </div>
</div>

<script src="assets/face/face-api.min.js"></script>
<script>
(async function () {
    const video = document.getElementById('face-video');
    const status = document.getElementById('face-status');
    const btn = document.getElementById('match-btn');
    const errBox = document.getElementById('face-error');
    function showError(m){ errBox.textContent = m; errBox.style.display='block'; }
    function setStatus(m){ status.textContent = m; }
    function csrf(){ return document.querySelector('meta[name=csrf-token]').content; }

    try {
        const MODEL_URL = 'assets/face/models';
        await faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL);
        await faceapi.nets.faceLandmark68Net.loadFromUri(MODEL_URL);
        await faceapi.nets.faceRecognitionNet.loadFromUri(MODEL_URL);
        setStatus('Model loaded.');
    } catch (e) { showError('Model load failed: ' + e.message); return; }

    let stream;
    try {
        stream = await navigator.mediaDevices.getUserMedia({ video:{ width:480, height:360, facingMode:'user' } });
        video.srcObject = stream;
        await new Promise(r => video.onloadedmetadata = r);
        btn.disabled = false;
        setStatus('Position your face and click "Sign in with face".');
    } catch (e) { showError('Camera denied or unavailable: ' + e.message); return; }

    btn.addEventListener('click', async () => {
        btn.disabled = true;
        setStatus('Looking for your face…');
        const det = await faceapi.detectSingleFace(video, new faceapi.TinyFaceDetectorOptions({inputSize:224}))
                                  .withFaceLandmarks().withFaceDescriptor();
        if (!det) { showError('No face detected. Try better lighting.'); btn.disabled=false; return; }
        setStatus('Matching…');
        try {
            const r = await fetch('face_login.php', {
                method:'POST',
                headers:{'Content-Type':'application/json','X-CSRF-Token':csrf()},
                body: JSON.stringify({ descriptor: Array.from(det.descriptor) })
            });
            const j = await r.json();
            if (r.status === 429) {
                showError(`Too many failed attempts. Try again in ${j.minutes || 15} minutes.`);
                return;
            }
            if (j.ok) {
                setStatus(`✅ Matched (distance ${j.distance}). Redirecting…`);
                if (stream) stream.getTracks().forEach(t => t.stop());
                location.href = j.next || 'dashboard.php';
            } else {
                showError('No matching enrolled examiner. Use password sign-in.');
                btn.disabled = false;
            }
        } catch (e) {
            showError('Network error: ' + e.message);
            btn.disabled = false;
        }
    });
})();
</script>
</body>
</html>
