using DocumentOperator.Application.Common.DTOs; using DocumentOperator.Application.Features.Documents.ExtractSwissQrCode; using DocumentOperator.Application.Features.Documents.ValidatePdf; using DocumentOperator.Domain.Models.ValueObjects; using MediatR; using Microsoft.AspNetCore.Mvc; namespace DocumentOperator.API.Endpoints.v1; /// /// Document endpoints (Minimal API) /// public static class DocumentEndpoints { /// /// Maps all document-related endpoints /// public static void MapDocumentEndpoints(this IEndpointRouteBuilder app) { var group = app.MapGroup("/api/v1/documents") .WithTags("Documents"); // POST /api/v1/documents/validate group.MapPost("/validate", ValidatePdf) .WithName("ValidatePdf") .WithSummary("Validates a PDF document and returns metadata") .WithDescription("Validates the PDF format and extracts metadata (page count, file size, PDF version, attachments)") .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError); // POST /api/v1/documents/extract-swiss-qr-code group.MapPost("/extract-swiss-qr-code", ExtractSwissQrCode) .WithName("ExtractSwissQrCode") .WithSummary("Extracts Swiss QR Code from the last page of a PDF document") .WithDescription(@"Extracts and parses a Swiss QR Code (Swiss QR Bill Standard 2.0) from the last page of a PDF document. **Requirements:** - PDF must contain a valid Swiss QR Code on the last page - QR Code must conform to Swiss QR Bill Standard 2.0 - References array is required (can be empty) **Returns:** - All QR code fields (IBAN, amount, creditor, debtor, reference, etc.) - References array (passed through from request) **Use Case:** Extract payment information from Swiss QR invoices for automated processing.") .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError); } /// /// Validates a PDF document and returns metadata /// /// PDF as Base64 string /// MediatR instance /// Cancellation token /// PDF metadata (page count, file size, etc.) /// PDF is valid, metadata returned /// Invalid PDF or Base64 format /// Internal server error during validation private static async Task ValidatePdf( ValidatePdfRequest request, IMediator mediator, CancellationToken cancellationToken) { // DTO → Query (Value Objects erstellen - kann DomainValidationException werfen!) var query = new ValidatePdfQuery( Base64String.Create(request.Base64Pdf) ); // MediatR Handler aufrufen (ValidationBehavior → Handler) var metadata = await mediator.Send(query, cancellationToken); // PdfMetadata → Response DTO var response = new ValidatePdfResponse( metadata.PageCount, metadata.FileSizeBytes, metadata.FileSizeMB, metadata.PdfVersion, metadata.HasAttachments, metadata.AttachmentCount ); return Results.Ok(response); } /// /// Extracts Swiss QR Code from the last page of a PDF document /// /// References array + PDF as Base64 string /// MediatR instance /// Cancellation token /// References (passed through) + Swiss QR Code data /// Swiss QR Code extracted successfully /// Invalid PDF or Base64 format /// No Swiss QR Code found on the last page /// Internal server error during extraction private static async Task ExtractSwissQrCode( ExtractSwissQrCodeRequest request, IMediator mediator, CancellationToken cancellationToken) { // DTO → Query (Value Objects erstellen) var query = new ExtractSwissQrCodeQuery( References: request.References, PdfContent: Base64String.Create(request.Base64Pdf) ); // MediatR Handler aufrufen var result = await mediator.Send(query, cancellationToken); // Map Domain Value Object → DTO var response = new ExtractSwissQrCodeResponse( References: result.References, QrCodeData: MapQrCodeDataToDto(result.QrCodeData) ); return Results.Ok(response); } /// /// Maps SwissQrCodeData domain value object to DTO /// private static SwissQrCodeDataDto MapQrCodeDataToDto(Domain.ValueObjects.SwissQrCodeData qrCodeData) { return new SwissQrCodeDataDto( QrType: qrCodeData.QrType, Version: qrCodeData.Version, CodingType: qrCodeData.CodingType, Iban: qrCodeData.Iban, Creditor: MapAddressToDto(qrCodeData.Creditor), UltimateCreditor: qrCodeData.UltimateCreditor != null ? MapAddressToDto(qrCodeData.UltimateCreditor) : null, Amount: qrCodeData.Amount, Currency: qrCodeData.Currency, UltimateDebtor: qrCodeData.UltimateDebtor != null ? MapAddressToDto(qrCodeData.UltimateDebtor) : null, ReferenceType: qrCodeData.ReferenceType, Reference: qrCodeData.Reference, UnstructuredMessage: qrCodeData.UnstructuredMessage, BillInformation: qrCodeData.BillInformation, AlternativeProcedureParameters: qrCodeData.AlternativeProcedureParameters ); } /// /// Maps AddressData domain value object to DTO /// private static AddressDataDto MapAddressToDto(Domain.ValueObjects.AddressData address) { return new AddressDataDto( AddressType: address.AddressType, Name: address.Name, Street: address.Street, BuildingNumber: address.BuildingNumber, AddressLine1: address.AddressLine1, AddressLine2: address.AddressLine2, PostalCode: address.PostalCode, City: address.City, Country: address.Country ); } }