using System.Security.Claims; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; /// /// Track B live capture endpoints for credentialed clinicians. /// Observations entered via these endpoints skip the verification queue /// and are promoted synchronously to VigilCareClinical with full /// critical value alerting. /// [ApiController] [Route("api/v1/live-capture")] [Produces("application/json")] [Authorize(Roles = "CLINICIAN")] public class LiveCaptureController : ControllerBase { private readonly ILiveCaptureService _liveCapture; public LiveCaptureController(ILiveCaptureService liveCapture) => _liveCapture = liveCapture; /// /// Records observations against an existing VigilCareClinical encounter. /// Requires clinician attestation and password re-confirmation. /// Returns promoted observation IDs and any synchronous critical alerts. /// /// /// Track B workflow: no verification queue. Clinician attestation replaces /// the dual-human gate used in Track A (backfill). Each observation is /// promoted immediately and evaluated against critical alert thresholds /// before this response returns. /// /// Critical alerts are generated synchronously — a critical potassium /// value will have an open ClinicalAlert row in the database before /// the 201 response reaches the clinician's tablet. /// [HttpPost("encounters/{encounterId:guid}/observations")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status201Created)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status401Unauthorized)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status403Forbidden)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status409Conflict)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status422UnprocessableEntity)] public async Task RecordObservations( Guid encounterId, [FromBody] RecordObservationsRequest request) { var clinicianUserId = Guid.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!); var result = await _liveCapture.RecordObservationsAsync( encounterId, request, clinicianUserId); return StatusCode(201, ApiResponse.Created(result)); } /// /// Opens a new encounter and records initial vitals in a single request. /// Designed for outpatient workflows where the encounter does not yet /// exist in VigilCareClinical. /// /// /// Creates the encounter with status "active", records all observations, /// promotes immediately, and evaluates critical thresholds — all within /// a single database transaction. /// /// If the patient already has an active encounter, returns 409 with /// ACTIVE_ENCOUNTER_EXISTS. Use the observation-only endpoint instead. /// [HttpPost("encounters")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status201Created)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status401Unauthorized)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status403Forbidden)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status409Conflict)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status422UnprocessableEntity)] public async Task OpenEncounterWithVitals( [FromBody] OpenEncounterWithVitalsRequest request) { var clinicianUserId = Guid.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!); var result = await _liveCapture.OpenEncounterWithVitalsAsync( request, clinicianUserId); return StatusCode(201, ApiResponse.Created(result)); } }