import Foundation

/// Compact, human-readable rendering of token totals for the dashboards, where a day's usage can
/// run from a handful to many millions and a full grouped number (`12,345,678`) would overflow a
/// small stat card. Mirrors the GitHub-style "12.3K / 4.5M" abbreviation both clients show.
public enum TokenCount {
    /// `942` · `12.3K` · `4.5M` · `1.2B`. Exact below 1,000; thereafter one decimal, with a
    /// trailing `.0` trimmed (`5K`, not `5.0K`). Negative inputs clamp to `0`.
    public static func abbreviated(_ count: Int) -> String {
        let n = max(0, count)
        switch n {
        case ..<1_000:
            return "\(n)"
        case ..<1_000_000:
            return scaled(n, by: 1_000, suffix: "K")
        case ..<1_000_000_000:
            return scaled(n, by: 1_000_000, suffix: "M")
        default:
            return scaled(n, by: 1_000_000_000, suffix: "B")
        }
    }

    /// Renderings of `count` from most detailed to least, widest first — for a `ViewThatFits`
    /// that shows the fullest number the available width allows and falls back to the compact
    /// form (the last entry, always the narrowest) when space is tight.
    ///
    /// Capped at **three numerals**: every candidate is scaled to a K/M/B unit and carries at most
    /// three significant digits — the wide form keeps three (`1.63B`), the narrow form two
    /// (`1.6B`). Round numbers and sub-unit values render the same at both, so they collapse to a
    /// single candidate.
    ///
    /// e.g. `1_632_451_000` → `["1.63B", "1.6B"]`, `4_532_100` → `["4.53M", "4.5M"]`,
    ///      `12_345` → `["12.3K", "12K"]`, `942` → `["942"]`.
    public static func adaptiveCandidates(_ count: Int) -> [String] {
        let n = max(0, count)
        let candidates = [compact(n, digits: 3), compact(n, digits: 2)]
        var seen = Set<String>()
        return candidates.filter { seen.insert($0).inserted }
    }

    /// `n` scaled to a K/M/B unit with at most `digits` significant numerals (so the result never
    /// shows more than `digits` numeral characters, except past 999 of the top unit — beyond any
    /// realistic token total). e.g. `compact(1_632_451_000, 3)` → `"1.63B"`, `digits: 2` → `"1.6B"`.
    private static func compact(_ count: Int, digits: Int) -> String {
        let n = max(0, count)
        // Below 1K there's no unit to scale to — the bare integer is already ≤3 numerals.
        guard n >= 1_000 else { return "\(n)" }
        let units: [(divisor: Double, suffix: String)] =
            [(1_000_000_000, "B"), (1_000_000, "M"), (1_000, "K")]

        // Render at unit `index`, spending the numeral budget left after the integer part on
        // decimals. Returns the text and its leading numeric value (to detect a rounding spill).
        func render(_ index: Int) -> (text: String, lead: Double) {
            let scaled = Double(n) / units[index].divisor
            let intDigits = scaled >= 100 ? 3 : (scaled >= 10 ? 2 : 1)
            let text = String(format: "%.\(max(0, digits - intDigits))f", scaled)
            return (text, Double(text) ?? scaled)
        }

        var index = n >= 1_000_000_000 ? 0 : (n >= 1_000_000 ? 1 : 2)
        var (text, lead) = render(index)
        // Rounding to the budget can spill into the next unit (999,999,999 → "1000M"): a value
        // that rounds to ≥1000 of its unit is really 1 of the next unit up — re-render there.
        if lead >= 1_000, index > 0 {
            index -= 1
            text = render(index).text
        }
        // Trim a trailing-zero tail (and any dangling point) so "2.00B" → "2B", "1.60B" → "1.6B".
        if text.contains(".") {
            while text.hasSuffix("0") { text.removeLast() }
            if text.hasSuffix(".") { text.removeLast() }
        }
        return text + units[index].suffix
    }

    private static func scaled(_ n: Int, by divisor: Int, suffix: String) -> String {
        // One decimal, but drop a pointless ".0" so round magnitudes read cleanly (and so a value
        // that rounds *up* to a whole — 12.95 → "13.0" → "13" — doesn't keep a stray decimal).
        var text = String(format: "%.1f", Double(n) / Double(divisor))
        if text.hasSuffix(".0") { text = String(text.dropLast(2)) }
        return text + suffix
    }
}
