import Foundation import SwiftUI @MainActor class ChallengeViewModel: ObservableObject { @Published var challenges: [Challenge] = [] @Published var selectedChallenge: Challenge? @Published var leaderboard: [LeaderboardEntry] = [] @Published var isLoading: Bool = false @Published var error: ChallengeError? @Published var filter: ChallengeFilter = ChallengeFilter() private let service: ChallengeServiceProtocol init(service: ChallengeServiceProtocol = ChallengeService()) { self.service = service } func fetchChallenges() async { isLoading = true error = nil defer { isLoading = false } do { challenges = try await service.listChallenges(filter: filter) } catch let error as ChallengeError { self.error = error } catch { print("Failed to fetch challenges: \(error)") } } func selectChallenge(id: String) async { isLoading = true error = nil defer { isLoading = false } do { let result = try await service.getChallenge(id: id) selectedChallenge = result.challenge if let index = challenges.firstIndex(where: { $0.id == id }) { challenges[index] = result.challenge objectWillChange.send() } } catch let error as ChallengeError { self.error = error } catch { print("Failed to get challenge: \(error)") } } func createChallenge(request: CreateChallengeRequest) async -> Challenge? { isLoading = true error = nil defer { isLoading = false } do { let challenge = try await service.createChallenge(request: request) challenges.insert(challenge, at: 0) objectWillChange.send() return challenge } catch let error as ChallengeError { self.error = error return nil } catch { print("Failed to create challenge: \(error)") return nil } } func updateChallenge(id: String, request: UpdateChallengeRequest) async { isLoading = true error = nil defer { isLoading = false } do { let updatedChallenge = try await service.updateChallenge(id: id, request: request) if let index = challenges.firstIndex(where: { $0.id == id }) { challenges[index] = updatedChallenge objectWillChange.send() } if selectedChallenge?.id == id { selectedChallenge = updatedChallenge } } catch let error as ChallengeError { self.error = error } catch { print("Failed to update challenge: \(error)") } } func joinChallenge(id: String) async { do { try await service.joinChallenge(id: id) if let index = challenges.firstIndex(where: { $0.id == id }) { challenges[index].participationStatus = .participating challenges[index].participantCount += 1 objectWillChange.send() } } catch { print("Failed to join challenge: \(error)") } } func leaveChallenge(id: String) async { do { try await service.leaveChallenge(id: id) if let index = challenges.firstIndex(where: { $0.id == id }) { challenges[index].participationStatus = .notParticipating challenges[index].participantCount = max(0, challenges[index].participantCount - 1) objectWillChange.send() } } catch { print("Failed to leave challenge: \(error)") } } func fetchLeaderboard(challengeId: String) async { isLoading = true error = nil defer { isLoading = false } do { leaderboard = try await service.getLeaderboard(challengeId: challengeId) } catch let error as ChallengeError { self.error = error } catch { print("Failed to fetch leaderboard: \(error)") } } func submitProgress(challengeId: String, progress: ProgressSubmission) async { do { let result = try await service.submitProgress(challengeId: challengeId, progress: progress) if let index = challenges.firstIndex(where: { $0.id == challengeId }) { challenges[index].userProgress = result.progress objectWillChange.send() } if selectedChallenge?.id == challengeId { selectedChallenge?.userProgress = result.progress } } catch { print("Failed to submit progress: \(error)") } } var activeChallenges: [Challenge] { challenges.filter { $0.isActive }.sorted { $0.endDate < $1.endDate } } var upcomingChallenges: [Challenge] { challenges.filter { $0.isUpcoming }.sorted { $0.startDate < $1.startDate } } var completedChallenges: [Challenge] { challenges.filter { $0.isCompleted }.sorted { $0.endDate > $1.endDate } } var userChallenges: [Challenge] { challenges.filter { $0.participationStatus == .participating } } var challengeTypes: [ChallengeType] { ChallengeType.allCases } var challengeStatuses: [ChallengeStatus] { ChallengeStatus.allCases } }