namespace VideoDownloader.App.Services;
///
/// UC-D21 다운로드 예약 — 지정 시간대(예: 야간 02:00~08:00)에만 다운로드를 허용하는 게이트.
/// 시간 창은 "HH:mm-HH:mm" 형식. 자정을 가로지르는 창(22:00-06:00)도 지원한다.
///
public sealed class DownloadScheduler
{
/// 예약 창("HH:mm-HH:mm"). null/빈이면 항상 허용.
public string? Window { get; }
/// 예약 창이 설정돼 있으면 true.
public bool Enabled => !string.IsNullOrWhiteSpace(Window);
public DownloadScheduler(string? window = null)
{
Window = string.IsNullOrWhiteSpace(window) ? null : window.Trim();
}
/// 지정 시각에 다운로드가 허용되는지.
public bool CanRunNow(DateTime now)
{
if (!Enabled || TryParse(Window!, out var start, out var end) == false) return true;
var t = now.TimeOfDay;
return start <= end
? t >= start && t < end
: t >= start || t < end; // 자정 가로지름(예: 22:00-06:00)
}
/// 다음 예약 창 시작까지 남은 시간. 항상 허용이면 Zero.
public TimeSpan TimeUntilNextWindow(DateTime now)
{
if (!Enabled || !TryParse(Window!, out var start, out _)) return TimeSpan.Zero;
var target = now.Date + start;
if (target <= now) target = target.AddDays(1);
return target - now;
}
/// "HH:mm-HH:mm" 파싱. 형식이 틀리면 false(게이트는 열린 채 둔다).
public static bool TryParse(string window, out TimeSpan start, out TimeSpan end)
{
start = end = TimeSpan.Zero;
var parts = (window ?? "").Split('-');
if (parts.Length != 2) return false;
return TryParseTime(parts[0], out start) && TryParseTime(parts[1], out end);
}
private static bool TryParseTime(string s, out TimeSpan ts)
{
ts = TimeSpan.Zero;
var p = s.Trim().Split(':');
if (p.Length != 2 || !int.TryParse(p[0], out var h) || !int.TryParse(p[1], out var m)) return false;
if (h is < 0 or > 23 || m is < 0 or > 59) return false;
ts = new TimeSpan(h, m, 0);
return true;
}
}