using Fitness_core_BE.DTOs.Auth; using System.Text.Json; using Fitness_core_BE.Exceptions; namespace Fitness_core_BE.Helpers { public class DataExtractor { public static string GetRequiredString( Dictionary data, string key) { if (!data.TryGetValue(key, out var element)) throw new BadRequestException($"Missing required field '{key}'."); if (element.ValueKind != JsonValueKind.String) throw new BadRequestException($"Field '{key}' must be a string."); var value = element.GetString(); if (string.IsNullOrWhiteSpace(value)) return ""; //throw new BadRequestException($"Field '{key}' cannot be empty."); return value; } public static string? GetOptionalString( Dictionary data, string key) { if (!data.TryGetValue(key, out var element) || element.ValueKind == JsonValueKind.Null) return null; if (element.ValueKind == JsonValueKind.String) { var value = element.GetString(); return string.IsNullOrWhiteSpace(value) ? null : value; } if (element.ValueKind == JsonValueKind.Number) return element.GetRawText(); throw new ArgumentException($"Field '{key}' must be a string."); } public static string? GetOptionalStringByKeys( Dictionary data, params string[] keys) { foreach (var key in keys) { if (data.ContainsKey(key)) return GetOptionalString(data, key); } return null; } public static string GetRequiredStringByKeys( Dictionary data, params string[] keys) { var value = GetOptionalStringByKeys(data, keys); if (!string.IsNullOrWhiteSpace(value)) return value; throw new BadRequestException($"Missing required field '{keys[0]}'."); } // ===================== INT ===================== public static int GetRequiredInt( Dictionary data, string key) { if (!data.TryGetValue(key, out var element)) throw new BadRequestException($"Missing required field '{key}'."); if (element.TryGetInt32(out var value)) return value; if (element.ValueKind == JsonValueKind.String && int.TryParse(element.GetString(), out value)) { return value; } throw new BadRequestException($"Field '{key}' must be a valid integer."); } public static int? GetNullableInt( Dictionary data, string key) { if (!data.TryGetValue(key, out var element) || element.ValueKind == JsonValueKind.Null) return null; if (element.TryGetInt32(out var value)) return value; if (element.ValueKind == JsonValueKind.String && int.TryParse(element.GetString(), out value)) { return value; } throw new BadRequestException($"Field '{key}' must be a valid integer."); } // ===================== FLOAT ===================== public static float GetRequiredFloat( Dictionary data, string key) { if (!data.TryGetValue(key, out var element)) throw new BadRequestException($"Missing required field '{key}'."); if (element.TryGetSingle(out var value)) return value; // Allow numeric strings: "12.5" if (element.ValueKind == JsonValueKind.String && float.TryParse(element.GetString(), out value)) { return value; } throw new BadRequestException($"Field '{key}' must be a valid float."); } // ===================== DECIMAL ===================== public static decimal GetRequiredDecimal( Dictionary data, string key) { if (!data.TryGetValue(key, out var element)) throw new ArgumentException($"Missing required field '{key}'."); if (element.TryGetDecimal(out var value)) return value; // Allow numeric strings: "12.5" if (element.ValueKind == JsonValueKind.String && decimal.TryParse(element.GetString(), out value)) { return value; } throw new ArgumentException($"Field '{key}' must be a valid decimal."); } public static decimal? GetOptionalDecimal( Dictionary data, string key) { if (!data.TryGetValue(key, out var element) || element.ValueKind == JsonValueKind.Null) return null; if (element.TryGetDecimal(out var value)) return value; if (element.ValueKind == JsonValueKind.String && decimal.TryParse(element.GetString(), out value)) { return value; } throw new ArgumentException($"Field '{key}' must be a valid decimal."); } public static decimal? GetOptionalDecimalByKeys( Dictionary data, params string[] keys) { foreach (var key in keys) { if (data.ContainsKey(key)) return GetOptionalDecimal(data, key); } return null; } // ===================== BOOLEAN ===================== public static bool GetRequiredBool( Dictionary data, string key) { if (!data.TryGetValue(key, out var element)) throw new BadRequestException($"Missing required field '{key}'."); if (element.ValueKind == JsonValueKind.True) return true; if (element.ValueKind == JsonValueKind.False) return false; // Allow string values: "true" / "false" if (element.ValueKind == JsonValueKind.String && bool.TryParse(element.GetString(), out var value)) { return value; } throw new BadRequestException($"Field '{key}' must be a valid boolean."); } public static bool? GetOptionalBool( Dictionary data, string key) { if (!data.TryGetValue(key, out var element) || element.ValueKind == JsonValueKind.Null) return null; if (element.ValueKind == JsonValueKind.True) return true; if (element.ValueKind == JsonValueKind.False) return false; if (element.ValueKind == JsonValueKind.String && bool.TryParse(element.GetString(), out var value)) { return value; } throw new BadRequestException($"Field '{key}' must be a valid boolean."); } // ===================== GUID ===================== public static Guid GetRequiredGuid( Dictionary data, string key) { var value = GetRequiredString(data, key); if (!Guid.TryParse(value, out var guid)) throw new BadRequestException($"Field '{key}' must be a valid GUID."); return guid; } /// A JSON array of GUID strings, or null if the key is absent/null. An /// empty array is returned as an empty list (distinct from "field not sent"), /// which matters for "clear this plan's places" updates. public static List? GetOptionalGuidList( Dictionary data, string key) { if (!data.TryGetValue(key, out var element) || element.ValueKind == JsonValueKind.Null) return null; if (element.ValueKind != JsonValueKind.Array) throw new BadRequestException($"Field '{key}' must be an array."); var result = new List(); foreach (var item in element.EnumerateArray()) { if (item.ValueKind != JsonValueKind.String || !Guid.TryParse(item.GetString(), out var guid)) throw new BadRequestException($"Field '{key}' must contain only GUID strings."); result.Add(guid); } return result; } // ===================== DATETIME ===================== public static DateTime GetRequiredDateTime( Dictionary data, string key) { if (!data.TryGetValue(key, out var element)) throw new BadRequestException($"Missing required field '{key}'."); if (element.ValueKind == JsonValueKind.Null) throw new BadRequestException($"Field '{key}' cannot be null."); // ISO 8601 string if (element.ValueKind == JsonValueKind.String && DateTime.TryParse(element.GetString(), out var date)) { return date; } // Native JSON DateTime if (element.TryGetDateTime(out date)) { return date; } throw new BadRequestException($"Field '{key}' must be a valid DateTime."); } public static DateTime? GetOptionalDateTime( Dictionary data, string key) { if (!data.TryGetValue(key, out var element) || element.ValueKind == JsonValueKind.Null) return null; if (element.ValueKind == JsonValueKind.String && DateTime.TryParse(element.GetString(), out var date)) { return date; } if (element.TryGetDateTime(out date)) return date; throw new ArgumentException($"Field '{key}' must be a valid DateTime."); } // ===================== DATEONLY ===================== public static DateOnly GetRequiredDateOnly( Dictionary data, string key) { if (!data.TryGetValue(key, out var element)) throw new BadRequestException($"Missing required field '{key}'."); if (element.ValueKind == JsonValueKind.Null) throw new BadRequestException($"Field '{key}' cannot be null."); DateTime date; // ISO 8601 string (e.g., "2025-02-25") if (element.ValueKind == JsonValueKind.String && DateTime.TryParse(element.GetString(), out date)) { return DateOnly.FromDateTime(date); } if (element.TryGetDateTime(out date)) { return DateOnly.FromDateTime(date); } throw new BadRequestException($"Field '{key}' must be a valid date."); } /// /// A date of birth, rejected if it is today or in the future. Progress predictions /// divide by the member's age, so a zero age has to be stopped at the write rather /// than surfacing later as a failure in an unrelated screen. /// public static DateOnly GetRequiredDateOfBirth( Dictionary data, string key) { var birthDate = GetRequiredDateOnly(data, key); if (birthDate >= DateOnly.FromDateTime(DateTime.UtcNow)) throw new BadRequestException( $"Field '{key}' must be a date in the past."); return birthDate; } /// /// Same as GetRequiredDateOfBirth, but missing/null/blank is allowed and returns null /// instead of throwing -- a birthday isn't needed to run a subscription, so gyms that /// don't collect it at signup shouldn't be blocked from adding a member. /// public static DateOnly? GetOptionalDateOfBirth( Dictionary data, string key) { if (!data.TryGetValue(key, out var element) || element.ValueKind == JsonValueKind.Null) return null; if (element.ValueKind == JsonValueKind.String && string.IsNullOrWhiteSpace(element.GetString())) return null; return GetRequiredDateOfBirth(data, key); } // ===================== USER ID FROM HttpContext ===================== public static Guid GetUserIdFromContext(HttpContext httpContext) { if (httpContext == null) throw new ArgumentNullException(nameof(httpContext)); var userIdFromContext = httpContext.Items["UserId"]?.ToString() ?? httpContext.User?.FindFirst("UserId")?.Value; if (string.IsNullOrWhiteSpace(userIdFromContext) || !Guid.TryParse(userIdFromContext, out var userId)) { throw new UnauthorizedAccessException("User is not authenticated. Please login and send cookie."); } return userId; } //============================AuthResponce EXtractor============ public static AuthResponse GetRequiredAuthResponse(Dictionary data) { AuthResponse authResponse = new AuthResponse { StatusCode = GetRequiredInt(data, "StatusCode"), Success = GetRequiredBool(data, "success"), Message = GetRequiredString(data, "message"), Data = data }; return authResponse; } } }