<?php
// ─────────────────────────────────────────────────────────────
//  api/booking/checkin.php — QR pass check-in
//
//  POST /api/booking/checkin
//  Body (JSON): { qr_payload: string, appointment_id?: int }
//
//  Requires: active admin session
//  Returns:  { ok, customer_name, sessions_remaining|null, checkin_id }
// ─────────────────────────────────────────────────────────────
require_once dirname(dirname(__DIR__)) . '/lib.php';
require_once dirname(dirname(__DIR__)) . '/lib/ModuleToggle.php';
require_once dirname(dirname(__DIR__)) . '/lib/QrPass.php';

cors();
session_start_safe();
init_booking_db();

ModuleToggle::requireEnabled('module_membership');
api_require_admin();

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    json_out(['ok' => false, 'error' => 'POST required'], 405);
}

$raw  = file_get_contents('php://input');
$body = $raw ? (json_decode($raw, true) ?? []) : [];
if (empty($body) && !empty($_POST)) $body = $_POST;

$token         = trim($body['qr_payload'] ?? '');
$appointmentId = isset($body['appointment_id']) ? (int)$body['appointment_id'] : null;

if (!$token) {
    json_out(['ok' => false, 'error' => 'qr_payload is required'], 400);
}

// ── 1. Verify token signature + expiry ───────────────────────
try {
    $pass = QrPass::decode($token);
} catch (\RuntimeException $e) {
    json_out(['ok' => false, 'error' => $e->getMessage()], 400);
}

$membershipId = $pass['membership_id'];
$customerId   = $pass['customer_id'];

// ── 2. Open transaction + lock membership row ─────────────────
$db = db();
$db->beginTransaction();

try {
    // Lock row for update so concurrent scans don't both succeed
    $memStmt = $db->prepare(
        'SELECT m.id, m.status, m.expires_at, m.sessions_remaining,
                c.name AS customer_name, c.email AS customer_email
         FROM bk_memberships m
         JOIN bk_customers c ON c.id = m.customer_id
         WHERE m.id = ? AND m.customer_id = ?
         FOR UPDATE'
    );
    $memStmt->execute([$membershipId, $customerId]);
    $membership = $memStmt->fetch();

    if (!$membership) {
        $db->rollBack();
        json_out(['ok' => false, 'error' => 'Membership not found'], 404);
    }

    if ($membership['status'] !== 'active') {
        $db->rollBack();
        json_out(['ok' => false, 'error' => 'Membership is not active'], 400);
    }

    if ($membership['expires_at'] && strtotime($membership['expires_at']) < time()) {
        $db->rollBack();
        json_out(['ok' => false, 'error' => 'Membership has expired'], 400);
    }

    $remaining = $membership['sessions_remaining'];

    // NULL = unlimited plan — no session gate
    if ($remaining !== null && (int)$remaining <= 0) {
        $db->rollBack();
        json_out(['ok' => false, 'error' => 'No sessions remaining on this membership'], 400);
    }

    // ── 3. Optionally verify appointment_id belongs to this customer ──
    if ($appointmentId) {
        $apptCheck = $db->prepare(
            'SELECT id FROM bk_appointments WHERE id = ? AND customer_id = ?'
        );
        $apptCheck->execute([$appointmentId, $customerId]);
        if (!$apptCheck->fetch()) {
            $db->rollBack();
            json_out(['ok' => false, 'error' => 'Appointment not found for this customer'], 404);
        }
    }

    // ── 4. Log check-in ───────────────────────────────────────
    $adminId = (int)($_SESSION['admin_user_id'] ?? 0);

    // Calculate what sessions_remaining will be after deduction (for snapshot)
    $snapRemaining = ($remaining !== null) ? max(0, (int)$remaining - 1) : null;

    $db->prepare(
        'INSERT INTO bk_checkins
         (appointment_id, membership_id, customer_id, sessions_remaining, scanned_by, method)
         VALUES (?, ?, ?, ?, ?, "qr")'
    )->execute([$appointmentId, $membershipId, $customerId, $snapRemaining, $adminId ?: null]);

    $checkinId = (int) $db->lastInsertId();

    // ── 5. Deduct session if plan has a limit ─────────────────
    $newRemaining = $remaining; // stays null for unlimited
    if ($remaining !== null) {
        $deductStmt = $db->prepare(
            'UPDATE bk_memberships
             SET sessions_remaining = sessions_remaining - 1
             WHERE id = ? AND sessions_remaining > 0'
        );
        $deductStmt->execute([$membershipId]);

        if ($deductStmt->rowCount() === 0) {
            // Another scan beat us to it
            $db->rollBack();
            json_out(['ok' => false, 'error' => 'No sessions remaining (concurrent scan)'], 409);
        }

        $newRemaining = (int)$remaining - 1;
    }

    $db->commit();

    json_out([
        'ok'                => true,
        'checkin_id'        => $checkinId,
        'customer_name'     => $membership['customer_name'],
        'customer_email'    => $membership['customer_email'],
        'sessions_remaining'=> $newRemaining, // null = unlimited
        'checked_in_at'     => date('Y-m-d H:i:s'),
    ]);

} catch (\Throwable $e) {
    $db->rollBack();
    error_log('Check-in error: ' . $e->getMessage());
    json_out(['ok' => false, 'error' => 'Check-in failed: ' . $e->getMessage()], 500);
}
