245 lines · 9.0 KB · Swift
RawDownload
import Foundation public struct MiniMaxUsageSnapshot: Sendable {    public let planName: String?    public let availablePrompts: Int?    public let currentPrompts: Int?    public let remainingPrompts: Int?    public let windowMinutes: Int?    public let usedPercent: Double?    public let resetsAt: Date?    public let updatedAt: Date    public let services: [MiniMaxServiceUsage]?    public let billingSummary: MiniMaxBillingSummary?    public let pointsBalance: Double?    public let subscriptionExpiresAt: Date?    public let subscriptionRenewsAt: Date?     public var primaryService: MiniMaxServiceUsage? {        self.orderedQuotaServices.first    }     public var secondaryService: MiniMaxServiceUsage? {        let services = self.orderedQuotaServices        guard services.count >= 2 else { return nil }        return services[1]    }     public var tertiaryService: MiniMaxServiceUsage? {        let services = self.orderedQuotaServices        guard services.count >= 3 else { return nil }        return services[2]    }     public var orderedQuotaServices: [MiniMaxServiceUsage] {        guard let services, !services.isEmpty else { return [] }        return services.enumerated().sorted { lhs, rhs in            let lhsRank = self.quotaServiceRank(lhs.element, originalIndex: lhs.offset)            let rhsRank = self.quotaServiceRank(rhs.element, originalIndex: rhs.offset)            if lhsRank.primary != rhsRank.primary {                return lhsRank.primary < rhsRank.primary            }            if lhsRank.window != rhsRank.window {                return lhsRank.window < rhsRank.window            }            return lhsRank.originalIndex < rhsRank.originalIndex        }.map(\.element)    }     private func quotaServiceRank(        _ service: MiniMaxServiceUsage,        originalIndex: Int) -> (primary: Int, window: Int, originalIndex: Int)    {        (            primary: service.isPrimaryTextQuotaLane ? 0 : 1,            window: self.quotaWindowRank(service),            originalIndex: originalIndex)    }     private func quotaWindowRank(_ service: MiniMaxServiceUsage) -> Int {        let window = service.windowType.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()        if window == "weekly" {            return 1        }        return 0    }     public init(        planName: String?,        availablePrompts: Int?,        currentPrompts: Int?,        remainingPrompts: Int?,        windowMinutes: Int?,        usedPercent: Double?,        resetsAt: Date?,        updatedAt: Date,        services: [MiniMaxServiceUsage]? = nil,        billingSummary: MiniMaxBillingSummary? = nil,        pointsBalance: Double? = nil,        subscriptionExpiresAt: Date? = nil,        subscriptionRenewsAt: Date? = nil)    {        self.planName = planName        self.availablePrompts = availablePrompts        self.currentPrompts = currentPrompts        self.remainingPrompts = remainingPrompts        self.windowMinutes = windowMinutes        self.usedPercent = usedPercent        self.resetsAt = resetsAt        self.updatedAt = updatedAt        self.services = services        self.billingSummary = billingSummary        self.pointsBalance = pointsBalance        self.subscriptionExpiresAt = subscriptionExpiresAt        self.subscriptionRenewsAt = subscriptionRenewsAt    }     public func withBillingSummary(_ billingSummary: MiniMaxBillingSummary?) -> MiniMaxUsageSnapshot {        MiniMaxUsageSnapshot(            planName: self.planName,            availablePrompts: self.availablePrompts,            currentPrompts: self.currentPrompts,            remainingPrompts: self.remainingPrompts,            windowMinutes: self.windowMinutes,            usedPercent: self.usedPercent,            resetsAt: self.resetsAt,            updatedAt: self.updatedAt,            services: self.services,            billingSummary: billingSummary,            pointsBalance: self.pointsBalance,            subscriptionExpiresAt: self.subscriptionExpiresAt,            subscriptionRenewsAt: self.subscriptionRenewsAt)    }} extension MiniMaxUsageSnapshot {    public func toUsageSnapshot() -> UsageSnapshot {        // If we have services array, use that for multi-service support        if let services = self.services, !services.isEmpty {            let primaryWindow = self.rateWindow(for: self.primaryService)            let secondaryWindow = self.rateWindow(for: self.secondaryService)            let tertiaryWindow = self.rateWindow(for: self.tertiaryService)             let planName = self.planName?.trimmingCharacters(in: .whitespacesAndNewlines)            let loginMethod = (planName?.isEmpty ?? true) ? nil : planName            let identity = ProviderIdentitySnapshot(                providerID: .minimax,                accountEmail: nil,                accountOrganization: nil,                loginMethod: loginMethod)             return UsageSnapshot(                primary: primaryWindow,                secondary: secondaryWindow,                tertiary: tertiaryWindow,                providerCost: self.pointsBalanceSnapshot(),                minimaxUsage: self,                subscriptionExpiresAt: self.subscriptionExpiresAt,                subscriptionRenewsAt: self.subscriptionRenewsAt,                updatedAt: self.updatedAt,                identity: identity)        }         // Fallback to single-service mode for backward compatibility        let used = max(0, min(100, self.usedPercent ?? 0))        let resetDescription = self.limitDescription()        let primary = RateWindow(            usedPercent: used,            windowMinutes: self.windowMinutes,            resetsAt: self.resetsAt,            resetDescription: resetDescription)         let planName = self.planName?.trimmingCharacters(in: .whitespacesAndNewlines)        let loginMethod = (planName?.isEmpty ?? true) ? nil : planName        let identity = ProviderIdentitySnapshot(            providerID: .minimax,            accountEmail: nil,            accountOrganization: nil,            loginMethod: loginMethod)         return UsageSnapshot(            primary: primary,            secondary: nil,            tertiary: nil,            providerCost: self.pointsBalanceSnapshot(),            minimaxUsage: self,            subscriptionExpiresAt: self.subscriptionExpiresAt,            subscriptionRenewsAt: self.subscriptionRenewsAt,            updatedAt: self.updatedAt,            identity: identity)    }     private func rateWindow(for service: MiniMaxServiceUsage?) -> RateWindow? {        guard let service else { return nil }        let windowMinutes = self.windowMinutes(for: service)        return RateWindow(            usedPercent: max(0, min(100, service.percent)),            windowMinutes: windowMinutes,            resetsAt: service.resetsAt,            resetDescription: service.resetDescription)    }     private func limitDescription() -> String? {        guard let availablePrompts, availablePrompts > 0 else {            return self.windowDescription()        }         if let windowDescription = self.windowDescription() {            return "\(availablePrompts) prompts / \(windowDescription)"        }        return "\(availablePrompts) prompts"    }     private func windowDescription() -> String? {        guard let windowMinutes, windowMinutes > 0 else { return nil }        if windowMinutes % (24 * 60) == 0 {            let days = windowMinutes / (24 * 60)            return "\(days) \(days == 1 ? "day" : "days")"        }        if windowMinutes % 60 == 0 {            let hours = windowMinutes / 60            return "\(hours) \(hours == 1 ? "hour" : "hours")"        }        return "\(windowMinutes) \(windowMinutes == 1 ? "minute" : "minutes")"    }     private func windowMinutes(for service: MiniMaxServiceUsage) -> Int? {        let windowType = service.windowType.lowercased().trimmingCharacters(in: .whitespacesAndNewlines)         // Handle "Today" case - 24 hours = 1440 minutes        if windowType == "today" {            return 24 * 60        }        if windowType == "weekly" {            return 7 * 24 * 60        }         // Handle time duration formats like "5 hours", "30 minutes", etc.        let components = windowType.split(separator: " ")        guard components.count >= 2 else { return nil }         guard let value = Int(components[0]) else { return nil }        let unit = components[1].lowercased()         switch unit {        case "hour", "hours", "h", "hr", "hrs":            return value * 60        case "minute", "minutes", "min", "mins", "m":            return value        case "day", "days", "d":            return value * 24 * 60        default:            return nil        }    }     private func pointsBalanceSnapshot() -> ProviderCostSnapshot? {        guard let pointsBalance, pointsBalance >= 0 else { return nil }        return ProviderCostSnapshot(            used: pointsBalance,            limit: 0,            currencyCode: "Points",            period: "MiniMax points balance",            updatedAt: self.updatedAt)    }}