78 lines
2.5 KiB
Swift
78 lines
2.5 KiB
Swift
import Foundation
|
|
|
|
// MARK: - Abacus Usage Snapshot
|
|
|
|
public struct AbacusUsageSnapshot: Sendable {
|
|
public let creditsUsed: Double?
|
|
public let creditsTotal: Double?
|
|
public let resetsAt: Date?
|
|
public let planName: String?
|
|
|
|
public init(
|
|
creditsUsed: Double? = nil,
|
|
creditsTotal: Double? = nil,
|
|
resetsAt: Date? = nil,
|
|
planName: String? = nil)
|
|
{
|
|
self.creditsUsed = creditsUsed
|
|
self.creditsTotal = creditsTotal
|
|
self.resetsAt = resetsAt
|
|
self.planName = planName
|
|
}
|
|
|
|
public func toUsageSnapshot() -> UsageSnapshot {
|
|
let percentUsed: Double = if let used = self.creditsUsed, let total = self.creditsTotal, total > 0 {
|
|
(used / total) * 100.0
|
|
} else {
|
|
0
|
|
}
|
|
|
|
let resetDesc: String? = if let used = self.creditsUsed, let total = self.creditsTotal {
|
|
"\(Self.formatCredits(used)) / \(Self.formatCredits(total)) credits"
|
|
} else {
|
|
nil
|
|
}
|
|
|
|
// Derive window from actual billing cycle when possible.
|
|
// Assume the cycle started one calendar month before resetsAt.
|
|
let windowMinutes: Int = if let resetDate = self.resetsAt,
|
|
let cycleStart = Calendar.current.date(byAdding: .month, value: -1, to: resetDate)
|
|
{
|
|
max(1, Int(resetDate.timeIntervalSince(cycleStart) / 60))
|
|
} else {
|
|
30 * 24 * 60
|
|
}
|
|
|
|
let primary = RateWindow(
|
|
usedPercent: percentUsed,
|
|
windowMinutes: windowMinutes,
|
|
resetsAt: self.resetsAt,
|
|
resetDescription: resetDesc)
|
|
|
|
let identity = ProviderIdentitySnapshot(
|
|
providerID: .abacus,
|
|
accountEmail: nil,
|
|
accountOrganization: nil,
|
|
loginMethod: self.planName)
|
|
|
|
return UsageSnapshot(
|
|
primary: primary,
|
|
secondary: nil,
|
|
tertiary: nil,
|
|
providerCost: nil,
|
|
updatedAt: Date(),
|
|
identity: identity)
|
|
}
|
|
|
|
// MARK: - Formatting
|
|
|
|
/// Thread-safe credit formatting — allocates per call to avoid shared mutable state.
|
|
private static func formatCredits(_ value: Double) -> String {
|
|
let formatter = NumberFormatter()
|
|
formatter.numberStyle = .decimal
|
|
formatter.locale = Locale(identifier: "en_US")
|
|
formatter.maximumFractionDigits = value >= 1000 ? 0 : 1
|
|
return formatter.string(from: NSNumber(value: value)) ?? String(format: "%.0f", value)
|
|
}
|
|
}
|