using System.Text.Json; using BigRegister.Api.Data; using BigRegister.Domain.Applications; using BigRegister.Domain.Diplomas; using BigRegister.Domain.Documents; using BigRegister.Domain.People; using BigRegister.Domain.Registrations; namespace BigRegister.Api.Contracts; /// Domain → wire DTO mapping (the anti-corruption boundary, server side). public static class Mappers { private static string D(DateOnly d) => d.ToString("yyyy-MM-dd"); public static RegistrationStatusDto ToDto(this RegistrationStatus s) => s switch { RegistrationStatus.Geregistreerd g => new(s.Tag.ToString(), HerregistratieDatum: D(g.HerregistratieDatum)), RegistrationStatus.Geschorst g => new(s.Tag.ToString(), GeschorstTot: D(g.GeschorstTot), Reden: g.Reden), RegistrationStatus.Doorgehaald d => new(s.Tag.ToString(), DoorgehaaldOp: D(d.DoorgehaaldOp), Reden: d.Reden), _ => throw new ArgumentOutOfRangeException(nameof(s), s, "Unknown RegistrationStatus variant"), }; public static RegistrationDto ToDto(this Registration r) => new( r.BigNummer, r.Naam, r.Beroep, D(r.Registratiedatum), D(r.Geboortedatum), r.Status.ToDto()); public static AdresDto ToDto(this Adres a) => new(a.Straat, a.Postcode, a.Woonplaats); public static PersonDto ToDto(this Person p) => new(p.Naam, D(p.Geboortedatum), p.Adres.ToDto()); public static PolicyQuestionDto ToDto(this PolicyQuestion q) => new( q.Id, q.Vraag, q.Type == QuestionType.JaNee ? "ja-nee" : "tekst"); public static DuoDiplomaDto ToDto(this Diploma d) => new( d.Id, d.Naam, d.Instelling, d.Jaar, DiplomaRules.ProfessionFor(d), DiplomaRules.QuestionsFor(d).Select(q => q.ToDto()).ToList()); public static DocumentCategoryDto ToDto(this DocumentCategory c) => new( c.CategoryId, c.Label, c.Description, c.Required, c.AcceptedTypes, c.MaxSizeMb, c.Multiple, c.AllowPostDelivery); /// Wire projection of an — the null "Concept" case /// is the one place a status has no , so it becomes the wire /// convention's magic string here at the boundary rather than living inside the domain type. /// Shared by and ZgwZaakMapper, so both status producers /// agree on the projection (WP-68 F3). public static AanvraagStatusDto ToDto(this AanvraagStatus s) => new( s.Tag?.ToString() ?? "Concept", s.StepIndex, s.StepCount, s.Referentie, s.Manual, s.Reden); // Aanvraag status is COMPUTED ON READ (see the StatusAt extension, Data/AanvraagMapper.cs) — // this is now a one-line projection of that onto the wire DTO (WP-68 F3, WP-73). public static AanvraagStatusDto ToStatusDto(this Aanvraag a, DateTimeOffset now) => a.StatusAt(now).ToDto(); /// SubmittedAt only exists once Submitted/Decided (WP-73) — null for a Concept, /// same as the wire DTO's own nullable field. private static string? SubmittedAtOf(Aanvraag a) => a switch { Aanvraag.Concept => null, Aanvraag.Submitted s => s.SubmittedAt.ToString("o"), Aanvraag.Decided d => d.SubmittedAt.ToString("o"), _ => null, }; /// Draft only exists pre-submission (WP-73) — null once Submitted/Decided (nothing /// reads it past that point; see AanvraagMapper.ApplyTo's Submitted branch). private static JsonElement? DraftOf(Aanvraag a) => a is Aanvraag.Concept c ? c.Draft : null; public static ApplicationSummaryDto ToSummaryDto(this Aanvraag a, DateTimeOffset now) => new( a.Id, a.Type, a.ToStatusDto(now), a.DocumentIds, a.CreatedAt.ToString("o"), a.UpdatedAt.ToString("o"), SubmittedAtOf(a)); /// Admin summary — same shape plus the owner (WP-36; the user-facing list leaves Owner null). public static ApplicationSummaryDto ToAdminSummaryDto(this Aanvraag a, DateTimeOffset now) => a.ToSummaryDto(now) with { Owner = a.Owner }; public static ApplicationDetailDto ToDetailDto(this Aanvraag a, DateTimeOffset now) => new( a.Id, a.Type, a.ToStatusDto(now), DraftOf(a), a.DocumentIds, a.CreatedAt.ToString("o"), a.UpdatedAt.ToString("o"), SubmittedAtOf(a)); }