

@router.get("/sessions")
def list_quiz_sessions(
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """One management row per accessible quiz: attempt state, progress and last score.

    Powers the session-management view (start / resume / review + analysis,
    repeat, rename, delete) without the client fanning out per-quiz requests.
    """
    quizzes = (
        db.query(Quiz)
        .filter(Quiz.deleted_at.is_(None), Quiz.course_id.is_(None))
        .filter(general_quiz_visibility(current_user))
        .order_by(Quiz.created_at.desc())
        .all()
    )
    if not quizzes:
        return []
    quiz_ids = [q.id for q in quizzes]

    attempts = (
        db.query(QuizAttempt)
        .filter(QuizAttempt.user_id == current_user.id, QuizAttempt.quiz_id.in_(quiz_ids))
        .order_by(QuizAttempt.started_at)
        .all()
    )
    active: dict[int, QuizAttempt] = {}
    finished: dict[int, list[QuizAttempt]] = {}
    for attempt in attempts:
        if attempt.completed_at is None:
            active[attempt.quiz_id] = attempt  # newest wins (ordered by started_at)
        elif not attempt.expired:
            finished.setdefault(attempt.quiz_id, []).append(attempt)

    # Answered counts for live attempts come from the Redis progress blob; a
    # Redis outage degrades to "0 answered" rather than failing the whole page.
    answered: dict[int, int] = {}
    if active:
        try:
            import json as _json

            import redis as redis_lib

            from app.config import settings

            r = redis_lib.from_url(settings.REDIS_URL, decode_responses=True)
            keys = [f"quiz_progress:{current_user.id}:{a.id}" for a in active.values()]
            for attempt, raw in zip(active.values(), r.mget(keys)):
                if raw:
                    answered[attempt.id] = len(_json.loads(raw).get("answers", {}) or {})
        except Exception:
            logger.warning("Redis unavailable for session progress", exc_info=True)

    categories = {c.id: c.name for c in db.query(QuizCategory).all()}
    rows = []
    for quiz in quizzes:
        live = active.get(quiz.id)
        done = finished.get(quiz.id, [])
        last = done[-1] if done else None
        per_attempt = quiz.questions_per_attempt or quiz.questions_count or 0

        def pct(attempt):
            return round(attempt.score / attempt.total_questions * 100) if attempt.total_questions else 0

        last_activity = (
            live.started_at if live else last.completed_at if last else quiz.created_at
        )
        rows.append({
            "quiz_id": quiz.id,
            "title": quiz.title,
            "mode": quiz.mode,
            "origin": quiz.origin,
            "questions_count": quiz.questions_count,
            "questions_per_attempt": per_attempt,
            "time_limit_minutes": quiz.time_limit_minutes,
            "category_id": quiz.category_id,
            "category_name": categories.get(quiz.category_id),
            "is_published": quiz.is_published,
            "is_shared": quiz.is_shared,
            "is_owner": quiz.user_id == current_user.id,
            "created_at": quiz.created_at.isoformat() if quiz.created_at else None,
            "state": "in_progress" if live else "completed" if last else "not_started",
            "active_attempt_id": live.id if live else None,
            "answered": answered.get(live.id, 0) if live else (last.total_questions if last else 0),
            "total": (live.total_questions or per_attempt) if live else per_attempt,
            "attempts_count": len(done),
            "last_attempt_id": last.id if last else None,
            "last_percentage": pct(last) if last else None,
            "last_score": last.score if last else None,
            "last_total": last.total_questions if last else None,
            "last_completed_at": last.completed_at.isoformat() if last else None,
            "best_percentage": max((pct(a) for a in done), default=None),
            "last_activity": last_activity.isoformat() if last_activity else None,
        })
    rows.sort(key=lambda r: (r["last_activity"] or ""), reverse=True)
    return rows
