prepare(" SELECT c.id, c.question, c.answer, c.level, c.sort_order, cp.next_review, cp.last_rating, cp.last_reviewed_at, cp.ease_factor, cp.interval_days, cp.repetitions, d.name AS deck_name, d.slug AS deck_slug FROM cards c JOIN decks d ON d.id = c.deck_id AND d.slug = :slug LEFT JOIN card_progress cp ON cp.card_id = c.id AND cp.user_id = :uid ORDER BY c.sort_order "); $stmt->execute([':slug' => $slug, ':uid' => $user_id]); json_ok(['cards' => $stmt->fetchAll()]); } // GET /api/cards/due?deck={slug}&limit=N if (get_method() === 'GET' && ($segments[1] ?? '') === 'due') { $slug = get_param('deck', ''); $limit = max(1, min(50, (int) get_param('limit', 20))); $query = " SELECT c.id, c.question, c.answer, c.level, c.sort_order, c.deck_id, d.slug AS deck_slug, d.name AS deck_name, cp.next_review, cp.last_rating, cp.last_reviewed_at, cp.ease_factor, cp.interval_days, cp.repetitions FROM cards c JOIN decks d ON d.id = c.deck_id LEFT JOIN card_progress cp ON cp.card_id = c.id AND cp.user_id = :uid WHERE (cp.next_review IS NULL OR cp.next_review <= CURRENT_DATE) "; $params = [':uid' => $user_id]; if ($slug) { $query .= " AND d.slug = :slug"; $params[':slug'] = $slug; } $query .= " ORDER BY cp.next_review ASC NULLS FIRST, c.sort_order LIMIT " . $limit; $stmt = $pdo->prepare($query); $stmt->execute($params); json_ok(['cards' => $stmt->fetchAll()]); } // POST /api/cards/{id}/review if (get_method() === 'POST' && is_numeric($segments[1] ?? '') && ($segments[2] ?? '') === 'review') { $card_id = (int) $segments[1]; $body = get_json_body(); $rating = $body['rating'] ?? ''; if (!in_array($rating, ['easy', 'medium', 'hard', 'wrong'])) { json_error('Ungueltige Bewertung. Erlaubt: easy, medium, hard, wrong'); } $stmt = $pdo->prepare("SELECT id FROM cards WHERE id = :id"); $stmt->execute([':id' => $card_id]); if (!$stmt->fetch()) json_error('Karte nicht gefunden', 404); $stmt = $pdo->prepare("SELECT * FROM card_progress WHERE user_id = :uid AND card_id = :cid"); $stmt->execute([':uid' => $user_id, ':cid' => $card_id]); $progress = $stmt->fetch(); $ease = $progress['ease_factor'] ?? 2.5; $interval = $progress['interval_days'] ?? 0; $reps = $progress['repetitions'] ?? 0; $result = sm2_calculate($rating, $ease, $interval, $reps); $stmt = $pdo->prepare(" INSERT INTO card_progress (user_id, card_id, ease_factor, interval_days, repetitions, next_review, last_rating, last_reviewed_at) VALUES (:uid, :cid, :ease, :intv, :reps, :next, :rating, NOW()) ON CONFLICT (user_id, card_id) DO UPDATE SET ease_factor = :ease, interval_days = :intv, repetitions = :reps, next_review = :next, last_rating = :rating, last_reviewed_at = NOW() "); $stmt->execute([ ':uid' => $user_id, ':cid' => $card_id, ':ease' => $result['ease_factor'], ':intv' => $result['interval_days'], ':reps' => $result['repetitions'], ':next' => $result['next_review'], ':rating' => $rating ]); json_ok(['progress' => $result]); } json_error('Unbekannter Cards-Endpunkt', 404);