using WorldApi.Models; namespace WorldApi.Services; public class WorldCycleService { private readonly double _dayLengthSeconds; private readonly DateTime _epochUtc; public WorldCycleService(IConfiguration configuration) { _dayLengthSeconds = Math.Max(1.0, configuration.GetValue("WorldCycle:DayLengthSeconds") ?? 1200.0); var configuredEpoch = configuration["WorldCycle:EpochUtc"]; _epochUtc = DateTime.TryParse( configuredEpoch, null, System.Globalization.DateTimeStyles.AdjustToUniversal | System.Globalization.DateTimeStyles.AssumeUniversal, out var parsedEpoch) ? parsedEpoch : new DateTime(2026, 1, 1, 0, 0, 0, DateTimeKind.Utc); } public WorldCycleResponse GetCurrentCycle() { var now = DateTime.UtcNow; var elapsedSeconds = Math.Max(0.0, (now - _epochUtc).TotalSeconds); var timeOfDaySeconds = elapsedSeconds % _dayLengthSeconds; var normalized = timeOfDaySeconds / _dayLengthSeconds; var solarCurve = Math.Sin(normalized * Math.PI); var lightLevel = Math.Clamp(solarCurve, 0.0, 1.0); return new WorldCycleResponse { CurrentUtc = now, DayLengthSeconds = _dayLengthSeconds, TimeOfDaySeconds = timeOfDaySeconds, TimeOfDayNormalized = normalized, Phase = ResolvePhase(normalized), IsDay = lightLevel > 0.0, LightLevel = lightLevel }; } private static string ResolvePhase(double normalized) { return normalized switch { < 0.20 => "night", < 0.30 => "dawn", < 0.70 => "day", < 0.80 => "dusk", _ => "night" }; } }