import Foundation public struct CopilotUsageResponse: Sendable, Decodable { private struct AnyCodingKey: CodingKey { let stringValue: String let intValue: Int? init?(stringValue: String) { self.stringValue = stringValue self.intValue = nil } init?(intValue: Int) { self.stringValue = String(intValue) self.intValue = intValue } } public struct QuotaSnapshot: Sendable, Decodable { public let entitlement: Double public let remaining: Double public let percentRemaining: Double public let quotaId: String public let hasPercentRemaining: Bool public let unlimited: Bool private let entitlementWasDecoded: Bool private let remainingWasDecoded: Bool public var usedPercent: Double { max(0, 100 - self.percentRemaining) } public var overQuotaUsedPercent: Double? { self.usedPercent > 100 ? self.usedPercent : nil } public var isPlaceholder: Bool { if self.unlimited { return false } if self.entitlement == 0, self.remaining == 0, self.percentRemaining == 0, !self.hasPercentRemaining { return true } // An explicit zero-entitlement, zero-remaining snapshot carries no usable quota signal. // GitHub returns this shape for token-based billing / Copilot Business seats, // sometimes as percent_remaining=100 with a non-empty quota_id, which would // otherwise render as a misleading "0% used" (100 - 100). Treat it as a // placeholder so the usual handling drops it instead of showing fake usage. return self.entitlementWasDecoded && self.remainingWasDecoded && self.entitlement == 0 && self .remaining == 0 } private enum CodingKeys: String, CodingKey { case entitlement case remaining case percentRemaining = "percent_remaining" case quotaId = "quota_id" case unlimited } public init( entitlement: Double, remaining: Double, percentRemaining: Double, quotaId: String, hasPercentRemaining: Bool = true, unlimited: Bool = false) { self.entitlement = entitlement self.remaining = remaining self.percentRemaining = unlimited ? 100 : percentRemaining self.quotaId = quotaId self.hasPercentRemaining = unlimited || hasPercentRemaining self.unlimited = unlimited self.entitlementWasDecoded = true self.remainingWasDecoded = true } public init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let decodedEntitlement = Self.decodeNumberIfPresent(container: container, key: .entitlement) let decodedRemaining = Self.decodeNumberIfPresent(container: container, key: .remaining) self.entitlement = decodedEntitlement ?? 0 self.remaining = decodedRemaining ?? 0 self.entitlementWasDecoded = decodedEntitlement != nil self.remainingWasDecoded = decodedRemaining != nil let decodedUnlimited = try container.decodeIfPresent(Bool.self, forKey: .unlimited) ?? false let decodedPercent = Self.decodeNumberIfPresent(container: container, key: .percentRemaining) if decodedUnlimited { self.percentRemaining = 100 self.hasPercentRemaining = true } else if let decodedPercent { self.percentRemaining = decodedPercent self.hasPercentRemaining = true } else if let entitlement = decodedEntitlement, entitlement > 0, let remaining = decodedRemaining { let derived = (remaining / entitlement) * 100 self.percentRemaining = derived self.hasPercentRemaining = true } else { // Without percent_remaining and both inputs for derivation, the percent is unknown. self.percentRemaining = 0 self.hasPercentRemaining = false } self.quotaId = try container.decodeIfPresent(String.self, forKey: .quotaId) ?? "" self.unlimited = decodedUnlimited } private static func decodeNumberIfPresent( container: KeyedDecodingContainer, key: CodingKeys) -> Double? { if let value = try? container.decodeIfPresent(Double.self, forKey: key) { return value } if let value = try? container.decodeIfPresent(Int.self, forKey: key) { return Double(value) } if let value = try? container.decodeIfPresent(String.self, forKey: key) { return Double(value) } return nil } } public struct QuotaCounts: Sendable, Decodable { public let chat: Double? public let completions: Double? private enum CodingKeys: String, CodingKey { case chat case completions } public init(chat: Double?, completions: Double?) { self.chat = chat self.completions = completions } public init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.chat = Self.decodeNumberIfPresent(container: container, key: .chat) self.completions = Self.decodeNumberIfPresent(container: container, key: .completions) } private static func decodeNumberIfPresent( container: KeyedDecodingContainer, key: CodingKeys) -> Double? { if let value = try? container.decodeIfPresent(Double.self, forKey: key) { return value } if let value = try? container.decodeIfPresent(Int.self, forKey: key) { return Double(value) } if let value = try? container.decodeIfPresent(String.self, forKey: key) { return Double(value) } return nil } } public struct QuotaSnapshots: Sendable, Decodable { public let premiumInteractions: QuotaSnapshot? public let chat: QuotaSnapshot? private enum CodingKeys: String, CodingKey { case premiumInteractions = "premium_interactions" case chat } public init(premiumInteractions: QuotaSnapshot?, chat: QuotaSnapshot?) { self.premiumInteractions = premiumInteractions self.chat = chat } public init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) var premium = try container.decodeIfPresent(QuotaSnapshot.self, forKey: .premiumInteractions) var chat = try container.decodeIfPresent(QuotaSnapshot.self, forKey: .chat) if premium?.isPlaceholder == true { premium = nil } if chat?.isPlaceholder == true { chat = nil } if premium == nil || chat == nil { let dynamic = try decoder.container(keyedBy: AnyCodingKey.self) var fallbackPremium: QuotaSnapshot? var fallbackChat: QuotaSnapshot? var firstUsable: QuotaSnapshot? for key in dynamic.allKeys { let value: QuotaSnapshot do { guard let decoded = try dynamic.decodeIfPresent(QuotaSnapshot.self, forKey: key) else { continue } guard !decoded.isPlaceholder else { continue } value = decoded } catch { continue } let name = key.stringValue.lowercased() if firstUsable == nil { firstUsable = value } if fallbackChat == nil, name.contains("chat") { fallbackChat = value continue } if fallbackPremium == nil, name.contains("premium") || name.contains("completion") || name.contains("code") { fallbackPremium = value } } if premium == nil { premium = fallbackPremium } if chat == nil { chat = fallbackChat } if premium == nil, chat == nil { // If keys are unfamiliar, still expose one usable quota instead of failing. chat = firstUsable } } self.premiumInteractions = premium self.chat = chat } } public let quotaSnapshots: QuotaSnapshots public let copilotPlan: String public let tokenBasedBilling: Bool public let assignedDate: String? public let quotaResetDate: String? private enum CodingKeys: String, CodingKey { case quotaSnapshots = "quota_snapshots" case copilotPlan = "copilot_plan" case tokenBasedBilling = "token_based_billing" case assignedDate = "assigned_date" case quotaResetDate = "quota_reset_date" case monthlyQuotas = "monthly_quotas" case limitedUserQuotas = "limited_user_quotas" } public init( quotaSnapshots: QuotaSnapshots, copilotPlan: String, tokenBasedBilling: Bool = false, assignedDate: String?, quotaResetDate: String?) { self.quotaSnapshots = quotaSnapshots self.copilotPlan = copilotPlan self.tokenBasedBilling = tokenBasedBilling self.assignedDate = assignedDate self.quotaResetDate = quotaResetDate } public init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let directSnapshots = try container.decodeIfPresent(QuotaSnapshots.self, forKey: .quotaSnapshots) let monthlyQuotas = try container.decodeIfPresent(QuotaCounts.self, forKey: .monthlyQuotas) let limitedUserQuotas = try container.decodeIfPresent(QuotaCounts.self, forKey: .limitedUserQuotas) let monthlyLimitedSnapshots = Self.makeQuotaSnapshots(monthly: monthlyQuotas, limited: limitedUserQuotas) let premium = Self.usableQuotaSnapshot(from: directSnapshots?.premiumInteractions) ?? Self.usableQuotaSnapshot(from: monthlyLimitedSnapshots?.premiumInteractions) let chat = Self.usableQuotaSnapshot(from: directSnapshots?.chat) ?? Self.usableQuotaSnapshot(from: monthlyLimitedSnapshots?.chat) if premium != nil || chat != nil { self.quotaSnapshots = QuotaSnapshots(premiumInteractions: premium, chat: chat) } else { self.quotaSnapshots = directSnapshots ?? QuotaSnapshots(premiumInteractions: nil, chat: nil) } self.copilotPlan = try container.decodeIfPresent(String.self, forKey: .copilotPlan) ?? "unknown" self.tokenBasedBilling = try container.decodeIfPresent(Bool.self, forKey: .tokenBasedBilling) ?? false self.assignedDate = try container.decodeIfPresent(String.self, forKey: .assignedDate) self.quotaResetDate = try container.decodeIfPresent(String.self, forKey: .quotaResetDate) } private static func makeQuotaSnapshots(monthly: QuotaCounts?, limited: QuotaCounts?) -> QuotaSnapshots? { let premium = Self.makeQuotaSnapshot( monthly: monthly?.completions, limited: limited?.completions, quotaID: "completions") let chat = Self.makeQuotaSnapshot( monthly: monthly?.chat, limited: limited?.chat, quotaID: "chat") guard premium != nil || chat != nil else { return nil } return QuotaSnapshots(premiumInteractions: premium, chat: chat) } private static func makeQuotaSnapshot(monthly: Double?, limited: Double?, quotaID: String) -> QuotaSnapshot? { guard monthly != nil || limited != nil else { return nil } guard let monthly else { // Without a monthly denominator, avoid fabricating a misleading percentage. return nil } guard let limited else { // Without the limited/remaining value, usage is unknown. return nil } let entitlement = max(0, monthly) guard entitlement > 0 else { // A zero denominator cannot produce a meaningful percentage. return nil } let remaining = max(0, limited) let percentRemaining = max(0, min(100, (remaining / entitlement) * 100)) return QuotaSnapshot( entitlement: entitlement, remaining: remaining, percentRemaining: percentRemaining, quotaId: quotaID) } private static func usableQuotaSnapshot(from snapshot: QuotaSnapshot?) -> QuotaSnapshot? { guard let snapshot, !snapshot.isPlaceholder, snapshot.hasPercentRemaining else { return nil } return snapshot } }