using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using QuestionsApi.Models; using QuestionsApi.Services; using System.Security.Claims; namespace QuestionsApi.Controllers; [ApiController, Route("api/dynamic-questions"), Authorize(Roles = "USER,SUPER")] public sealed class DynamicQuestionsController : ControllerBase { private readonly DynamicQuestionService _questions; private readonly QuestionReportStore _reports; public DynamicQuestionsController(DynamicQuestionService questions, QuestionReportStore reports) { _questions = questions; _reports = reports; } [HttpGet("status")] public async Task> Status() => Ok(await _questions.StatusAsync(UserId())); [HttpPost("next")] public async Task> Next([FromBody] DynamicNextRequest request) { try { return Ok(await _questions.NextAsync(UserId(), request.RunToken)); } catch (ArgumentException exception) { return BadRequest(exception.Message); } catch (InvalidOperationException exception) { return Conflict(exception.Message); } } [HttpPost("attempt")] public async Task> Attempt([FromBody] DynamicAttemptRequest request) { if (string.IsNullOrWhiteSpace(request.GenerationToken)) return BadRequest("generationToken required"); try { return Ok(await _questions.AttemptAsync(UserId(), request)); } catch (ArgumentException exception) { return BadRequest(exception.Message); } catch (KeyNotFoundException) { return NotFound(); } catch (InvalidOperationException exception) { return Conflict(exception.Message); } } [HttpPost("report")] public async Task Report([FromBody] DynamicReportRequest request) { var category = request.Category.Trim().ToLowerInvariant(); var details = request.Details.Trim(); if (!QuestionReportCategory.All.Contains(category)) return BadRequest("category is invalid"); if (details.Length is < 10 or > 2000) return BadRequest("details must be between 10 and 2000 characters"); try { var (claims, _) = await _questions.RegenerateAsync(UserId(), request.GenerationToken); var report = await _reports.CreateDynamicAsync(claims, UserId(), category, details); return StatusCode(StatusCodes.Status201Created, new { id = report.Id, questionExternalId = report.QuestionExternalId }); } catch (ArgumentException exception) { return BadRequest(exception.Message); } catch (KeyNotFoundException) { return NotFound(); } } private string UserId() => User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.FindFirstValue(ClaimTypes.Name) ?? User.Identity?.Name ?? ""; }