using UtopiaCanteenSystem.Models;
namespace UtopiaCanteenSystem.Services;
///
/// Resolves current meal session from production (MySQL meal_schedule). Caches schedules per site for 60 seconds.
/// No SQLite; scan validation uses production data only.
///
public class DbMealSessionResolver : IMealSessionResolver
{
private readonly IMealScheduleService _mealScheduleService;
private readonly TimeSpan _cacheTtl = TimeSpan.FromSeconds(60);
private readonly Dictionary Schedules, DateTime ExpiryUtc)> _cache = new(StringComparer.OrdinalIgnoreCase);
private readonly object _cacheLock = new();
public DbMealSessionResolver(IMealScheduleService mealScheduleService)
{
_mealScheduleService = mealScheduleService;
}
private static MealSession MapMealNameToSession(string? mealName)
{
if (string.IsNullOrWhiteSpace(mealName))
return MealSession.None;
var name = mealName.Trim();
return name.ToLowerInvariant() switch
{
"breakfast" => MealSession.Breakfast,
"sehri" => MealSession.Breakfast,
"lunch" => MealSession.Lunch,
"iftari" => MealSession.Lunch,
"tea" => MealSession.Tea,
"dinner" => MealSession.Dinner,
_ => MealSession.None
};
}
///
//public MealSession GetCurrentSession(DateTime nowLocal, string siteId)
//{
// var normalizedSite = (siteId ?? string.Empty).Trim();
// if (string.IsNullOrEmpty(normalizedSite))
// normalizedSite = "01";
// if (normalizedSite.Length == 1 && char.IsDigit(normalizedSite[0]))
// normalizedSite = normalizedSite.PadLeft(2, '0');
// var schedules = GetSchedulesForSiteCached(normalizedSite);
// var t = nowLocal.TimeOfDay;
// //foreach (var s in schedules)
// //{
// // if (!TryParseTime(s.StartTime, out var start) || !TryParseTime(s.EndTime, out var end))
// // continue;
// // if (t >= start && t < end)
// // return (MealSession)s.MealSession;
// //}
// foreach (var s in schedules)
// {
// if (!TryParseTime(s.StartTime, out var start) || !TryParseTime(s.EndTime, out var end))
// continue;
// if (t >= start && t < end)
// return MapMealNameToSession(s.MealName);
// }
// return MealSession.None;
//}
public ResolvedMealSession? GetCurrentSession(DateTime nowLocal, string siteId)
{
var normalizedSite = (siteId ?? string.Empty).Trim();
if (string.IsNullOrEmpty(normalizedSite))
normalizedSite = "01";
if (normalizedSite.Length == 1 && char.IsDigit(normalizedSite[0]))
normalizedSite = normalizedSite.PadLeft(2, '0');
var schedules = GetSchedulesForSiteCached(normalizedSite);
var t = nowLocal.TimeOfDay;
foreach (var s in schedules)
{
if (!TryParseTime(s.StartTime, out var start) || !TryParseTime(s.EndTime, out var end))
continue;
if (t >= start && t < end)
{
return new ResolvedMealSession
{
Session = MapMealNameToSession(s.MealName),
MealName = s.MealName ?? string.Empty,
SiteId = normalizedSite,
StartTime = s.StartTime ?? string.Empty,
EndTime = s.EndTime ?? string.Empty
};
}
}
return null;
}
private List GetSchedulesForSiteCached(string siteId)
{
lock (_cacheLock)
{
if (_cache.TryGetValue(siteId, out var entry) && DateTime.UtcNow < entry.ExpiryUtc)
return entry.Schedules;
}
var list = _mealScheduleService.GetActiveSchedulesForSite(siteId).ToList();
lock (_cacheLock)
{
_cache[siteId] = (list, DateTime.UtcNow.Add(_cacheTtl));
}
return list;
}
private static bool TryParseTime(string value, out TimeSpan time)
{
time = TimeSpan.Zero;
if (string.IsNullOrWhiteSpace(value))
return false;
return TimeSpan.TryParse(value.Trim(), null, out time)
|| TimeSpan.TryParse(value.Trim().Replace(".", ":"), null, out time);
}
}