using System.Text.Json; using System.Text.Json.Serialization; using SeaHaven.Services.DTOs; using SeaHaven.Services.Exceptions; namespace SeaHaven.Services.Helpers { public static class WorkOrderAdditionalContactsMapper { private const int MaxNameLength = 200; private const int MaxPhoneLength = 50; private const int MaxNotesLength = 2000; private const int MaxSerializedLength = 4000; private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, PropertyNameCaseInsensitive = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; public static List? ParseJson(string? json) { if (string.IsNullOrWhiteSpace(json)) return null; try { var parsed = JsonSerializer.Deserialize>(json, JsonOptions); return parsed is { Count: > 0 } ? parsed : null; } catch (JsonException) { return null; } } public static string? SerializeForStorage(IReadOnlyList? contacts) { var normalized = NormalizeList(contacts); if (normalized.Count == 0) return null; var json = JsonSerializer.Serialize(normalized, JsonOptions); if (json.Length > MaxSerializedLength) { throw new WorkOrderBoardValidationException( "InvalidValue", "additionalContacts must serialize to at most 4000 characters."); } return json; } public static string? SerializeForPatch(string? jsonValue) { if (string.IsNullOrWhiteSpace(jsonValue)) return null; List? parsed; try { parsed = JsonSerializer.Deserialize>(jsonValue.Trim(), JsonOptions); } catch (JsonException) { throw new WorkOrderBoardValidationException( "InvalidValue", "additionalContacts must be a JSON array of { name, phone, notes? }."); } return SerializeForStorage(parsed); } public static List NormalizeList(IReadOnlyList? contacts) { var normalized = new List(); if (contacts == null || contacts.Count == 0) return normalized; foreach (var raw in contacts) { var name = raw.Name?.Trim() ?? ""; var phone = raw.Phone?.Trim() ?? ""; var notes = raw.Notes?.Trim(); if (string.IsNullOrEmpty(name) && string.IsNullOrEmpty(phone)) continue; if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(phone)) { throw new WorkOrderBoardValidationException( "InvalidValue", "Each additional contact must include a name and phone."); } if (name.Length > MaxNameLength) { throw new WorkOrderBoardValidationException( "InvalidValue", "Each additional contact name must be at most 200 characters."); } if (phone.Length > MaxPhoneLength) { throw new WorkOrderBoardValidationException( "InvalidValue", "Each additional contact phone must be at most 50 characters."); } if (notes != null && notes.Length > MaxNotesLength) { throw new WorkOrderBoardValidationException( "InvalidValue", "Each additional contact notes must be at most 2000 characters."); } normalized.Add(new WorkOrderAdditionalContactDto( name, phone, string.IsNullOrEmpty(notes) ? null : notes)); } return normalized; } } }