using System.Collections.Concurrent; namespace AxCopilot.Services; /// 알림 타입. public enum NotificationType { Info, Success, Warning, Error } /// 알림 항목. public record NotificationEntry( string Title, string Message, NotificationType Type, DateTime Timestamp); /// /// Phase L3-7: 알림 센터 — 앱 전체에서 알림을 발행하고 이력을 관리합니다. /// 트레이 BalloonTip + 인앱 알림 큐를 결합합니다. /// public static class NotificationCenterService { private const int MaxHistory = 50; private static readonly ConcurrentQueue _history = new(); /// 새 알림 발행 이벤트. UI 계층에서 구독하여 인앱 알림 표시에 활용합니다. public static event EventHandler? NotificationRaised; /// 최근 알림 이력 (최대 50건, 최신 순). public static IReadOnlyList History { get { var list = _history.ToArray(); Array.Reverse(list); return list; } } /// 알림을 발행합니다. 트레이 BalloonTip + 이벤트를 동시에 발화합니다. public static void Show(string title, string message, NotificationType type = NotificationType.Info) { var entry = Record(title, message, type); NotificationService.NotifyBalloonOnly(title, message); TryRaise(entry); } /// 성공 알림. public static void ShowSuccess(string title, string message) => Show(title, message, NotificationType.Success); /// 경고 알림. public static void ShowWarning(string title, string message) => Show(title, message, NotificationType.Warning); /// 오류 알림. public static void ShowError(string title, string message) => Show(title, message, NotificationType.Error); /// 에이전트 작업 완료 알림 (BackgroundAgentService 전용 편의 메서드). public static void NotifyAgentCompleted(string agentType, string? result) { var preview = result?.Length > 80 ? result[..77] + "…" : result ?? "완료"; Show($"{agentType} 에이전트 완료", preview, NotificationType.Success); } /// 이력을 초기화합니다. public static void ClearHistory() { while (_history.TryDequeue(out _)) { } } internal static NotificationEntry Record(string title, string message, NotificationType type) { var entry = new NotificationEntry(title, message, type, DateTime.Now); _history.Enqueue(entry); while (_history.Count > MaxHistory && _history.TryDequeue(out _)) { } TryRaise(entry); return entry; } private static void TryRaise(NotificationEntry entry) { try { NotificationRaised?.Invoke(null, entry); } catch (Exception) { } } }