import AppKit
import CodexBarCore
import Foundation

extension StatusItemController {
    func applyStoredMenuBarLayoutIfNeeded(
        provider: UsageProvider,
        snapshot: UsageSnapshot?,
        icon: NSImage?,
        warningFlash: Bool,
        statusItem: NSStatusItem,
        now: Date = .init())
        -> Bool?
    {
        let resolution = self.settings.menuBarLayoutResolution(for: provider)
        guard !resolution.usesLegacyRendering,
              self.settings.menuBarIconStyle == .iconAndPercent,
              let button = statusItem.button
        else {
            statusItem.length = NSStatusItem.variableLength
            return nil
        }

        let renderedIcon = icon.map { warningFlash ? Self.quotaWarningFlashImage(base: $0) : $0 }
        let data = self.menuBarLayoutRenderData(
            provider: provider,
            snapshot: snapshot,
            warningFlash: warningFlash,
            now: now)
        let appearanceName = button.effectiveAppearance.bestMatch(from: [.darkAqua, .aqua])?.rawValue ?? "default"
        let options = MenuBarLayoutRenderOptions(
            size: self.settings.menuBarLayoutSize,
            highContrast: self.shouldUseHighContrastStatusItemContent,
            showUsed: self.settings.usageBarsShowUsed,
            appearanceName: appearanceName,
            isDebugApp: Self.isDebugApp(bundleIdentifier: Bundle.main.bundleIdentifier),
            now: now)
        let rendered = self.menuBarLayoutRenderer.render(
            layout: resolution.layout,
            data: data,
            icon: renderedIcon,
            options: options)
        let wasCached = button.image == nil
            && button.imagePosition == .noImage
            && button.attributedTitle.isEqual(to: rendered.attributedTitle)
        self.setButtonLayoutContent(rendered, for: button, statusItem: statusItem)
        return wasCached
    }

    func menuBarLayoutRenderData(
        provider: UsageProvider,
        snapshot: UsageSnapshot?,
        warningFlash: Bool,
        now: Date = .init())
        -> MenuBarLayoutRenderData
    {
        let windows = self.menuBarLayoutWindows(provider: provider, snapshot: snapshot, now: now)
        let scopedNamed = MenuBarLayoutSemanticWindowResolver.scopedWeeklyNamedWindow(snapshot: snapshot)
        let paceWindow = windows.weekly ?? windows.automatic
        let runsOut = paceWindow
            .flatMap { self.store.weeklyPace(provider: provider, window: $0, now: now) }
            .flatMap { UsagePaceText.weeklyDetail(provider: provider, pace: $0, now: now).rightLabel }
        let costStrings = self.menuBarLayoutCostStrings(provider: provider, now: now)
        let providerName = L(self.store.metadata(for: provider).displayName)
        let accountLabel = self.menuBarLayoutAccountLabel(provider: provider, snapshot: snapshot)

        return MenuBarLayoutRenderData(
            provider: provider,
            iconKey: "\(provider.rawValue):\(warningFlash ? "warning" : "normal")",
            providerName: providerName,
            accountLabel: accountLabel,
            session: MenuBarLayoutRenderWindow(windows.session),
            weekly: MenuBarLayoutRenderWindow(windows.weekly),
            scopedWeekly: MenuBarLayoutRenderWindow(scopedNamed?.window),
            scopedWeeklyTitle: scopedNamed?.title,
            automatic: MenuBarLayoutRenderWindow(windows.automatic),
            sessionPace: self.store.menuBarLayoutPaceText(provider: provider, window: windows.session, now: now),
            weeklyPace: self.store.menuBarLayoutPaceText(provider: provider, window: windows.weekly, now: now),
            automaticPace: self.store.menuBarLayoutPaceText(
                provider: provider,
                window: windows.automatic,
                now: now),
            runsOut: runsOut,
            costToday: costStrings.today,
            cost30d: costStrings.last30Days)
    }

    func menuBarLayoutAccountLabel(provider: UsageProvider, snapshot: UsageSnapshot?) -> String? {
        let rawAccountLabel = snapshot?.accountEmail(for: provider)?
            .trimmingCharacters(in: .whitespacesAndNewlines)
        return self.settings.hidePersonalInfo || rawAccountLabel?.isEmpty != false
            ? nil
            : rawAccountLabel
    }

    func menuBarLayoutCostStrings(
        provider: UsageProvider,
        now: Date = .init())
        -> (today: String?, last30Days: String?)
    {
        let snapshot = self.store.tokenSnapshotForCurrentProviderConfig(for: provider)?.snapshot
        let sourceCurrencyCode = snapshot?.currencyCode ?? "USD"
        let preferredCurrencyCode = self.settings.preferredCurrencyCode

        let today = MenuBarLayoutCostResolver.todayCostUSD(snapshot: snapshot, now: now).map {
            UsageFormatter.convertedCostString(
                $0,
                preferredCurrency: preferredCurrencyCode,
                providerCurrency: sourceCurrencyCode)
        }
        let last30Days = snapshot?.last30DaysCostUSD.map {
            UsageFormatter.convertedCostString(
                $0,
                preferredCurrency: preferredCurrencyCode,
                providerCurrency: sourceCurrencyCode)
        }
        return (today, last30Days)
    }

    func menuBarLayoutWindows(
        provider: UsageProvider,
        snapshot: UsageSnapshot?,
        now: Date)
        -> (session: RateWindow?, weekly: RateWindow?, automatic: RateWindow?)
    {
        if provider == .codex,
           let projection = self.store.codexConsumerProjectionIfNeeded(
               for: provider,
               surface: .menuBar,
               snapshotOverride: snapshot,
               now: now)
        {
            let session = projection.menuBarSelectableRateWindow(for: .session)
            let weekly = projection.menuBarSelectableRateWindow(for: .weekly)
            let automatic = projection.automaticMenuBarWindow()
            return (session, weekly, automatic)
        }

        let semanticWindows = MenuBarLayoutSemanticWindowResolver.windows(
            provider: provider,
            snapshot: snapshot)
        let automatic = MenuBarMetricWindowResolver.rateWindow(
            preference: .automatic,
            provider: provider,
            snapshot: snapshot,
            supportsAverage: self.settings.menuBarMetricSupportsAverage(for: provider),
            antigravityPrioritizeExhaustedQuotas: self.settings.antigravityPrioritizeExhaustedQuotas,
            now: now)
        return (semanticWindows.session, semanticWindows.weekly, automatic)
    }

    private func setButtonLayoutContent(
        _ rendered: MenuBarLayoutRenderedTitle,
        for button: NSStatusBarButton,
        statusItem: NSStatusItem)
    {
        button.image = nil
        button.imagePosition = .noImage
        if !button.attributedTitle.isEqual(to: rendered.attributedTitle) {
            button.attributedTitle = rendered.attributedTitle
        }
        if button.accessibilityTitle() != rendered.accessibilityLabel {
            button.setAccessibilityTitle(rendered.accessibilityLabel)
        }

        // AppKit exposes no content-inset API on NSStatusBarButton. Explicit item length is the actual
        // status-item padding mechanism: tight removes most edge space; regular keeps the native breathing room.
        let bounds = rendered.attributedTitle.boundingRect(
            with: NSSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude),
            options: [.usesLineFragmentOrigin, .usesFontLeading])
        let horizontalPadding: CGFloat = self.settings.menuBarLayoutGap == .tight ? 3 : 10
        statusItem.length = max(18, ceil(bounds.width) + horizontalPadding)
    }
}
