Files
Core_Be/Services/MealService.cs
T
Dhananjaya99 bffc9b1686 first commit
2026-09-28 11:44:26 +05:30

976 lines
41 KiB
C#

using Fitness_core_BE.Models;
using Fitness_core_BE.Helpers;
using Fitness_core_BE.Infra.UoW;
using Fitness_core_BE.Interfaces;
using Fitness_core_BE.Interfaces.Services;
using Fitness_core_BE.Exceptions;
namespace Fitness_core_BE.Services.Meal
{
public class MealService : IMealService
{
private readonly IMealRepository _repository;
private readonly UserRoleHelper _userRoleHelper;
private readonly IUnitOfWork _uow;
private readonly IActivityLogService _activityLogService;
private readonly IPushNotificationService _pushNotificationService;
public MealService(IMealRepository repository, UserRoleHelper userRoleHelper, IUnitOfWork uow, IActivityLogService activityLogService, IPushNotificationService pushNotificationService)
{
_repository = repository;
_userRoleHelper = userRoleHelper;
_uow = uow;
_activityLogService = activityLogService;
_pushNotificationService = pushNotificationService;
}
// Meal Category operations
public async Task<object> GetMealCategoryList(object payload, HttpContext httpContext)
{
try
{
var categories = await _repository.GetMealCategoryListAsync();
return categories;
}
catch
{
throw;
}
}
public async Task<object> CreateMealCategory(object payload, HttpContext httpContext)
{
await _uow.BeginAsync();
try
{
var data = DeserializePayload.Deserialize(payload);
var mealCategory = new MealCategory
{
Name = data["name"].GetString()!,
Description = data.ContainsKey("description") ? data["description"].GetString() : null,
CreateDate = DateTime.UtcNow
};
await _repository.CreateMealCategoryAsync(mealCategory);
await _uow.CommitAsync();
try
{
await _activityLogService.LogActivity(new { description = $"Meal category created with ID {mealCategory.MealCategoryId}" }, httpContext);
}
catch (Exception logEx)
{
Console.WriteLine($"Activity log failed (non-critical): {logEx.Message}");
}
return mealCategory;
}
catch
{
await _uow.RollbackAsync();
throw;
}
}
// Meal Plan operations
public async Task<object> GetMealPlanList(object payload, HttpContext httpContext)
{
Guid userId = DataExtractor.GetUserIdFromContext(httpContext);
string userType = await _userRoleHelper.GetUserType(userId);
Guid clientId;
if (userType == "Customer")
{
Guid cusId = await _userRoleHelper.GetCusId(userId);
try
{
clientId = await _userRoleHelper.GetClientIdByCusId(cusId);
}
catch (NotFoundException)
{
// Not every customer has joined a gym yet — they should still see the
// platform-wide admin suggestions (FKClientId == null in the repo query).
clientId = Guid.Empty;
}
}
else
{
clientId = await _userRoleHelper.GetClientId(userId);
}
try
{
var mealPlans = await _repository.GetMealPlanListAsync(userType, clientId);
return mealPlans;
}
catch
{
throw;
}
}
public async Task<object> GetMealPlanById(object payload, HttpContext httpContext)
{
try
{
var data = DeserializePayload.Deserialize(payload);
Guid mealPlanId = Guid.Parse(data["mealPlanId"].GetString()!);
var mealPlan = await _repository.GetMealPlanByIdAsync(mealPlanId);
if (mealPlan == null)
throw new KeyNotFoundException($"Meal plan with ID {mealPlanId} not found");
// Parse MealItems JSON strings to objects in nested meal items
if (mealPlan.MealItems != null)
{
foreach (var item in mealPlan.MealItems)
{
var itemAsString = item.MealItems as string;
if (!string.IsNullOrEmpty(itemAsString))
{
try
{
item.MealItems = System.Text.Json.JsonSerializer.Deserialize<object>(itemAsString);
}
catch
{
// Keep as string if parsing fails
}
}
}
}
return mealPlan;
}
catch
{
throw new Exception("An error occurred while fetching the meal plan details.");
}
}
public async Task<object> GetMealPlanByCustomerId(object payload, HttpContext httpContext)
{
try
{
Guid customerId;
var data = DeserializePayload.Deserialize(payload);
if (data.ContainsKey("customerId") &&
!string.IsNullOrEmpty(data["customerId"].GetString()))
{
customerId = Guid.Parse(data["customerId"].GetString()!);
}
else
{
Guid userId = DataExtractor.GetUserIdFromContext(httpContext);
customerId = await _userRoleHelper.GetCusId(userId);
}
var mealPlans = await _repository.GetMealPlansByCustomerIdAsync(customerId);
foreach (var mealPlan in mealPlans)
{
if (mealPlan.MealItems != null)
{
foreach (var item in mealPlan.MealItems)
{
var itemAsString = item.MealItems as string;
if (!string.IsNullOrEmpty(itemAsString))
{
try
{
item.MealItems = System.Text.Json.JsonSerializer.Deserialize<object>(itemAsString);
}
catch
{
// Keep as string if parsing fails
}
}
}
}
}
return mealPlans;
}
catch
{
throw new Exception("An error occurred while fetching meal plans for the customer.");
}
}
public async Task<object> CreateMealPlan(object payload, HttpContext httpContext)
{
await _uow.BeginAsync();
try
{
var data = DeserializePayload.Deserialize(payload);
Guid userId = DataExtractor.GetUserIdFromContext(httpContext);
Guid clientId = await _userRoleHelper.GetClientId(userId);
var newMealCategoryId = Guid.Parse(data["mealCategoryId"].GetString()!);
if (!await _repository.MealCategoryExistsAsync(newMealCategoryId))
throw new NotFoundException("Meal category not found.");
// Create meal plan
var mealPlan = new MealPlan
{
Name = data["name"].GetString()!,
Description = data.ContainsKey("description") ? data["description"].GetString() : null,
IsTemplate = data.ContainsKey("isTemplate") && data["isTemplate"].GetBoolean(),
FKMealCategoryId = newMealCategoryId,
FKClientId = clientId,
CreateDate = DateTime.UtcNow
};
await _repository.CreateMealPlanAsync(mealPlan);
// Handle customer assignments if provided
if (data.ContainsKey("customerIds") && data["customerIds"].ValueKind == System.Text.Json.JsonValueKind.Array)
{
var customerIdsArray = data["customerIds"].EnumerateArray();
foreach (var customerIdElement in customerIdsArray)
{
if (Guid.TryParse(customerIdElement.GetString(), out var customerId))
{
var customerMeal = new CustomerMeal
{
FKCustomerId = customerId,
FKMealPlanId = mealPlan.MealPlanId,
AssignedDate = DateTime.UtcNow,
IsActive = true
};
await _repository.AssignMealToCustomerAsync(customerMeal);
}
}
}
// Backward compatibility: single customerId field
else if (data.ContainsKey("customerId") && !string.IsNullOrEmpty(data["customerId"].GetString()))
{
var customerId = Guid.Parse(data["customerId"].GetString()!);
var customerMeal = new CustomerMeal
{
FKCustomerId = customerId,
FKMealPlanId = mealPlan.MealPlanId,
AssignedDate = DateTime.UtcNow,
IsActive = true
};
await _repository.AssignMealToCustomerAsync(customerMeal);
}
// Handle meal items if provided
if (data.ContainsKey("mealItems") && data["mealItems"].ValueKind == System.Text.Json.JsonValueKind.Array)
{
var mealItemsArray = data["mealItems"].EnumerateArray();
foreach (var mealItemData in mealItemsArray)
{
// Only create meal if name is provided
if (mealItemData.TryGetProperty("name", out var nameElement) &&
!string.IsNullOrEmpty(nameElement.GetString()))
{
// Parse nested mealItems JSON
string? mealItemsJson = null;
if (mealItemData.TryGetProperty("mealItems", out var mealItemsElement))
{
if (mealItemsElement.ValueKind == System.Text.Json.JsonValueKind.Array)
{
mealItemsJson = System.Text.Json.JsonSerializer.Serialize(mealItemsElement);
}
else if (mealItemsElement.ValueKind == System.Text.Json.JsonValueKind.String)
{
mealItemsJson = mealItemsElement.GetString();
}
}
var meal = new Models.Meal
{
Name = nameElement.GetString()!,
Description = mealItemData.TryGetProperty("description", out var desc) ? desc.GetString() : null,
Calories = TryParseDecimal(mealItemData, "calories"),
Protein = TryParseDecimal(mealItemData, "protein"),
Carbohydrates = TryParseDecimal(mealItemData, "carbohydrates"),
Fats = TryParseDecimal(mealItemData, "fats"),
ServingSize = mealItemData.TryGetProperty("servingSize", out var serving) ? serving.GetString() : null,
MealItems = mealItemsJson,
PreparationNotes = mealItemData.TryGetProperty("preparationNotes", out var notes) ? notes.GetString() : null,
FKMealPlanId = mealPlan.MealPlanId,
CreateDate = DateTime.UtcNow
};
await _repository.CreateMealAsync(meal);
}
}
}
await _uow.CommitAsync();
try
{
await _activityLogService.LogActivity(
new { description = $"Meal plan created with ID {mealPlan.MealPlanId}" },
httpContext
);
}
catch (Exception logEx)
{
Console.WriteLine($"Activity log failed (non-critical): {logEx.Message}");
}
// Fetch and return the created meal plan with parsed JSON
var createdMealPlan = await _repository.GetMealPlanByIdAsync(mealPlan.MealPlanId);
if (createdMealPlan == null)
throw new KeyNotFoundException($"Meal plan with ID {mealPlan.MealPlanId} not found after creation");
// Parse MealItems JSON strings to objects in nested meal items
if (createdMealPlan.MealItems != null)
{
foreach (var item in createdMealPlan.MealItems)
{
var itemAsString = item.MealItems as string;
if (!string.IsNullOrEmpty(itemAsString))
{
try
{
item.MealItems = System.Text.Json.JsonSerializer.Deserialize<object>(itemAsString);
}
catch
{
// Keep as string if parsing fails
}
}
}
}
return createdMealPlan;
}
catch
{
await _uow.RollbackAsync();
throw;
}
}
// Plans created via the AI chat flow aren't given a category by the customer — the
// mobile app used to guess one by fetching the category list and taking the first
// entry, which was both an extra round-trip and an arbitrary pick. Categories are a
// global taxonomy (MealCategory has no FKClientId), so a single well-known "AI
// Generated" category is get-or-created here and reused for every gym's AI plans.
private const string AiGeneratedCategoryName = "AI Generated";
private async Task<Guid> GetOrCreateAiMealCategoryIdAsync()
{
var categories = await _repository.GetMealCategoryListAsync();
var existing = categories.FirstOrDefault(c =>
string.Equals(c.Name, AiGeneratedCategoryName, StringComparison.OrdinalIgnoreCase));
if (existing != null)
return existing.MealCategoryId;
var created = await _repository.CreateMealCategoryAsync(new MealCategory
{
MealCategoryId = Guid.NewGuid(),
Name = AiGeneratedCategoryName,
Description = "Plans generated by the AI meal planner",
IsActive = true,
CreateDate = DateTime.UtcNow
});
return created.MealCategoryId;
}
public async Task<object> CreateCustomerMealPlan(object payload, HttpContext httpContext)
{
await _uow.BeginAsync();
try
{
var data = DeserializePayload.Deserialize(payload);
Guid customerId;
if (data.ContainsKey("customerId") &&
!string.IsNullOrEmpty(data["customerId"].GetString()))
{
customerId = Guid.Parse(data["customerId"].GetString()!);
}
else
{
Guid userId = DataExtractor.GetUserIdFromContext(httpContext);
customerId = await _userRoleHelper.GetCusId(userId);
}
// The plan belongs to the customer's own gym, not a fixed placeholder client.
Guid clientId = await _userRoleHelper.GetClientIdByCusId(customerId);
Guid mealCategoryId = data.ContainsKey("mealCategoryId") && !string.IsNullOrEmpty(data["mealCategoryId"].GetString())
? Guid.Parse(data["mealCategoryId"].GetString()!)
: await GetOrCreateAiMealCategoryIdAsync();
var mealPlan = new MealPlan
{
Name = data["name"].GetString()!,
Description = data.ContainsKey("description") ? data["description"].GetString() : null,
IsTemplate = data.ContainsKey("isTemplate") && data["isTemplate"].GetBoolean(),
FKMealCategoryId = mealCategoryId,
FKClientId = clientId,
CreateDate = DateTime.UtcNow
};
await _repository.CreateMealPlanAsync(mealPlan);
if (data.ContainsKey("mealItems") && data["mealItems"].ValueKind == System.Text.Json.JsonValueKind.Array)
{
var mealItemsArray = data["mealItems"].EnumerateArray();
foreach (var mealItemData in mealItemsArray)
{
if (mealItemData.TryGetProperty("name", out var nameElement) &&
!string.IsNullOrEmpty(nameElement.GetString()))
{
string? mealItemsJson = null;
if (mealItemData.TryGetProperty("mealItems", out var mealItemsElement))
{
if (mealItemsElement.ValueKind == System.Text.Json.JsonValueKind.Array)
{
mealItemsJson = System.Text.Json.JsonSerializer.Serialize(mealItemsElement);
}
else if (mealItemsElement.ValueKind == System.Text.Json.JsonValueKind.String)
{
mealItemsJson = mealItemsElement.GetString();
}
}
var meal = new Models.Meal
{
Name = nameElement.GetString()!,
Description = mealItemData.TryGetProperty("description", out var desc) ? desc.GetString() : null,
Calories = TryParseDecimal(mealItemData, "calories"),
Protein = TryParseDecimal(mealItemData, "protein"),
Carbohydrates = TryParseDecimal(mealItemData, "carbohydrates"),
Fats = TryParseDecimal(mealItemData, "fats"),
ServingSize = mealItemData.TryGetProperty("servingSize", out var serving) ? serving.GetString() : null,
MealItems = mealItemsJson,
PreparationNotes = mealItemData.TryGetProperty("preparationNotes", out var notes) ? notes.GetString() : null,
FKMealPlanId = mealPlan.MealPlanId,
CreateDate = DateTime.UtcNow
};
await _repository.CreateMealAsync(meal);
}
}
}
var customerMeal = new CustomerMeal
{
FKCustomerId = customerId,
FKMealPlanId = mealPlan.MealPlanId,
AssignedDate = DateTime.UtcNow,
IsActive = true,
IsFollowing = true
};
await _repository.AssignMealToCustomerAsync(customerMeal);
await _uow.CommitAsync();
try
{
await _activityLogService.LogActivity(
new { description = $"Customer meal plan created with ID {mealPlan.MealPlanId}" },
httpContext
);
}
catch (Exception logEx)
{
Console.WriteLine($"Activity log failed (non-critical): {logEx.Message}");
}
var createdMealPlan = await _repository.GetMealPlanByIdAsync(mealPlan.MealPlanId);
if (createdMealPlan == null)
throw new KeyNotFoundException($"Meal plan with ID {mealPlan.MealPlanId} not found after creation");
if (createdMealPlan.MealItems != null)
{
foreach (var item in createdMealPlan.MealItems)
{
var itemAsString = item.MealItems as string;
if (!string.IsNullOrEmpty(itemAsString))
{
try
{
item.MealItems = System.Text.Json.JsonSerializer.Deserialize<object>(itemAsString);
}
catch
{
// Keep as string if parsing fails
}
}
}
}
return createdMealPlan;
}
catch
{
await _uow.RollbackAsync();
throw;
}
}
// Meal Item operations
public async Task<object> GetMealItemsByMealPlanId(object payload, HttpContext httpContext)
{
try
{
var data = DeserializePayload.Deserialize(payload);
Guid mealPlanId = Guid.Parse(data["mealPlanId"].GetString()!);
var mealItems = await _repository.GetMealsByMealPlanIdAsync(mealPlanId);
// Parse MealItems JSON strings to objects
foreach (var item in mealItems)
{
var itemAsString = item.MealItems as string;
if (!string.IsNullOrEmpty(itemAsString))
{
try
{
item.MealItems = System.Text.Json.JsonSerializer.Deserialize<object>(itemAsString);
}
catch
{
// Keep as string if parsing fails
}
}
}
return mealItems;
}
catch
{
throw;
}
}
public async Task<object> CreateMealItem(object payload, HttpContext httpContext)
{
await _uow.BeginAsync();
try
{
var data = DeserializePayload.Deserialize(payload);
// Parse mealItems array and serialize to JSON string
string? mealItemsJson = null;
if (data.ContainsKey("mealItems"))
{
var mealItemsElement = data["mealItems"];
if (mealItemsElement.ValueKind == System.Text.Json.JsonValueKind.Array)
{
mealItemsJson = System.Text.Json.JsonSerializer.Serialize(mealItemsElement);
}
else if (mealItemsElement.ValueKind == System.Text.Json.JsonValueKind.String)
{
mealItemsJson = mealItemsElement.GetString();
}
}
var mealItem = new Models.Meal
{
Name = data["name"].GetString()!,
Description = data.ContainsKey("description") ? data["description"].GetString() : null,
Calories = data.ContainsKey("calories") && !string.IsNullOrEmpty(data["calories"].GetString())
? decimal.Parse(data["calories"].GetString()!)
: (decimal?)null,
Protein = data.ContainsKey("protein") && !string.IsNullOrEmpty(data["protein"].GetString())
? decimal.Parse(data["protein"].GetString()!)
: (decimal?)null,
Carbohydrates = data.ContainsKey("carbohydrates") && !string.IsNullOrEmpty(data["carbohydrates"].GetString())
? decimal.Parse(data["carbohydrates"].GetString()!)
: (decimal?)null,
Fats = data.ContainsKey("fats") && !string.IsNullOrEmpty(data["fats"].GetString())
? decimal.Parse(data["fats"].GetString()!)
: (decimal?)null,
ServingSize = data.ContainsKey("servingSize") ? data["servingSize"].GetString() : null,
MealItems = mealItemsJson,
PreparationNotes = data.ContainsKey("preparationNotes") ? data["preparationNotes"].GetString() : null,
FKMealPlanId = Guid.Parse(data["mealPlanId"].GetString()!),
CreateDate = DateTime.UtcNow
};
await _repository.CreateMealAsync(mealItem);
await _uow.CommitAsync();
// Log activity - non-blocking, failures won't crash the operation
try
{
await _activityLogService.LogActivity(new { description = $"Meal item created with ID {mealItem.MealId}" }, httpContext);
}
catch (Exception logEx)
{
Console.WriteLine($"Activity log failed (non-critical): {logEx.Message}");
}
// Parse mealItems JSON string to object before returning
object? parsedMealItems = mealItem.MealItems;
if (!string.IsNullOrEmpty(mealItem.MealItems))
{
try
{
parsedMealItems = System.Text.Json.JsonSerializer.Deserialize<object>(mealItem.MealItems);
}
catch
{
// Keep as string if parsing fails
}
}
return new
{
mealItem.MealId,
mealItem.Name,
mealItem.Description,
mealItem.Calories,
mealItem.Protein,
mealItem.Carbohydrates,
mealItem.Fats,
mealItem.ServingSize,
MealItems = parsedMealItems,
mealItem.PreparationNotes,
FKMealPlanId = mealItem.FKMealPlanId,
mealItem.IsActive,
mealItem.CreateDate
};
}
catch
{
await _uow.RollbackAsync();
throw;
}
}
// Update Meal Plan
public async Task<object> UpdateMealPlan(object payload, HttpContext httpContext)
{
await _uow.BeginAsync();
try
{
var data = DeserializePayload.Deserialize(payload);
Guid mealPlanId = Guid.Parse(data["mealPlanId"].GetString()!);
var existingMealPlan = await _repository.GetMealPlanEntityByIdAsync(mealPlanId);
if (existingMealPlan == null)
throw new KeyNotFoundException($"Meal plan with ID {mealPlanId} not found");
// Update meal plan properties
if (data.ContainsKey("name") && !string.IsNullOrEmpty(data["name"].GetString()))
existingMealPlan.Name = data["name"].GetString()!;
if (data.ContainsKey("description"))
existingMealPlan.Description = data["description"].GetString();
if (data.ContainsKey("isTemplate"))
existingMealPlan.IsTemplate = data["isTemplate"].GetBoolean();
if (data.ContainsKey("mealCategoryId"))
existingMealPlan.FKMealCategoryId = Guid.Parse(data["mealCategoryId"].GetString()!);
await _repository.UpdateMealPlanAsync(existingMealPlan);
// Handle meal items update if provided
if (data.ContainsKey("mealItems") && data["mealItems"].ValueKind == System.Text.Json.JsonValueKind.Array)
{
// Get existing meal items for this plan
var existingMeals = await _repository.GetMealsByMealPlanIdAsync(mealPlanId);
// Delete all existing meals (clean slate approach)
foreach (var existingMeal in existingMeals)
{
await _repository.DeleteMealAsync(existingMeal.MealId);
}
// Create new meals from payload
var mealItemsArray = data["mealItems"].EnumerateArray();
foreach (var mealItemData in mealItemsArray)
{
// Only create meal if name is provided
if (mealItemData.TryGetProperty("name", out var nameElement) &&
!string.IsNullOrEmpty(nameElement.GetString()))
{
string? mealItemsJson = null;
if (mealItemData.TryGetProperty("mealItems", out var mealItemsElement))
{
if (mealItemsElement.ValueKind == System.Text.Json.JsonValueKind.Array)
{
mealItemsJson = System.Text.Json.JsonSerializer.Serialize(mealItemsElement);
}
else if (mealItemsElement.ValueKind == System.Text.Json.JsonValueKind.String)
{
mealItemsJson = mealItemsElement.GetString();
}
}
var meal = new Models.Meal
{
Name = nameElement.GetString()!,
Description = mealItemData.TryGetProperty("description", out var desc) ? desc.GetString() : null,
Calories = TryParseDecimal(mealItemData, "calories"),
Protein = TryParseDecimal(mealItemData, "protein"),
Carbohydrates = TryParseDecimal(mealItemData, "carbohydrates"),
Fats = TryParseDecimal(mealItemData, "fats"),
ServingSize = mealItemData.TryGetProperty("servingSize", out var serving) ? serving.GetString() : null,
MealItems = mealItemsJson,
PreparationNotes = mealItemData.TryGetProperty("preparationNotes", out var notes) ? notes.GetString() : null,
FKMealPlanId = mealPlanId,
CreateDate = DateTime.UtcNow
};
await _repository.CreateMealAsync(meal);
}
}
}
await _uow.CommitAsync();
try
{
await _activityLogService.LogActivity(
new { description = $"Meal plan updated with ID {mealPlanId}" },
httpContext
);
}
catch (Exception logEx)
{
Console.WriteLine($"Activity log failed (non-critical): {logEx.Message}");
}
var updatedMealPlan = await _repository.GetMealPlanByIdAsync(mealPlanId);
if (updatedMealPlan == null)
throw new KeyNotFoundException($"Meal plan with ID {mealPlanId} not found after update");
// Parse MealItems JSON strings to objects in nested meal items
if (updatedMealPlan.MealItems != null)
{
foreach (var item in updatedMealPlan.MealItems)
{
var itemAsString = item.MealItems as string;
if (!string.IsNullOrEmpty(itemAsString))
{
try
{
item.MealItems = System.Text.Json.JsonSerializer.Deserialize<object>(itemAsString);
}
catch
{
// Keep as string if parsing fails
}
}
}
}
return updatedMealPlan;
}
catch
{
await _uow.RollbackAsync();
throw;
}
}
// Delete Meal Plan
public async Task<object> DeleteMealPlan(object payload, HttpContext httpContext)
{
await _uow.BeginAsync();
try
{
var data = DeserializePayload.Deserialize(payload);
Guid mealPlanId = Guid.Parse(data["mealPlanId"].GetString()!);
var existingMealPlan = await _repository.GetMealPlanEntityByIdAsync(mealPlanId);
if (existingMealPlan == null)
throw new KeyNotFoundException($"Meal plan with ID {mealPlanId} not found");
// Delete all associated meal items first (foreign key constraint)
var existingMeals = await _repository.GetMealsByMealPlanIdAsync(mealPlanId);
foreach (var meal in existingMeals)
{
await _repository.DeleteMealAsync(meal.MealId);
}
// Delete the meal plan
await _repository.DeleteMealPlanAsync(mealPlanId);
await _uow.CommitAsync();
try
{
await _activityLogService.LogActivity(
new { description = $"Meal plan deleted with ID {mealPlanId}" },
httpContext
);
}
catch (Exception logEx)
{
Console.WriteLine($"Activity log failed (non-critical): {logEx.Message}");
}
return new
{
Success = true,
Message = $"Meal plan with ID {mealPlanId} has been deleted successfully"
};
}
catch
{
await _uow.RollbackAsync();
throw;
}
}
// Helper method
private decimal? TryParseDecimal(System.Text.Json.JsonElement element, string propertyName)
{
if (element.TryGetProperty(propertyName, out var prop))
{
if (prop.ValueKind == System.Text.Json.JsonValueKind.Number)
{
return prop.GetDecimal();
}
else if (prop.ValueKind == System.Text.Json.JsonValueKind.String)
{
var strValue = prop.GetString();
if (!string.IsNullOrEmpty(strValue) && decimal.TryParse(strValue, out var result))
{
return result;
}
}
}
return null;
}
// Helper for JsonElement
private decimal? TryParseDecimal(Dictionary<string, System.Text.Json.JsonElement> data, string key)
{
if (!data.ContainsKey(key))
return null;
var element = data[key];
if (element.ValueKind == System.Text.Json.JsonValueKind.Number)
return element.GetDecimal();
if (element.ValueKind == System.Text.Json.JsonValueKind.String)
{
var strValue = element.GetString();
if (!string.IsNullOrEmpty(strValue) && decimal.TryParse(strValue, out var result))
return result;
}
return null;
}
// Customer Meal Assignment operations
public async Task<object> AssignMealToCustomer(object payload, HttpContext httpContext)
{
await _uow.BeginAsync();
try
{
var data = DeserializePayload.Deserialize(payload);
Guid mealPlanId = Guid.Parse(data["mealPlanId"].GetString()!);
Guid customerId = Guid.Parse(data["customerId"].GetString()!);
var customerMeal = new CustomerMeal
{
FKCustomerId = customerId,
FKMealPlanId = mealPlanId,
AssignedDate = DateTime.UtcNow,
Notes = data.ContainsKey("notes") ? data["notes"].GetString() : null,
IsActive = true
};
await _repository.AssignMealToCustomerAsync(customerMeal);
await _uow.CommitAsync();
try
{
await _activityLogService.LogActivity(
new { description = $"Meal plan {mealPlanId} assigned to customer {customerId}" },
httpContext
);
}
catch (Exception logEx)
{
Console.WriteLine($"Activity log failed (non-critical): {logEx.Message}");
}
try
{
var plan = await _repository.GetMealPlanEntityByIdAsync(mealPlanId);
var planName = plan?.Name ?? "a meal plan";
await _pushNotificationService.NotifyCustomerAsync(
customerId,
"New meal plan assigned",
$"Your coach assigned you \"{planName}\". Tap to view.",
"MealPlanAssigned",
"MealPlanSuggestionDetail",
new { mealPlanId }
);
}
catch (Exception notifyEx)
{
Console.WriteLine($"Push notification failed (non-critical): {notifyEx.Message}");
}
return new
{
customerMeal.CustomerMealId,
customerMeal.FKCustomerId,
customerMeal.FKMealPlanId,
customerMeal.AssignedDate,
customerMeal.Notes,
customerMeal.IsActive
};
}
catch
{
await _uow.RollbackAsync();
throw;
}
}
public async Task<object> UnassignMealFromCustomer(object payload, HttpContext httpContext)
{
await _uow.BeginAsync();
try
{
var data = DeserializePayload.Deserialize(payload);
Guid customerMealId = Guid.Parse(data["customerMealId"].GetString()!);
var result = await _repository.UnassignMealFromCustomerAsync(customerMealId);
if (!result)
{
throw new KeyNotFoundException($"Customer meal assignment with ID {customerMealId} not found");
}
await _uow.CommitAsync();
try
{
await _activityLogService.LogActivity(
new { description = $"Meal assignment {customerMealId} removed" },
httpContext
);
}
catch (Exception logEx)
{
Console.WriteLine($"Activity log failed (non-critical): {logEx.Message}");
}
return new { success = true, message = "Meal unassigned from customer successfully" };
}
catch
{
await _uow.RollbackAsync();
throw;
}
}
}
}