import CodexBarCore import Foundation enum CodexAccountHealth: Equatable { case ok case needsReauth case workspaceDeactivated case missingAuth case unavailable var label: String? { switch self { case .ok: nil case .needsReauth: "Needs re-auth" case .workspaceDeactivated: "Workspace deactivated" case .missingAuth: "Missing auth" case .unavailable: "Unavailable" } } static func status(for account: CodexVisibleAccount, error: String?) -> CodexAccountHealth { if let error { return self.status(forError: error) } if account.authenticationHealthLabel != nil { return .missingAuth } return .ok } static func status(forError error: String) -> CodexAccountHealth { let normalized = error.lowercased() if normalized.contains("deactivated") { return .workspaceDeactivated } if normalized.contains("expired") || normalized.contains("revoked") || normalized.contains("unauthorized") || normalized.contains("401") { return .needsReauth } if normalized.contains("missing"), normalized.contains("auth") { return .missingAuth } return .unavailable } } enum CodexAccountPresentationOrdering { static func orderedAccounts( _ accounts: [CodexVisibleAccount], snapshots: [CodexAccountUsageSnapshot], activeVisibleAccountID: String?) -> [CodexVisibleAccount] { guard accounts.count > 1 else { return accounts } let snapshotByID = Dictionary(uniqueKeysWithValues: snapshots.map { ($0.id, $0) }) let rankedAccounts = accounts.enumerated().map { index, account in RankedAccount( account: account, rank: Rank( account: account, snapshot: snapshotByID[account.id], activeVisibleAccountID: activeVisibleAccountID, originalIndex: index)) } let grouped = Dictionary(grouping: rankedAccounts, by: { Self.workspaceSortKey(for: $0.account) }) return grouped.values.sorted { lhs, rhs in (lhs.map(\.rank).min() ?? .last) < (rhs.map(\.rank).min() ?? .last) }.flatMap { group in group.sorted { lhs, rhs in lhs.rank < rhs.rank }.map(\.account) } } private struct RankedAccount { let account: CodexVisibleAccount let rank: Rank } private struct Rank: Comparable { static let last = Rank(bucket: Int.max, availabilityScore: -.greatestFiniteMagnitude, originalIndex: Int.max) let bucket: Int let availabilityScore: Double let displaySort: String let originalIndex: Int private init(bucket: Int, availabilityScore: Double, originalIndex: Int) { self.bucket = bucket self.availabilityScore = availabilityScore self.displaySort = "" self.originalIndex = originalIndex } init( account: CodexVisibleAccount, snapshot: CodexAccountUsageSnapshot?, activeVisibleAccountID: String?, originalIndex: Int) { self.originalIndex = originalIndex self.displaySort = account.menuDisplayName.lowercased() if account.id == activeVisibleAccountID { self.bucket = 0 } else { let health = CodexAccountHealth.status(for: account, error: snapshot?.error) if health != .ok { self.bucket = health == .missingAuth ? 4 : 3 } else if let availability = Self.availability(snapshot?.snapshot), availability <= 0 { self.bucket = 2 } else { self.bucket = 1 } } self.availabilityScore = Self.availability(snapshot?.snapshot) ?? -1 } static func < (lhs: Rank, rhs: Rank) -> Bool { if lhs.bucket != rhs.bucket { return lhs.bucket < rhs.bucket } if lhs.availabilityScore != rhs.availabilityScore { return lhs.availabilityScore > rhs.availabilityScore } if lhs.displaySort != rhs.displaySort { return lhs.displaySort < rhs.displaySort } return lhs.originalIndex < rhs.originalIndex } private static func availability(_ snapshot: UsageSnapshot?) -> Double? { guard let snapshot else { return nil } let session = snapshot.primary?.remainingPercent let weekly = snapshot.secondary?.remainingPercent return switch (session, weekly) { case let (.some(session), .some(weekly)): min(session, weekly) case let (.some(session), .none): session case let (.none, .some(weekly)): weekly case (.none, .none): nil } } } private static func workspaceSortKey(for account: CodexVisibleAccount) -> String { if let workspaceAccountID = account.workspaceAccountID, !workspaceAccountID.isEmpty { return workspaceAccountID.lowercased() } return account.menuWorkspaceLabel?.lowercased() ?? "personal" } } struct CodexAccountWorkspaceSection: Equatable { let title: String let accounts: [CodexVisibleAccount] } extension [CodexVisibleAccount] { func codexWorkspaceSections() -> [CodexAccountWorkspaceSection] { guard !self.isEmpty else { return [] } var sections: [CodexAccountWorkspaceSection] = [] for account in self { let title = account.menuWorkspaceLabel ?? "Personal" if let index = sections.firstIndex(where: { $0.title == title }) { var accounts = sections[index].accounts accounts.append(account) sections[index] = CodexAccountWorkspaceSection(title: title, accounts: accounts) } else { sections.append(CodexAccountWorkspaceSection(title: title, accounts: [account])) } } return sections } }