import SwiftUI
import NucleicCore

// MARK: - Context-window usage

/// Per-session context-window occupancy, derived from the latest turn's input tokens
/// against the model's window. Drives the document-icon metric in the composer pill.
struct ContextWindowUsage: Equatable {
    let usedTokens: Int
    let windowTokens: Int
    var utilization: Double {
        guard windowTokens > 0 else { return 0 }
        return min(100, Double(usedTokens) / Double(windowTokens) * 100)
    }
}

// MARK: - Shared formatting

/// Formatting + color helpers shared by the compact pill and the dashboard card, so
/// both read the same `AppStore` quota state identically.
private enum QuotaFormat {
    /// SF Symbols for each metric: a clock for the 5-hour window, a calendar for the
    /// weekly window, a document for the session context window.
    static let fiveHourIcon = "clock"
    static let weeklyIcon = "calendar"
    static let contextIcon = "doc.text"

    static func percent(_ value: Double) -> String { "\(Int(value.rounded()))%" }

    /// "6d 23h" / "2h 14m" / "14m" — rolls every 24h into days (the weekly window can
    /// be hundreds of hours out), and drops the finest unit once days are shown since
    /// minutes are noise at that scale. Coarse anyway: the views refresh every 30s.
    static func duration(_ interval: TimeInterval) -> String {
        let total = Int(interval)
        let days = total / 86_400
        let hours = (total % 86_400) / 3600
        let minutes = (total % 3600) / 60
        if days > 0 { return "\(days)d \(hours)h" }
        if hours > 0 { return "\(hours)h \(minutes)m" }
        if minutes > 0 { return "\(minutes)m" }
        return "<1m"
    }

    static func resetCaption(_ window: UsageWindow, now: Date) -> String? {
        guard let resetsAt = window.resetsAt else { return nil }
        let remaining = resetsAt.timeIntervalSince(now)
        guard remaining > 0 else { return "resetting…" }
        return "resets in \(duration(remaining))"
    }

    /// A filled-bar / glyph tint: calm accent until 75%, amber to 90%, red beyond.
    static func color(forPercent value: Double, palette: AppPalette) -> Color {
        switch value {
        case ..<75: return palette.accent
        case ..<90: return palette.attention
        default: return palette.danger
        }
    }

    static func statusLabel(_ status: String?) -> String {
        switch status {
        case "rejected": return "limit reached"
        case "allowed_warning": return "running low"
        case "allowed": return "OK"
        default: return status ?? "unknown"
        }
    }

    static func gauge(forStatus status: String?) -> String {
        switch status {
        case "rejected": return "gauge.with.dots.needle.100percent"
        case "allowed_warning": return "gauge.with.dots.needle.67percent"
        default: return "gauge.with.dots.needle.33percent"
        }
    }

    static func color(forStatus status: String?, palette: AppPalette) -> Color {
        switch status {
        case "rejected": return palette.danger
        case "allowed_warning": return palette.attention
        default: return .secondary
        }
    }
}

// MARK: - Compact pill

/// A compact, account-wide subscription-quota pill (used above the chat composer).
///
/// Shows, left to right: the session **context-window** usage (document icon), the
/// **5-hour** window (clock icon), and the **weekly** window (calendar icon) — each as a
/// `%` colored calm → amber → red. The account windows come from
/// `AppStore.subscriptionUsage` (real Pro/Max plan utilization via the OAuth usage
/// endpoint); when unavailable it falls back to the coarse `rate_limit_event` status,
/// and before any data arrives it shows a muted "Usage" placeholder so it's never blank.
struct QuotaIndicator: View {
    @Environment(AppStore.self) private var store
    /// Passed in rather than read from `\.appPalette`: the palette environment is
    /// injected inside `RootView`'s body, which doesn't reliably reach every host.
    let palette: AppPalette
    /// Session context-window usage, when shown in a chat (nil elsewhere).
    var contextUsage: ContextWindowUsage? = nil
    /// When true, render every metric in `.secondary` (to sit quietly beside the chat
    /// composer's branch label) instead of the per-utilization calm/amber/red colors.
    var monochrome: Bool = false

    private struct Metric: Identifiable {
        let id: String
        let icon: String
        let text: String
        let color: Color
    }

    var body: some View {
        // Re-evaluate every 30s so the "resets in" countdown stays current without a
        // tight per-second timer.
        TimelineView(.periodic(from: .now, by: 30)) { context in
            content(now: context.date)
                .font(.caption)
        }
    }

    @ViewBuilder
    private func content(now: Date) -> some View {
        let metrics = metrics()
        if !metrics.isEmpty {
            HStack(spacing: 10) {
                ForEach(metrics) { metric in
                    Label(metric.text, systemImage: metric.icon)
                        .foregroundStyle(monochrome ? AnyShapeStyle(.secondary) : AnyShapeStyle(metric.color))
                }
            }
            .help(helpText(now: now))
        } else {
            Label("Usage", systemImage: "gauge.with.dots.needle.33percent")
                .foregroundStyle(.secondary)
                .help("Checking Claude subscription usage…")
        }
    }

    private func metrics() -> [Metric] {
        var result: [Metric] = []
        if let ctx = contextUsage {
            result.append(Metric(
                id: "context", icon: QuotaFormat.contextIcon,
                text: QuotaFormat.percent(ctx.utilization),
                color: QuotaFormat.color(forPercent: ctx.utilization, palette: palette)))
        }
        if let usage = store.subscriptionUsage, usage.peakUtilization != nil {
            if let f = usage.fiveHour {
                result.append(Metric(
                    id: "fiveHour", icon: QuotaFormat.fiveHourIcon,
                    text: QuotaFormat.percent(f.utilization),
                    color: QuotaFormat.color(forPercent: f.utilization, palette: palette)))
            }
            if let w = usage.sevenDay {
                result.append(Metric(
                    id: "weekly", icon: QuotaFormat.weeklyIcon,
                    text: QuotaFormat.percent(w.utilization),
                    color: QuotaFormat.color(forPercent: w.utilization, palette: palette)))
            }
        } else if let rl = store.latestRateLimit {
            result.append(Metric(
                id: "status", icon: QuotaFormat.gauge(forStatus: rl.status),
                text: QuotaFormat.statusLabel(rl.status),
                color: QuotaFormat.color(forStatus: rl.status, palette: palette)))
        }
        return result
    }

    private func helpText(now: Date) -> String {
        var lines: [String] = []
        if let ctx = contextUsage {
            lines.append("Context window: \(QuotaFormat.percent(ctx.utilization)) "
                + "(\(ctx.usedTokens.formatted()) / \(ctx.windowTokens.formatted()) tokens)")
        }
        if let usage = store.subscriptionUsage, usage.peakUtilization != nil {
            lines.append("Claude subscription usage")
            func line(_ name: String, _ w: UsageWindow?) {
                guard let w else { return }
                var s = "  \(name): \(QuotaFormat.percent(w.utilization)) used"
                if let caption = QuotaFormat.resetCaption(w, now: now) { s += " · \(caption)" }
                lines.append(s)
            }
            line("5-hour", usage.fiveHour)
            line("Weekly", usage.sevenDay)
            line("Weekly · Opus", usage.sevenDayOpus)
            line("Weekly · Sonnet", usage.sevenDaySonnet)
        } else if let rl = store.latestRateLimit {
            var s = "Usage quota: \(QuotaFormat.statusLabel(rl.status))"
            if let resetsAt = rl.resetsAt {
                let remaining = resetsAt.timeIntervalSince(now)
                if remaining > 0 { s += " · resets in \(QuotaFormat.duration(remaining))" }
            }
            lines.append(s)
        }
        return lines.joined(separator: "\n")
    }
}

// MARK: - Dashboard card

/// The home-dashboard form of the quota indicator: a card (matching the stat cards)
/// with labeled progress bars for the 5-hour (clock) and weekly (calendar) windows and
/// reset countdowns. `matchHeight` lets the home view size it to the activity chart so
/// the two columns line up; it falls back to the `rate_limit_event` status, then a
/// "checking…" placeholder, so it always shows something next to the chart.
struct QuotaCard: View {
    @Environment(AppStore.self) private var store
    @Environment(\.appPalette) private var palette
    /// When > 0, the card stretches to at least this height (set to the activity
    /// chart's measured height so the two columns are the same height).
    var matchHeight: CGFloat = 0

    var body: some View {
        TimelineView(.periodic(from: .now, by: 30)) { context in
            VStack(alignment: .leading, spacing: 12) {
                HStack(spacing: 6) {
                    Image(systemName: "gauge.with.dots.needle.67percent").foregroundStyle(.secondary)
                    Text("Usage").font(.headline)
                }
                content(now: context.date)
            }
            .frame(maxWidth: .infinity, alignment: .topLeading)
            .padding(16)
            .frame(minHeight: matchHeight > 0 ? matchHeight : nil, alignment: .top)
            .background(AppTheme.surface, in: .rect(cornerRadius: 12))
            .overlay(RoundedRectangle(cornerRadius: 12).strokeBorder(AppTheme.hairline, lineWidth: 1))
        }
    }

    @ViewBuilder
    private func content(now: Date) -> some View {
        if let usage = store.subscriptionUsage, usage.peakUtilization != nil {
            VStack(alignment: .leading, spacing: 14) {
                windowRow("5-hour limit", icon: QuotaFormat.fiveHourIcon, usage.fiveHour, now: now)
                windowRow("Weekly limit", icon: QuotaFormat.weeklyIcon, usage.sevenDay, now: now)
            }
        } else if let rl = store.latestRateLimit {
            HStack(spacing: 6) {
                Image(systemName: QuotaFormat.gauge(forStatus: rl.status))
                    .foregroundStyle(QuotaFormat.color(forStatus: rl.status, palette: palette))
                Text("Quota \(QuotaFormat.statusLabel(rl.status))")
                    .foregroundStyle(.secondary)
            }
            .font(.callout)
        } else {
            Text("Checking subscription usage…")
                .font(.callout).foregroundStyle(.secondary)
        }
    }

    /// One window's bar; renders nothing for an absent window.
    @ViewBuilder
    private func windowRow(_ label: String, icon: String, _ window: UsageWindow?, now: Date) -> some View {
        if let window {
            let color = barColor(window.utilization)
            VStack(alignment: .leading, spacing: 4) {
                HStack(spacing: 5) {
                    Image(systemName: icon).font(.caption).foregroundStyle(.secondary)
                    Text(label).font(.subheadline)
                    Spacer()
                    Text(QuotaFormat.percent(window.utilization))
                        .font(.subheadline.monospacedDigit().weight(.semibold))
                        .foregroundStyle(color)
                }
                bar(fraction: window.utilization / 100, color: color)
                if let caption = QuotaFormat.resetCaption(window, now: now) {
                    Text(caption).font(.caption2).foregroundStyle(.secondary)
                }
            }
        }
    }

    /// Bar/label tint for the card. Uses the softer, appearance-aware `usageBar`
    /// (lighter in dark, lower-contrast in light than the saturated accent) while a
    /// window sits in normal range, then escalates to the shared amber/red once it
    /// runs low / over so the warning still reads loudly.
    private func barColor(_ utilization: Double) -> Color {
        switch utilization {
        case ..<75: return AppTheme.usageBar
        case ..<90: return palette.attention
        default: return palette.danger
        }
    }

    private func bar(fraction: Double, color: Color) -> some View {
        GeometryReader { geo in
            ZStack(alignment: .leading) {
                Capsule().fill(Color.secondary.opacity(0.18))
                Capsule().fill(color)
                    .frame(width: max(3, geo.size.width * min(1, max(0, fraction))))
            }
        }
        .frame(height: 6)
    }
}
