Files
steipete--codexbar/Sources/CodexBarCore/Providers/CommandCode/CommandCodeUsageSnapshot.swift
T
2026-07-13 12:22:33 +08:00

125 lines
5.1 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Foundation
/// Parsed view of CommandCode `/internal/billing/credits` + `/internal/billing/subscriptions`.
public struct CommandCodeUsageSnapshot: Sendable {
/// USD remaining in the current monthly grant (`credits.monthlyCredits`).
public let monthlyCreditsRemaining: Double
/// USD top-up balance carried over (`credits.purchasedCredits`).
public let purchasedCredits: Double
/// USD remaining in the premium monthly grant (`credits.premiumMonthlyCredits`).
public let premiumMonthlyCredits: Double
/// USD remaining in the open-source monthly grant (`credits.opensourceMonthlyCredits`).
public let opensourceMonthlyCredits: Double
/// Subscription plan, or nil when the user is on the free tier.
public let plan: CommandCodePlanCatalog.Plan?
/// `currentPeriodEnd` from the active subscription.
public let billingPeriodEnd: Date?
/// Subscription status (e.g. `active`, `canceled`).
public let subscriptionStatus: String?
/// The optional subscription request timed out or failed for this refresh.
public let subscriptionEnrichmentUnavailable: Bool
public let updatedAt: Date
public init(
monthlyCreditsRemaining: Double,
purchasedCredits: Double,
premiumMonthlyCredits: Double,
opensourceMonthlyCredits: Double,
plan: CommandCodePlanCatalog.Plan?,
billingPeriodEnd: Date?,
subscriptionStatus: String?,
subscriptionEnrichmentUnavailable: Bool = false,
updatedAt: Date = Date())
{
self.monthlyCreditsRemaining = monthlyCreditsRemaining
self.purchasedCredits = purchasedCredits
self.premiumMonthlyCredits = premiumMonthlyCredits
self.opensourceMonthlyCredits = opensourceMonthlyCredits
self.plan = plan
self.billingPeriodEnd = billingPeriodEnd
self.subscriptionStatus = subscriptionStatus
self.subscriptionEnrichmentUnavailable = subscriptionEnrichmentUnavailable
self.updatedAt = updatedAt
}
/// USD allocation for the active monthly grant (from the catalog).
public var monthlyCreditsTotal: Double? {
self.plan?.monthlyCreditsUSD
}
/// USD spent in the current monthly grant (total remaining), clamped to [0, total].
public var monthlyCreditsUsed: Double? {
guard let total = self.monthlyCreditsTotal else { return nil }
return max(0, min(total, total - self.monthlyCreditsRemaining))
}
public func toUsageSnapshot() -> UsageSnapshot {
let primary = self.makePrimaryWindow()
let identity = ProviderIdentitySnapshot(
providerID: .commandcode,
accountEmail: nil,
accountOrganization: nil,
loginMethod: self.makeLoginMethod())
return UsageSnapshot(
primary: primary,
secondary: nil,
tertiary: nil,
providerCost: nil,
commandCodeSubscriptionEnrichmentUnavailable: self.subscriptionEnrichmentUnavailable,
commandCodeHasSubscriptionPlan: self.plan != nil,
commandCodeMonthlyGrantDepleted: self.monthlyCreditsRemaining <= 0,
updatedAt: self.updatedAt,
identity: identity)
}
private func makePrimaryWindow() -> RateWindow? {
guard let total = self.monthlyCreditsTotal, total > 0 else {
// Free / unknown plan with no allowance — surface 100% so the bar renders empty.
if self.monthlyCreditsRemaining > 0 || self.purchasedCredits > 0 {
return RateWindow(
usedPercent: 0,
windowMinutes: nil,
resetsAt: self.billingPeriodEnd,
resetDescription: nil)
}
return nil
}
let used = self.monthlyCreditsUsed ?? 0
let percent = min(100, max(0, (used / total) * 100))
return RateWindow(
usedPercent: percent,
windowMinutes: nil,
resetsAt: self.billingPeriodEnd,
resetDescription: nil)
}
private func makeLoginMethod() -> String? {
var parts: [String] = []
if let name = self.plan?.displayName, !name.isEmpty {
parts.append(name)
}
if let total = self.monthlyCreditsTotal {
let used = self.monthlyCreditsUsed ?? 0
parts.append("\(Self.formatUSD(used)) of \(Self.formatUSD(total))")
} else if self.monthlyCreditsRemaining > 0 {
parts.append("\(Self.formatUSD(self.monthlyCreditsRemaining)) remaining")
}
if self.purchasedCredits > 0 {
parts.append("+ \(Self.formatUSD(self.purchasedCredits)) credits")
}
return parts.isEmpty ? nil : parts.joined(separator: " · ")
}
static func formatUSD(_ value: Double) -> String {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.currencyCode = "USD"
formatter.locale = Locale(identifier: "en_US")
formatter.maximumFractionDigits = value < 100 ? 2 : 0
formatter.minimumFractionDigits = value < 100 ? 2 : 0
return formatter.string(from: NSNumber(value: value)) ?? "$\(value)"
}
}