using System.Security.Claims; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; [ApiController] [Route("api/v1/cover-sheets")] [Produces("application/json")] [Authorize] public class CoverSheetController : ControllerBase { private readonly ICoverSheetService _coverSheets; public CoverSheetController(ICoverSheetService coverSheets) { _coverSheets = coverSheets; } /// /// Generates one or more cover sheets with unique barcode codes. /// Each cover sheet encodes batch type, track, optional patient, and /// optional entry clerk assignment. /// [HttpPost("generate")] [Authorize(Roles = "INTAKE_CLERK,ADMINISTRATOR")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status201Created)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task Generate([FromBody] GenerateCoverSheetsRequest request) { var actorUserId = Guid.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!); var sheets = await _coverSheets.GenerateAsync(request, actorUserId); var response = sheets.Select(MapToResponse).ToList(); return StatusCode(201, ApiResponse>.Created(response)); } /// /// Looks up a cover sheet by its barcode code. Used during barcode-assisted /// upload to auto-populate batch type, track, patient, and clerk assignment. /// [HttpGet("lookup/{code}")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task Lookup(string code) { var sheet = await _coverSheets.LookupByCodeAsync(code); if (sheet is null) return NotFound(ApiResponse.Fail(404, "Cover sheet not found.", "COVER_SHEET_NOT_FOUND")); return Ok(ApiResponse.Ok(MapToResponse(sheet))); } /// /// Lists cover sheets with optional filters. /// [HttpGet] [Authorize(Roles = "INTAKE_CLERK,ADMINISTRATOR")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task List( [FromQuery] bool? isUsed, [FromQuery] Guid? patientId, [FromQuery] int page = 1, [FromQuery] int pageSize = 20) { var sheets = await _coverSheets.ListAsync(isUsed, patientId, page, pageSize); var response = sheets.Select(MapToResponse).ToList(); return Ok(ApiResponse>.Ok(response)); } /// /// Generates a printable PDF containing cover sheets with QR codes. /// Each page has the cover sheet code as a QR code, plus human-readable /// batch type, patient info, and generation date. /// [HttpPost("{id:guid}/pdf")] [Authorize(Roles = "INTAKE_CLERK,ADMINISTRATOR")] [ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task GeneratePdf(Guid id) { var sheet = await _coverSheets.LookupByIdAsync(id); if (sheet is null) return NotFound(ApiResponse.Fail(404, "Cover sheet not found.", "COVER_SHEET_NOT_FOUND")); var pdfBytes = CoverSheetPdfGenerator.Generate(sheet); return File(pdfBytes, "application/pdf", $"coversheet-{sheet.Code}.pdf"); } /// /// Generates a batch PDF containing multiple cover sheets (one per page). /// Accepts a list of cover sheet IDs. /// [HttpPost("batch-pdf")] [Authorize(Roles = "INTAKE_CLERK,ADMINISTRATOR")] [ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)] public async Task GenerateBatchPdf([FromBody] BatchPdfRequest request) { var sheets = await _coverSheets.GetByIdsAsync(request.CoverSheetIds); var pdfBytes = CoverSheetPdfGenerator.GenerateBatch(sheets); return File(pdfBytes, "application/pdf", $"coversheets-batch-{DateTime.UtcNow:yyyyMMdd}.pdf"); } private static CoverSheetResponse MapToResponse(CoverSheet sheet) => new( Id: sheet.Id, Code: sheet.Code, BatchType: sheet.BatchType.ToDbString(), Track: sheet.Track.ToDbString(), PatientId: sheet.PatientId, PatientName: sheet.Patient?.FullName, PatientMrn: sheet.Patient?.Mrn, AssignToUserId: sheet.AssignToUserId, AssignToUserName: sheet.AssignToUser?.FullName, IsUsed: sheet.IsUsed, BatchId: sheet.BatchId, CreatedAt: sheet.CreatedAt, UsedAt: sheet.UsedAt ); }