import 'dart:convert'; import 'dart:io'; import 'package:shelf/shelf.dart'; import 'package:shelf_router/shelf_router.dart'; import '../cors_headers.dart'; import '../firebase_auth.dart'; import '../pipeline/question_pipeline.dart'; import '../question_service.dart'; import '../questions_db.dart'; const String questionsBasePath = '/v1/me/questions'; Handler questionsHandler({ required FirebaseAuthVerifier auth, required QuestionsDb questionsDb, required QuestionService questionService, QuestionPipeline? questionPipeline, }) { final Router router = Router(); router.post('$questionsBasePath/bootstrap', (Request request) async { final String? firebaseUid = await _verify(auth, request); if (firebaseUid == null) { return _jsonResponse(401, {'error': 'Unauthorized'}); } try { final Map question = await questionService.ensureStarterQuestionOnLogin(firebaseUid); final int unansweredCount = await questionsDb.countUnansweredQuestions(firebaseUid); return _jsonResponse(200, { 'question': question, 'unansweredCount': unansweredCount, }); } catch (e, st) { stderr.writeln('Bootstrap questions error: $e\n$st'); return _jsonResponse(500, {'error': 'Internal error'}); } }); router.get(questionsBasePath, (Request request) async { final String? firebaseUid = await _verify(auth, request); if (firebaseUid == null) { return _jsonResponse(401, {'error': 'Unauthorized'}); } try { final List> rows = await questionsDb.listUnansweredQuestions(firebaseUid); final List> questions = rows .map( (Map row) => questionsDb.toClientPayload( row, unansweredCount: rows.length, ), ) .toList(); return _jsonResponse(200, { 'questions': questions, 'unansweredCount': questions.length, }); } catch (e, st) { stderr.writeln('List questions error: $e\n$st'); return _jsonResponse(500, {'error': 'Internal error'}); } }); router.post( '$questionsBasePath//answer', (Request request, String id) async { final String? firebaseUid = await _verify(auth, request); if (firebaseUid == null) { return _jsonResponse(401, {'error': 'Unauthorized'}); } try { final String body = await request.readAsString(); final Map json = body.isEmpty ? {} : jsonDecode(body) as Map; final num answer = (json['answer'] as num?) ?? 0; final Map? updated = await questionsDb.submitAnswer( questionId: id, assignedUserId: firebaseUid, userResponse: answer, ); if (updated == null) { return _jsonResponse(404, {'error': 'Not found'}); } if (questionPipeline != null) { await questionPipeline.onAnswerSubmitted( firebaseUid: firebaseUid, answeredQuestion: updated, userResponse: answer, ); } final int unansweredCount = await questionsDb.countUnansweredQuestions(firebaseUid); return _jsonResponse(200, { 'question': updated, 'unansweredCount': unansweredCount, }); } catch (e, st) { stderr.writeln('Answer question error: $e\n$st'); return _jsonResponse(500, {'error': 'Internal error'}); } }, ); router.post( '$questionsBasePath//defer', (Request request, String id) async { final String? firebaseUid = await _verify(auth, request); if (firebaseUid == null) { return _jsonResponse(401, {'error': 'Unauthorized'}); } try { final Map? updated = await questionsDb.deferQuestion( questionId: id, assignedUserId: firebaseUid, ); if (updated == null) { return _jsonResponse(404, {'error': 'Not found'}); } final int unansweredCount = await questionsDb.countUnansweredQuestions(firebaseUid); return _jsonResponse(200, { 'question': updated, 'unansweredCount': unansweredCount, }); } catch (e, st) { stderr.writeln('Defer question error: $e\n$st'); return _jsonResponse(500, {'error': 'Internal error'}); } }, ); return (Request request) async { if (request.method == 'OPTIONS' && request.requestedUri.path.startsWith(questionsBasePath)) { return Response.ok('', headers: apiCorsHeaders()); } final String? firebaseUid = await _verify(auth, request); if (firebaseUid == null) { return _jsonResponse(401, {'error': 'Unauthorized'}); } final Response response = await router.call(request); if (response.statusCode != 404) { return response; } return _jsonResponse(404, {'error': 'Not found'}); }; } Future _verify(FirebaseAuthVerifier auth, Request request) { return auth.verifyBearerToken( request.headers['Authorization'] ?? request.headers['authorization'], ); } Response _jsonResponse(int status, Map body) { return Response( status, body: jsonEncode(body), headers: { ...apiCorsHeaders(), 'Content-Type': 'application/json', }, ); }