return $this->getRoadmapById((int) $this->pdo->lastInsertId()); } public function updateRoadmapStatus($sessionId, $roadmapId, $status) { $session = $this->getSession($sessionId); $userId = (int) $session['id']; $normalizedStatus = $this->normalizeRoadmapStatus($status); $stmt = $this->pdo->prepare(" UPDATE roadmaps SET status = ?, updated_at = datetime('now') WHERE id = ? AND user_id = ? "); $stmt->execute([$normalizedStatus, $roadmapId, $userId]); return $this->getRoadmapById($roadmapId); } public function confirmRoadmapContinuation($sessionId, $roadmapId) { $session = $this->getSession($sessionId); $userId = (int) $session['id']; $stmt = $this->pdo->prepare(" UPDATE roadmaps SET continuation_confirmed = 1, scope = CASE WHEN scope = 'trial' THEN 'standard' ELSE scope END, updated_at = datetime('now') WHERE id = ? AND user_id = ? "); $stmt->execute([$roadmapId, $userId]); return $this->getRoadmapById($roadmapId); } private function getActiveRoadmapForUser(int $userId): ?array { try { $stmt = $this->pdo->prepare(" SELECT * FROM roadmaps WHERE user_id = ? AND status = 'active' ORDER BY updated_at DESC LIMIT 1 "); $stmt->execute([$userId]); $row = $stmt->fetch(); if (!$row) { return null; } return $this->hydrateRoadmapRow($row); } catch (Throwable $e) { return null; } } private function archiveActiveRoadmaps(int $userId): void { try { $stmt = $this->pdo->prepare(" UPDATE roadmaps SET status = 'archived', updated_at = datetime('now') WHERE user_id = ? AND status = 'active' "); $stmt->execute([$userId]); } catch (Throwable $e) { // Table may not exist yet; ignore to avoid breaking session load } } private function getRoadmapById(int $roadmapId): ?array { try { $stmt = $this->pdo->prepare("SELECT * FROM roadmaps WHERE id = ?"); $stmt->execute([$roadmapId]); $row = $stmt->fetch(); return $row ? $this->hydrateRoadmapRow($row) : null; } catch (Throwable $e) { return null; } } private function hydrateRoadmapRow(array $row): array { return [ 'id' => (int) $row['id'], 'status' => $row['status'] ?? 'active', 'scope' => $row['scope'] ?? 'trial', 'intent' => json_decode($row['intent'] ?? '[]', true) ?: [], 'plan' => json_decode($row['plan'] ?? '[]', true) ?: [], 'continuationConfirmed' => !empty($row['continuation_confirmed']), 'createdAt' => $row['created_at'] ?? null, 'updatedAt' => $row['updated_at'] ?? null, ]; } private function normalizeRoadmapStatus($status): string { $value = strtolower(trim((string) $status)); $allowed = ['active', 'completed', 'archived']; return in_array($value, $allowed, true) ? $value : 'active'; } private function normalizeRoadmapScope($scope): string { $value = strtolower(trim((string) $scope)); $allowed = ['trial', 'standard']; return in_array($value, $allowed, true) ? $value : 'trial'; } private function normalizeTrack($level) { if ($level === null) { return null; } $map = [ 'a1' => 'Beginner', 'a2' => 'Beginner', 'b1' => 'Intermediate', 'b2' => 'Intermediate', 'beginner' => 'Beginner', 'intermediate' => 'Intermediate', ]; $normalized = strtolower(trim((string) $level)); if (isset($map[$normalized])) { return $map[$normalized]; } if ($normalized !== '' && $normalized[0] === 'c') { return 'Intermediate'; } return $level; } /** * Normalize language input */ private function normalizeLanguage($language) { $value = strtolower(trim((string) $language)); if ($value === '') { return 'German'; } if (strpos($value, 'en') === 0) { return 'English'; } if (strpos($value, 'de') === 0 || strpos($value, 'ger') === 0) { return 'German'; } return 'German'; } } ?>