import Foundation import SwiftUI // MARK: - Family Plan struct FamilyPlan: Identifiable, Equatable, Codable { let id: String let ownerId: String let ownerName: String let members: [FamilyMember] let maxMembers: Int let subscriptionStatus: SubscriptionStatus let renewalDate: Date? let createdAt: Date enum CodingKeys: String, CodingKey { case id, ownerId, ownerName, members, maxMembers, subscriptionStatus, renewalDate, createdAt } init( id: String, ownerId: String, ownerName: String, members: [FamilyMember], maxMembers: Int, subscriptionStatus: SubscriptionStatus, renewalDate: Date?, createdAt: Date ) { self.id = id self.ownerId = ownerId self.ownerName = ownerName self.members = members self.maxMembers = maxMembers self.subscriptionStatus = subscriptionStatus self.renewalDate = renewalDate self.createdAt = createdAt } static func == (lhs: FamilyPlan, rhs: FamilyPlan) -> Bool { lhs.id == rhs.id } var availableSlots: Int { maxMembers - members.count } var isActive: Bool { subscriptionStatus == .active } } // MARK: - Family Member struct FamilyMember: Identifiable, Equatable, Codable { let id: String let name: String let email: String let role: MemberRole let joinedAt: Date let avatarUrl: String? var isPrimary: Bool var totalDistanceKm: Double var totalWorkouts: Int var weeklyDistanceKm: Double var weeklyWorkouts: Int enum CodingKeys: String, CodingKey { case id, name, email, role, joinedAt, avatarUrl, isPrimary, totalDistanceKm, totalWorkouts, weeklyDistanceKm, weeklyWorkouts } init( id: String, name: String, email: String, role: MemberRole, joinedAt: Date, avatarUrl: String?, isPrimary: Bool, totalDistanceKm: Double, totalWorkouts: Int, weeklyDistanceKm: Double, weeklyWorkouts: Int ) { self.id = id self.name = name self.email = email self.role = role self.joinedAt = joinedAt self.avatarUrl = avatarUrl self.isPrimary = isPrimary self.totalDistanceKm = totalDistanceKm self.totalWorkouts = totalWorkouts self.weeklyDistanceKm = weeklyDistanceKm self.weeklyWorkouts = weeklyWorkouts } static func == (lhs: FamilyMember, rhs: FamilyMember) -> Bool { lhs.id == rhs.id } } // MARK: - Member Role enum MemberRole: String, CaseIterable, Codable { case owner case member case pending var displayName: String { switch self { case .owner: return "Owner" case .member: return "Member" case .pending: return "Pending" } } var icon: String { switch self { case .owner: return "star.fill" case .member: return "person.fill" case .pending: return "person.crop.circle.badge.exclamationmark" } } } // MARK: - Subscription Status enum SubscriptionStatus: String, CaseIterable, Codable { case active case expired case cancelled case pending var displayName: String { switch self { case .active: return "Active" case .expired: return "Expired" case .cancelled: return "Cancelled" case .pending: return "Pending" } } var color: Color { switch self { case .active: return .green case .expired: return .red case .cancelled: return .orange case .pending: return .yellow } } } // MARK: - Family Leaderboard Entry struct FamilyLeaderboardEntry: Identifiable, Codable { let id: String let memberId: String let memberName: String let avatarUrl: String? let metric: LeaderboardMetric let value: Double let rank: Int } // MARK: - Leaderboard Metric enum LeaderboardMetric: String, CaseIterable, Codable { case distance case workouts case streak var displayName: String { switch self { case .distance: return "Distance" case .workouts: return "Workouts" case .streak: return "Streak" } } var unit: String { switch self { case .distance: return "km" case .workouts: return "" case .streak: return "days" } } } // MARK: - Invite Member Request struct InviteMemberRequest: Encodable { let email: String let name: String } // MARK: - API Response Types struct FamilyPlanDetailResponse: Decodable { let plan: FamilyPlan } struct InviteMemberResponse: Decodable { let success: Bool let invitationId: String let memberEmail: String } struct RemoveMemberResponse: Decodable { let success: Bool let memberId: String } struct FamilyLeaderboardResponse: Decodable { let entries: [FamilyLeaderboardEntry] let metric: LeaderboardMetric }