using Nexus.Api.Models; namespace Nexus.Api.Services; public sealed class DashboardService( IOpenClawGatewayClient gateway, ITaskService taskService, ILogger logger) : IDashboardService { public async Task GetStatusAsync() { try { return await gateway.GetStatusAsync(); } catch (Exception ex) { logger.LogWarning(ex, "Dashboard status check failed"); return new DashboardStatus(false, "Offline", 0, 0); } } public async Task> GetAgentsAsync() { try { return await gateway.GetAgentsAsync(); } catch (Exception ex) { logger.LogWarning(ex, "Dashboard agents fetch failed"); return []; } } public async Task> GetOperationsAsync(int limit, string? agentFilter) { try { var entries = await gateway.GetAllAgentOperationsAsync(Math.Clamp(limit, 1, 100)); if (!string.IsNullOrWhiteSpace(agentFilter)) { entries = entries .Where(e => string.Equals(e.AgentId, agentFilter, StringComparison.OrdinalIgnoreCase) || string.Equals(e.Agent, agentFilter, StringComparison.OrdinalIgnoreCase)) .ToList(); } return entries; } catch (Exception ex) { logger.LogWarning(ex, "Dashboard operations fetch failed"); return []; } } public async Task SendChatAsync(string agentId, string message) { try { return await gateway.SendChatMessageAsync(agentId, message); } catch (Exception ex) { logger.LogWarning(ex, "Dashboard chat send failed"); return new ChatResponse(false, null, "Gateway nicht erreichbar"); } } public async Task> GetMessagesAsync(string? sessionKey, int limit, int offset) { try { var key = string.IsNullOrWhiteSpace(sessionKey) ? "agent:iris:main" : sessionKey.Trim(); var messages = await gateway.GetSessionHistoryAsync(key, Math.Clamp(limit, 1, 200), Math.Max(0, offset)); return messages .Where(m => string.Equals(m.Role, "user", StringComparison.OrdinalIgnoreCase) || string.Equals(m.Role, "assistant", StringComparison.OrdinalIgnoreCase)) .ToList(); } catch (Exception ex) { logger.LogWarning(ex, "Dashboard messages fetch failed"); return []; } } public async Task> GetQueueAsync(CancellationToken ct) { try { var cronTask = gateway.GetQueueAsync(); var tasksTask = taskService.GetOpenAsync(ct); await Task.WhenAll(cronTask, tasksTask); var merged = new List(cronTask.Result); foreach (var t in tasksTask.Result) { merged.Add(new QueueItem("task-" + t.Id, t.Title, t.State, NormalizePriority(t.Priority), "task", "--")); } return merged .OrderBy(q => PriorityOrder.GetValueOrDefault(q.Priority, 99)) .ToList(); } catch (Exception ex) { logger.LogWarning(ex, "Dashboard queue fetch failed"); return []; } } public async Task DeleteQueueItemAsync(string id, string? source, CancellationToken ct) { if (string.Equals(source, "cron", StringComparison.OrdinalIgnoreCase)) { var ok = await gateway.DeleteCronJobAsync(id); return new QueueDeleteResult(ok ? QueueDeleteOutcome.Deleted : QueueDeleteOutcome.GatewayError); } if (string.Equals(source, "task", StringComparison.OrdinalIgnoreCase) || id.StartsWith("task-")) { if (!id.StartsWith("task-")) return new QueueDeleteResult(QueueDeleteOutcome.InvalidTaskId); if (!Guid.TryParse(id["task-".Length..], out var guid)) return new QueueDeleteResult(QueueDeleteOutcome.InvalidTaskId); var result = await taskService.CompleteViaQueueAsync(guid, ct); return result.Outcome switch { TaskOperationOutcome.NotFound => new QueueDeleteResult(QueueDeleteOutcome.TaskNotFound), _ => new QueueDeleteResult(QueueDeleteOutcome.Deleted) }; } var deleted = await gateway.DeleteCronJobAsync(id); return new QueueDeleteResult(deleted ? QueueDeleteOutcome.Deleted : QueueDeleteOutcome.NotFound); } public async Task CycleQueuePriorityAsync(string id, CancellationToken ct) { if (!id.StartsWith("task-")) return new QueuePriorityResult(QueuePriorityOutcome.Ignored); if (!Guid.TryParse(id["task-".Length..], out var guid)) return new QueuePriorityResult(QueuePriorityOutcome.InvalidTaskId); var result = await taskService.CyclePriorityAsync(guid, ct); return result.Outcome switch { TaskOperationOutcome.NotFound => new QueuePriorityResult(QueuePriorityOutcome.TaskNotFound), _ => new QueuePriorityResult(QueuePriorityOutcome.Updated, result.Task?.Priority) }; } public async Task GetAgentModelAsync(string agentId) { try { return await gateway.GetAgentModelAsync(agentId); } catch (Exception ex) { logger.LogWarning(ex, "GetAgentModel failed for {AgentId}", agentId); return null; } } public async Task SetAgentModelAsync(string agentId, string model) { try { return await gateway.SetAgentModelAsync(agentId, model); } catch (Exception ex) { logger.LogWarning(ex, "SetAgentModel failed for {AgentId}", agentId); return false; } } public async Task> GetAgentActivityAsync(string agentId, int limit) { try { return await gateway.GetAgentActivityAsync(agentId, Math.Clamp(limit, 1, 20)); } catch (Exception ex) { logger.LogWarning(ex, "GetAgentActivity failed for {AgentId}", agentId); return []; } } public List GetAvailableModels() => gateway.GetAvailableModels(); private static string NormalizePriority(string priority) => priority.ToLowerInvariant() switch { "high" or "critical" or "urgent" => "high", "low" or "minor" => "low", _ => "medium" }; private static readonly Dictionary PriorityOrder = new(StringComparer.OrdinalIgnoreCase) { ["high"] = 0, ["medium"] = 1, ["low"] = 2 }; }