<?php
// enroll_face.php
//
// IMPORTANT: handle JSON POSTs BEFORE including header.php — otherwise PHP
// prints the HTML chrome and the JSON response gets prepended with "<!DOCTYPE"
// which breaks JSON.parse on the client.

require_once __DIR__ . '/auth.php';
require_once __DIR__ . '/lib/face.php';
require_login();

$user = current_user();

// ---------- JSON enrollment endpoint (no HTML output allowed before this) ----------
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; }

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

    if (!is_array($samples) || count($samples) < 1 || count($samples) > 10) {
        http_response_code(422);
        echo json_encode(['ok'=>false,'error'=>'need 1–10 samples']);
        exit;
    }
    $avg = face_descriptor_average($samples);
    if (!$avg) {
        http_response_code(422);
        echo json_encode(['ok'=>false,'error'=>'invalid descriptor']);
        exit;
    }

    try {
        $enc = face_descriptor_encode($avg);
        $pdo->prepare("UPDATE users SET face_descriptor = ?, face_enrolled_at = NOW(), face_login_enabled = 1 WHERE id = ?")
            ->execute([$enc, $user['id']]);
        audit_log($pdo, 'face_enroll', ['user_id' => (int)$user['id'], 'samples' => count($samples)]);
        echo json_encode(['ok'=>true,'enrolled_at' => date('c')]);
    } catch (Throwable $e) {
        error_log('face_enroll: ' . $e->getMessage());
        http_response_code(500);
        echo json_encode(['ok'=>false,'error'=>'save_failed']);
    }
    exit;
}

// ---------- Form actions (non-JSON POST) ----------
$msg = '';
$err = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    csrf_require();
    if (isset($_POST['disable_face'])) {
        $pdo->prepare("UPDATE users SET face_login_enabled = 0 WHERE id = ?")->execute([$user['id']]);
        audit_log($pdo, 'face_disable', []);
        $msg = 'Face login disabled (descriptor kept – you can re-enable later).';
    }
    if (isset($_POST['clear_face'])) {
        $pdo->prepare("UPDATE users SET face_descriptor = NULL, face_enrolled_at = NULL, face_login_enabled = 0 WHERE id = ?")
            ->execute([$user['id']]);
        audit_log($pdo, 'face_clear', []);
        $msg = 'Face data deleted from your profile.';
    }
}

// Load current enrollment status
$stmt = $pdo->prepare("SELECT face_enrolled_at, face_login_enabled FROM users WHERE id = ?");
$stmt->execute([$user['id']]);
$row = $stmt->fetch();

// Now safe to include the HTML chrome
require_once __DIR__ . '/header.php';
?>
<h2>Face Login Enrollment</h2>
<p class="muted">Enroll your face once and you'll be able to sign in with it from the login page. This is in addition to your password — your password still works.</p>

<?php if ($msg): ?><div class="alert alert-success"><?php echo h($msg); ?></div><?php endif; ?>
<?php if ($err): ?><div class="alert alert-danger"><?php echo h($err); ?></div><?php endif; ?>

<div class="alert alert-info">
    <strong>How it works:</strong> The camera captures 5 short snapshots. Each is converted to a 128-number "descriptor" using a model that runs entirely in your browser — your photos never leave this device. Only the averaged descriptor is stored.
</div>

<?php if ($row && $row['face_enrolled_at']): ?>
    <div class="alert alert-success">
        ✓ Face enrolled on <strong><?php echo h($row['face_enrolled_at']); ?></strong>
        — login currently <strong><?php echo $row['face_login_enabled'] ? 'ENABLED' : 'DISABLED'; ?></strong>.
    </div>
    <form method="post" style="display:inline">
        <?php echo csrf_field(); ?>
        <button type="submit" name="disable_face" value="1" class="btn"><?php echo $row['face_login_enabled'] ? 'Disable face login' : '—'; ?></button>
    </form>
    <form method="post" style="display:inline" onsubmit="return confirm('Delete your face data entirely?');">
        <?php echo csrf_field(); ?>
        <button type="submit" name="clear_face" value="1" class="btn btn-danger">Delete face data</button>
    </form>
    <hr>
    <p>Re-enroll if your appearance has changed significantly (new glasses, beard, lighting setup):</p>
<?php endif; ?>

<div id="face-card" style="background:#0f172a; color:#f1f5f9; padding:18px; border-radius:10px; max-width:520px; margin-top:14px;">
    <video id="face-video" autoplay muted playsinline style="width:100%; max-width:480px; border-radius:8px; background:#000; aspect-ratio:4/3;"></video>
    <div id="face-status" style="margin-top:10px; font-size:14px; min-height:22px;">Loading face model…</div>
    <div class="flex" style="margin-top:10px;">
        <button id="capture-btn" class="btn btn-primary" disabled>Start capture (5 samples)</button>
        <button id="stop-btn" class="btn">Stop camera</button>
        <span id="sample-count" class="muted">0 / 5 samples</span>
    </div>
    <div id="face-error" class="alert alert-danger" style="display:none; margin-top:10px;"></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('capture-btn');
    const stopBtn = document.getElementById('stop-btn');
    const count = document.getElementById('sample-count');
    const errBox = document.getElementById('face-error');

    function showError(m) { errBox.textContent = m; errBox.style.display = 'block'; }
    function setStatus(m) { status.textContent = m; }

    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. Click "Start capture" and keep your face centered.');
    } catch (e) {
        showError('Could not load face model: ' + e.message);
        return;
    }

    let stream;
    try {
        stream = await navigator.mediaDevices.getUserMedia({ video: { width: 480, height: 360, facingMode: 'user' } });
        video.srcObject = stream;
        await new Promise(res => video.onloadedmetadata = res);
        btn.disabled = false;
    } catch (e) {
        showError('Camera access denied or unavailable: ' + e.message);
        return;
    }

    stopBtn.addEventListener('click', () => {
        if (stream) stream.getTracks().forEach(t => t.stop());
        setStatus('Camera stopped.');
        btn.disabled = true;
    });

    const REQUIRED_SAMPLES = 5;
    const samples = [];

    btn.addEventListener('click', async () => {
        btn.disabled = true;
        samples.length = 0;
        count.textContent = `0 / ${REQUIRED_SAMPLES} samples`;
        setStatus('Capturing… hold still and look at the camera.');

        for (let i = 0; i < REQUIRED_SAMPLES; i++) {
            await new Promise(r => setTimeout(r, 500));
            const det = await faceapi.detectSingleFace(video, new faceapi.TinyFaceDetectorOptions({ inputSize: 224 }))
                                     .withFaceLandmarks().withFaceDescriptor();
            if (!det) {
                showError(`Could not detect a face on sample ${i+1}. Try better lighting and face the camera directly.`);
                btn.disabled = false;
                return;
            }
            samples.push(Array.from(det.descriptor));
            count.textContent = `${samples.length} / ${REQUIRED_SAMPLES} samples`;
        }

        setStatus('Saving…');
        try {
            const r = await OSCE.fetchJSON('enroll_face.php', { method:'POST', body: { samples } });
            if (r.ok) {
                setStatus('✅ Face enrolled successfully! Reloading…');
                setTimeout(() => location.reload(), 1200);
            } else {
                showError('Save failed: ' + (r.error || 'unknown'));
                btn.disabled = false;
            }
        } catch (e) {
            showError('Network error: ' + e.message);
            btn.disabled = false;
        }
    });
})();
</script>

<?php require __DIR__ . '/footer.php'; ?>
