import Foundation import Observation import OSLog private let gameCenterLogger = Logger(subsystem: "com.treyt.mlbTVOS", category: "GameCenter") private func logGameCenter(_ message: String) { gameCenterLogger.debug("\(message, privacy: .public)") print("[GameCenter] \(message)") } @Observable @MainActor final class GameCenterViewModel { var feed: LiveGameFeed? var highlights: [Highlight] = [] var winProbabilityHome: Double? var winProbabilityAway: Double? var isLoading = false var errorMessage: String? var lastUpdated: Date? private let statsAPI = MLBStatsAPI() @ObservationIgnored private var lastHighlightsFetch: Date? func watch(game: Game) async { guard let gamePk = game.gamePk else { logGameCenter("watch: no gamePk for game id=\(game.id)") errorMessage = "No live game feed is available for this matchup." return } logGameCenter("watch: starting for gamePk=\(gamePk)") while !Task.isCancelled { await refresh(gamePk: gamePk) await refreshHighlightsIfNeeded(gamePk: gamePk, gameDate: game.gameDate) await refreshWinProbability(gamePk: gamePk) let liveState = feed?.gameData.status?.abstractGameState == "Live" if !liveState { break } try? await Task.sleep(for: .seconds(12)) } } func refresh(gamePk: String) async { isLoading = true errorMessage = nil do { logGameCenter("refresh: fetching feed for gamePk=\(gamePk)") feed = try await statsAPI.fetchGameFeed(gamePk: gamePk) logGameCenter("refresh: success playEvents=\(feed?.currentPlay?.playEvents?.count ?? 0) allPlays=\(feed?.liveData.plays.allPlays.count ?? 0)") lastUpdated = Date() } catch { logGameCenter("refresh: FAILED gamePk=\(gamePk) error=\(error)") errorMessage = "Failed to load game center." } isLoading = false } private func refreshHighlightsIfNeeded(gamePk: String, gameDate: String) async { // Only fetch highlights every 60 seconds if let last = lastHighlightsFetch, Date().timeIntervalSince(last) < 60 { return } let serverAPI = MLBServerAPI() do { highlights = try await serverAPI.fetchHighlights(gamePk: gamePk, gameDate: gameDate) lastHighlightsFetch = Date() } catch { // Highlights are supplementary — don't surface errors } } private func refreshWinProbability(gamePk: String) async { do { let entries = try await statsAPI.fetchWinProbability(gamePk: gamePk) if let latest = entries.last, let home = latest.homeTeamWinProbability, let away = latest.awayTeamWinProbability { winProbabilityHome = home winProbabilityAway = away } } catch { // Win probability is supplementary — don't surface errors } } }