using System; using System.Timers; using System.Windows.Threading; using AxCopilot.Models; using AxCopilot.Views; using Microsoft.Win32; namespace AxCopilot.Services; public sealed class WorktimeReminderService : IDisposable { private readonly SettingsService _settings; private readonly Dispatcher _dispatcher; private readonly object _lock = new object(); private DateTime _sessionStart; private TimeSpan _todayTotal; private DateTime _lastDate; private DateTime _lastShownAt; private Timer? _periodicTimer; private volatile bool _disposed; public WorktimeReminderService(SettingsService settings, Dispatcher dispatcher) { _settings = settings; _dispatcher = dispatcher; _sessionStart = DateTime.Now; _lastDate = DateTime.Today; _lastShownAt = DateTime.MinValue; try { int todayActiveSeconds = UsageStatisticsService.GetTodayActiveSeconds(); if (todayActiveSeconds > 0) { _todayTotal = TimeSpan.FromSeconds(todayActiveSeconds); LogService.Info($"이전 세션 누적 시간 복원: {_todayTotal}"); } } catch (Exception ex) { LogService.Warn("이전 세션 시간 복원 실패: " + ex.Message); } SystemEvents.SessionSwitch += OnSessionSwitch; LogService.Info("WorktimeReminderService 초기화 완료."); ReminderSettings reminder = _settings.Settings.Reminder; if (!reminder.Enabled) { return; } _dispatcher.BeginInvoke((Delegate)(Action)delegate { if (!_disposed) { ShowPopup(); } }, (DispatcherPriority)2, Array.Empty()); StartPeriodicTimer(); } private void OnSessionSwitch(object sender, SessionSwitchEventArgs e) { if (_disposed) { return; } LogService.Info($"SessionSwitch 이벤트: {e.Reason}"); lock (_lock) { switch (e.Reason) { case SessionSwitchReason.SessionLock: AccumulateSession(); StopPeriodicTimer(); LogService.Info($"세션 잠금. 누적 시간: {_todayTotal}"); break; case SessionSwitchReason.SessionLogon: case SessionSwitchReason.SessionUnlock: { ResetForNewDayIfNeeded(); _sessionStart = DateTime.Now; ReminderSettings reminder = _settings.Settings.Reminder; if (!reminder.Enabled) { LogService.Info("알림 비활성화 상태. 팝업 생략."); break; } int num = Math.Max(1, reminder.IntervalMinutes); double totalMinutes = (DateTime.Now - _lastShownAt).TotalMinutes; LogService.Info($"알림 간격 확인: 경과={totalMinutes:F1}분, 설정={num}분"); if (totalMinutes >= (double)num) { ShowPopup(); } StartPeriodicTimer(); break; } case SessionSwitchReason.SessionLogoff: break; } } } private void AccumulateSession() { ResetForNewDayIfNeeded(); _todayTotal += DateTime.Now - _sessionStart; _sessionStart = DateTime.Now; } private void ResetForNewDayIfNeeded() { if (DateTime.Today == _lastDate) { return; } DateTime today = DateTime.Today; _sessionStart = today; _lastDate = DateTime.Today; _lastShownAt = DateTime.MinValue; try { int todayActiveSeconds = UsageStatisticsService.GetTodayActiveSeconds(); _todayTotal = ((todayActiveSeconds > 0) ? TimeSpan.FromSeconds(todayActiveSeconds) : TimeSpan.Zero); } catch { _todayTotal = TimeSpan.Zero; } } public TimeSpan GetTodayUsage() { lock (_lock) { ResetForNewDayIfNeeded(); return _todayTotal + (DateTime.Now - _sessionStart); } } private void StartPeriodicTimer() { StopPeriodicTimer(); int num = Math.Max(1, _settings.Settings.Reminder.IntervalMinutes); _periodicTimer = new Timer(num * 60000); _periodicTimer.Elapsed += OnPeriodicTimerElapsed; _periodicTimer.AutoReset = true; _periodicTimer.Start(); LogService.Info($"격려 알림 주기 타이머 시작: {num}분 간격"); } private void StopPeriodicTimer() { if (_periodicTimer != null) { _periodicTimer.Elapsed -= OnPeriodicTimerElapsed; _periodicTimer.Stop(); _periodicTimer.Dispose(); _periodicTimer = null; } } private void OnPeriodicTimerElapsed(object? sender, ElapsedEventArgs e) { if (_disposed) { return; } ReminderSettings reminder = _settings.Settings.Reminder; if (!reminder.Enabled) { StopPeriodicTimer(); return; } lock (_lock) { ResetForNewDayIfNeeded(); ShowPopup(); } } public void RestartTimer() { ReminderSettings reminder = _settings.Settings.Reminder; if (reminder.Enabled) { StartPeriodicTimer(); } else { StopPeriodicTimer(); } } private void ShowPopup() { _lastShownAt = DateTime.Now; TimeSpan usage = GetTodayUsage(); var (text, author) = QuoteService.GetRandom(_settings?.Settings.Reminder.EnabledCategories); _dispatcher.BeginInvoke((Delegate)(Action)delegate { if (_disposed) { return; } try { if (_settings != null) { ReminderPopupWindow reminderPopupWindow = new ReminderPopupWindow(text, author, usage, _settings); reminderPopupWindow.Show(); } } catch (Exception ex) { LogService.Warn("알림 팝업 표시 실패: " + ex.Message); } }, (DispatcherPriority)9, Array.Empty()); } public void Dispose() { if (!_disposed) { _disposed = true; StopPeriodicTimer(); SystemEvents.SessionSwitch -= OnSessionSwitch; } } }