import Foundation

/// Projects parsed `cswap --list --json` rows into the provider-neutral
/// account snapshot consumed by menus. Identity uses the source-issued numeric
/// slot (`claude-swap:<slot>`), never email or credential-derived values.
public enum ClaudeSwapAccountProjection {
    public static let sourceName = "claude-swap"
    public static let sourceLabel = "claude-swap"
    static let fiveHourWindowMinutes = 5 * 60
    static let sevenDayWindowMinutes = 7 * 24 * 60

    public static func shouldPresentAccounts(accountCount: Int, showSingleAccount: Bool) -> Bool {
        accountCount >= (showSingleAccount ? 1 : 2)
    }

    public static func accountSnapshots(
        from list: ClaudeSwapAccountList,
        now: Date = Date()) -> [ProviderAccountUsageSnapshot]
    {
        let ordered = list.accounts.sorted { lhs, rhs in
            if lhs.isActive != rhs.isActive {
                return lhs.isActive
            }
            return lhs.number < rhs.number
        }
        return ordered.map { row in
            ProviderAccountUsageSnapshot(
                id: ProviderAccountIdentity(source: self.sourceName, opaqueID: String(row.number)),
                provider: .claude,
                displayLabel: self.displayLabel(for: row),
                isActive: row.isActive,
                canActivate: !row.isActive && self.canActivate(row),
                snapshot: self.usageSnapshot(for: row, now: now),
                error: self.errorText(for: row),
                sourceLabel: self.sourceLabel)
        }
    }

    public static func displayError(
        accountError: String?,
        adapterError: String?,
        switchError: String? = nil) -> String?
    {
        switchError.map { "Account switch failed: \($0)" }
            ?? accountError
            ?? adapterError.map { "Showing the last successful update: \($0)" }
    }

    static func displayLabel(for row: ClaudeSwapAccountRow) -> String {
        row.email.isEmpty ? "Account \(row.number)" : row.email
    }

    private static func usageSnapshot(for row: ClaudeSwapAccountRow, now: Date) -> UsageSnapshot? {
        guard row.usageStatus == .ok else { return nil }
        let primary = row.fiveHour.map { window in
            RateWindow(
                usedPercent: window.usedPercent,
                windowMinutes: self.fiveHourWindowMinutes,
                resetsAt: window.resetsAt,
                resetDescription: nil)
        }
        let secondary = row.sevenDay.map { window in
            RateWindow(
                usedPercent: window.usedPercent,
                windowMinutes: self.sevenDayWindowMinutes,
                resetsAt: window.resetsAt,
                resetDescription: nil)
        }
        let scoped = self.scopedRateWindows(for: row)
        guard primary != nil || secondary != nil || !scoped.isEmpty else { return nil }
        return UsageSnapshot(
            primary: primary,
            secondary: secondary,
            extraRateWindows: scoped.isEmpty ? nil : scoped,
            updatedAt: now,
            identity: ProviderIdentitySnapshot(
                providerID: .claude,
                accountEmail: self.displayLabel(for: row),
                accountOrganization: nil,
                loginMethod: self.sourceLabel))
    }

    private static func scopedRateWindows(for row: ClaudeSwapAccountRow) -> [NamedRateWindow] {
        ClaudeScopedWeeklyLimitMapper.extraRateWindows(from: row.scoped.map { window in
            ClaudeScopedWeeklyLimitMapper.Limit(
                kind: "weekly_scoped",
                group: "weekly",
                percent: window.usedPercent,
                resetsAt: window.resetsAt,
                modelID: nil,
                modelName: window.name)
        })
    }

    private static func errorText(for row: ClaudeSwapAccountRow) -> String? {
        switch row.usageStatus {
        case .ok:
            row.fiveHour == nil && row.sevenDay == nil && self.scopedRateWindows(for: row).isEmpty
                ? "No usage windows reported."
                : nil
        case .tokenExpired:
            "Token expired. Switch to this account in claude-swap to refresh it."
        case .reloginRequired:
            "Re-login required. Re-authenticate this account in claude-swap."
        case .apiKey:
            "API-key account; subscription usage is unavailable."
        case .keychainUnavailable:
            "claude-swap could not read the active account's Keychain entry."
        case .noCredentials:
            "No stored credentials for this account slot."
        case .unavailable:
            "Usage fetch failed."
        case let .unknown(raw):
            "Unrecognized claude-swap status: \(raw)"
        }
    }

    private static func canActivate(_ row: ClaudeSwapAccountRow) -> Bool {
        switch row.usageStatus {
        case .ok, .apiKey, .unavailable:
            true
        case .tokenExpired, .reloginRequired, .keychainUnavailable, .noCredentials, .unknown:
            false
        }
    }
}
