package service import ( "context" "fmt" "log/slog" "github.com/TopherMayor/unified-media-manager/internal/db" ) type DashboardStats struct { TotalMedia int64 `json:"total_media"` Monitored int64 `json:"monitored"` Unavailable int64 `json:"unavailable"` Available int64 `json:"available"` QualityUpgrades int64 `json:"quality_upgrades"` QueuePending int64 `json:"queue_pending"` QueueDownloading int64 `json:"queue_downloading"` QueueFailed int64 `json:"queue_failed"` BlocklistCount int64 `json:"blocklist_count"` BlocklistExpired int64 `json:"blocklist_expired"` IndexersEnabled int64 `json:"indexers_enabled"` MediaByType map[string]int64 `json:"media_by_type"` StorageByType map[string]int64 `json:"storage_by_type"` RecentDownloads int64 `json:"recent_downloads"` } type DashboardService struct { db *db.DB } func NewDashboardService(database *db.DB) *DashboardService { return &DashboardService{db: database} } func (s *DashboardService) Stats(ctx context.Context) (*DashboardStats, error) { stats := &DashboardStats{ MediaByType: make(map[string]int64), StorageByType: make(map[string]int64), } combinedQuery := ` SELECT (SELECT COUNT(*) FROM media WHERE deleted_at IS NULL), (SELECT COUNT(*) FROM media WHERE monitored = true AND deleted_at IS NULL), (SELECT COUNT(*) FROM media WHERE status = 'unavailable' AND deleted_at IS NULL), (SELECT COUNT(*) FROM media WHERE status = 'available' AND deleted_at IS NULL), (SELECT COUNT(*) FROM media WHERE desired_quality IS NOT NULL AND current_quality IS NULL AND deleted_at IS NULL), (SELECT COUNT(*) FROM download_queue WHERE status = 'pending'), (SELECT COUNT(*) FROM download_queue WHERE status = 'downloading'), (SELECT COUNT(*) FROM download_queue WHERE status = 'failed'), (SELECT COUNT(*) FROM blocklist), (SELECT COUNT(*) FROM blocklist WHERE auto_expires_at IS NOT NULL AND auto_expires_at < NOW()), (SELECT COUNT(*) FROM indexers WHERE enabled = true), (SELECT COUNT(*) FROM download_history WHERE created_at > NOW() - INTERVAL '24 hours')` err := s.db.Pool.QueryRow(ctx, combinedQuery).Scan( &stats.TotalMedia, &stats.Monitored, &stats.Unavailable, &stats.Available, &stats.QualityUpgrades, &stats.QueuePending, &stats.QueueDownloading, &stats.QueueFailed, &stats.BlocklistCount, &stats.BlocklistExpired, &stats.IndexersEnabled, &stats.RecentDownloads) if err != nil { slog.Error("dashboard combined query failed", "error", err) return nil, fmt.Errorf("dashboard stats: %w", err) } rows, err := s.db.Pool.Query(ctx, "SELECT media_type, COUNT(*) FROM media WHERE deleted_at IS NULL GROUP BY media_type") if err == nil { defer rows.Close() for rows.Next() { var mediaType string var count int64 if err := rows.Scan(&mediaType, &count); err == nil { stats.MediaByType[mediaType] = count } } } sRows, err := s.db.Pool.Query(ctx, `SELECT m.media_type, COALESCE(SUM(mf.file_size), 0) FROM media m JOIN media_files mf ON m.id = mf.media_id AND mf.deleted_at IS NULL WHERE m.deleted_at IS NULL GROUP BY m.media_type`) if err == nil { defer sRows.Close() for sRows.Next() { var mediaType string var totalSize int64 if err := sRows.Scan(&mediaType, &totalSize); err == nil { stats.StorageByType[mediaType] = totalSize } } } return stats, nil }