using CareFix.Api.Ai; using CareFix.Api.Options; using Dapper; using Microsoft.Extensions.Options; namespace CareFix.Api.Infrastructure; /// What a monitoring tool or an admin needs to know at a glance. public sealed class HealthService(ControlDb db, AiUsageService usage, IOptions options) { /// Anonymous liveness check: is the service up and can it reach its own database? public async Task<(bool Ok, object Body)> LivenessAsync(CancellationToken ct) { try { await using var c = await db.OpenAsync(ct); await c.ExecuteScalarAsync("SELECT 1"); return (true, new { status = "ok", at = DateTime.UtcNow }); } catch (Exception ex) { return (false, new { status = "down", reason = ex.GetType().Name, at = DateTime.UtcNow }); } } /// Full picture for admins: work in flight, agents, integration backlog, AI spend. public async Task DetailAsync(CancellationToken ct) { await using var c = await db.OpenAsync(ct); using var m = await c.QueryMultipleAsync(""" SELECT (SELECT COUNT(*) FROM CF_TICKET WHERE State <> 'Closed') AS OpenTickets, (SELECT COUNT(*) FROM CF_TICKET WHERE AiBusy = 1) AS AiBusy, (SELECT COUNT(*) FROM CF_FIX WHERE State = 'Proposed') AS AwaitingApproval, (SELECT COUNT(*) FROM CF_FIX WHERE State = 'Approved') AS ReadyToRun; SELECT (SELECT COUNT(*) FROM CF_HOSPITAL WHERE Status = 'Active') AS ActiveHospitals, (SELECT COUNT(*) FROM CF_HOSPITAL h WHERE h.Status = 'Active' AND h.SchemaCapturedAt IS NULL) AS SchemaMissing, (SELECT COUNT(*) FROM CF_AGENT WHERE LastSeenAt > DATEADD(MINUTE, -2, SYSUTCDATETIME())) AS AgentsOnline, (SELECT COUNT(*) FROM CF_AGENT WHERE LastSeenAt IS NULL OR LastSeenAt <= DATEADD(MINUTE, -2, SYSUTCDATETIME())) AS AgentsOffline, (SELECT COUNT(*) FROM CF_AGENT_JOB WHERE State = 'Pending') AS AgentJobsWaiting; SELECT (SELECT COUNT(*) FROM CF_WEBHOOK_OUTBOX WHERE State = 'Pending') AS WebhooksPending, (SELECT COUNT(*) FROM CF_WEBHOOK_OUTBOX WHERE State = 'Failed') AS WebhooksFailed, (SELECT COUNT(*) FROM CF_MINE_BATCH WHERE State IN ('Queued','Processing')) AS LearningRunning; SELECT TOP 1 At FROM CF_AUDIT WHERE Action = 'MaintenanceRun' ORDER BY EventId DESC; SELECT TOP 5 h.Name, a.LastSeenAt FROM CF_AGENT a JOIN CF_HOSPITAL h ON h.HospitalId = a.HospitalId WHERE h.Status = 'Active' AND (a.LastSeenAt IS NULL OR a.LastSeenAt <= DATEADD(MINUTE, -2, SYSUTCDATETIME())) ORDER BY a.LastSeenAt; """); var work = await m.ReadSingleAsync(); var hospitals = await m.ReadSingleAsync(); var queues = await m.ReadSingleAsync(); var lastMaintenance = await m.ReadSingleOrDefaultAsync(); var offlineAgents = await m.ReadAsync(); var cfg = options.Value; var spent = await usage.MonthToDateAsync(ct); return new { status = "ok", at = DateTime.UtcNow, work, hospitals, queues, offlineAgents, lastMaintenance, ai = new { model = cfg.Claude.Model, apiKeySet = !string.IsNullOrWhiteSpace(cfg.Claude.ApiKey), monthToDateUsd = spent, monthlyBudgetUsd = cfg.Claude.MonthlyBudgetUsd, budgetReached = cfg.Claude.MonthlyBudgetUsd > 0 && spent >= cfg.Claude.MonthlyBudgetUsd, }, integration = new { inboundApi = !string.IsNullOrWhiteSpace(cfg.Integration.InboundKey), outboundWebhook = !string.IsNullOrWhiteSpace(cfg.Integration.WebhookUrl), }, }; } }