import AppKit
import CodexBarCore
import SwiftUI
import Testing
@testable import CodexBar

@MainActor
@Suite(.serialized)
struct StatusMenuCostMenuCardTests {
    @Test
    func `cost menu omits detail text beside a history submenu`() {
        let tokenUsage = UsageMenuCardView.Model.TokenUsageSection(
            sessionLine: "Today: $74.83 - 87M tokens",
            monthLine: "Last 30 days: $4,279.64 - 5.7B tokens",
            hintLine: "Costs are estimated from local usage.",
            errorLine: "Cost refresh failed.",
            errorCopyText: nil)

        let visibleLines = StatusItemController.costMenuVisibleDetailLines(
            provider: .codex,
            tokenUsage: tokenUsage,
            hasSubmenu: true)
        #expect(visibleLines == [])
        #expect(StatusItemController.costMenuVisibleDetailLines(
            provider: .claude,
            tokenUsage: tokenUsage,
            hasSubmenu: true) == [])

        let fallbackTitle = StatusItemController.costMenuFallbackAttributedTitle(
            title: "Cost",
            visibleDetailLines: visibleLines)
        #expect(fallbackTitle.string == "Cost")
    }

    @Test
    func `cost menu preserves summary lines without history submenu`() {
        let tokenUsage = UsageMenuCardView.Model.TokenUsageSection(
            sessionLine: "Today: $74.83 - 87M tokens",
            monthLine: "Last 30 days: $4,279.64 - 5.7B tokens",
            hintLine: "Costs are estimated from local usage.",
            errorLine: "Cost refresh failed.",
            errorCopyText: nil)

        let visibleLines = StatusItemController.costMenuVisibleDetailLines(
            provider: .codex,
            tokenUsage: tokenUsage,
            hasSubmenu: false)
        #expect(visibleLines == [
            "Today: $74.83 - 87M tokens",
            "Last 30 days: $4,279.64 - 5.7B tokens",
            "Costs are estimated from local usage.",
            "Cost refresh failed.",
        ])

        let fallbackTitle = StatusItemController.costMenuFallbackAttributedTitle(
            title: "Cost",
            visibleDetailLines: visibleLines)
        #expect(fallbackTitle.string.contains("Today: $74.83 - 87M tokens"))
        #expect(fallbackTitle.string.contains("Last 30 days: $4,279.64 - 5.7B tokens"))
        #expect(fallbackTitle.string.contains("Costs are estimated from local usage."))
        #expect(fallbackTitle.string.contains("Cost refresh failed."))
    }

    @Test
    func `cost menu tooltip preserves hint and error details`() {
        let tokenUsage = UsageMenuCardView.Model.TokenUsageSection(
            sessionLine: "Today: $1.00",
            monthLine: "Last 30 days: $9.00",
            hintLine: "Costs are estimated from local usage.",
            errorLine: "Cost refresh failed.",
            errorCopyText: nil)

        #expect(StatusItemController.costMenuTooltipLines(provider: .codex, tokenUsage: tokenUsage) == [
            "Today: $1.00",
            "Last 30 days: $9.00",
            "Costs are estimated from local usage.",
            "Cost refresh failed.",
        ])
        #expect(StatusItemController.costMenuTooltipLines(provider: .claude, tokenUsage: tokenUsage) == [
            "Today: $1.00",
            "Last 30 days: $9.00",
            "Costs are estimated from local usage.",
            "Cost refresh failed.",
        ])
    }

    @Test
    func `cost menu with history submenu omits native tooltip`() {
        let settings = self.makeSettings()
        let fetcher = UsageFetcher()
        let store = UsageStore(
            fetcher: fetcher,
            browserDetection: BrowserDetection(cacheTTL: 0),
            settings: settings)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: fetcher.loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: .system)
        defer { controller.releaseStatusItemsForTesting() }

        let tokenUsage = UsageMenuCardView.Model.TokenUsageSection(
            sessionLine: "Today: $1.00",
            monthLine: "Last 30 days: $9.00",
            hintLine: "Costs are estimated from local usage.",
            errorLine: nil,
            errorCopyText: nil)
        let submenu = NSMenu()

        let item = controller.makeCostMenuCardItem(
            model: self.makeModel(tokenUsage: tokenUsage),
            submenu: submenu,
            width: StatusItemController.menuCardBaseWidth)

        #expect(item.submenu === submenu)
        #expect(item.toolTip == nil)
    }

    @Test
    func `rendered cost menu keeps long dynamic details inside fixed row width`() throws {
        let previousRendering = StatusItemController.menuCardRenderingEnabled
        StatusItemController.menuCardRenderingEnabled = true
        defer { StatusItemController.menuCardRenderingEnabled = previousRendering }

        let settings = self.makeSettings()
        let fetcher = UsageFetcher()
        let store = UsageStore(
            fetcher: fetcher,
            browserDetection: BrowserDetection(cacheTTL: 0),
            settings: settings)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: fetcher.loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: .system)
        defer { controller.releaseStatusItemsForTesting() }

        let width = StatusItemController.menuCardBaseWidth
        let tokenUsage = UsageMenuCardView.Model.TokenUsageSection(
            sessionLine: "Today: $227.42 - 267M tokens - " + String(repeating: "wide ", count: 20),
            monthLine: "Last 30 days: $52,431.09 - 77B tokens - " + String(repeating: "wide ", count: 20),
            hintLine: "Costs are estimated from local usage.",
            errorLine: nil,
            errorCopyText: nil)
        let model = self.makeModel(tokenUsage: tokenUsage)

        // No history submenu — detail lines are visible and must be clipped to the row width.
        let item = controller.makeCostMenuCardItem(
            model: model,
            submenu: nil,
            width: width)
        let view = try #require(item.view)

        #expect(view is any MenuCardMeasuring)
        #expect(abs(view.frame.width - width) <= 0.5)
        #expect(item.title == "Cost")
        #expect(item.toolTip?.contains("$52,431.09") == true)
        #expect(item.submenu == nil)
    }

    @Test
    func `cost menu title stays consistent across providers`() {
        #expect(StatusItemController.costMenuTitleForProvider(.codex) == "Cost")
        #expect(StatusItemController.costMenuTitleForProvider(.claude) == "Cost")
        #expect(StatusItemController.costMenuTitleForProvider(.mistral) == "Cost")
    }

    private func makeSettings() -> SettingsStore {
        let suite = "StatusMenuCostMenuCardTests-\(UUID().uuidString)"
        let defaults = UserDefaults(suiteName: suite)!
        defaults.removePersistentDomain(forName: suite)
        return SettingsStore(
            userDefaults: defaults,
            configStore: testConfigStore(suiteName: suite),
            zaiTokenStore: NoopZaiTokenStore(),
            syntheticTokenStore: NoopSyntheticTokenStore())
    }

    private func makeModel(
        tokenUsage: UsageMenuCardView.Model.TokenUsageSection) -> UsageMenuCardView.Model
    {
        UsageMenuCardView.Model(
            provider: .codex,
            providerName: "Codex",
            email: "user@example.com",
            subtitleText: "Updated now",
            subtitleStyle: .info,
            planText: "Pro",
            metrics: [],
            usageNotes: [],
            openAIAPIUsage: nil,
            inlineUsageDashboard: nil,
            creditsText: nil,
            creditsRemaining: nil,
            creditsProgressPercent: nil,
            creditsScaleText: nil,
            creditsHintText: nil,
            creditsHintCopyText: nil,
            providerCost: nil,
            tokenUsage: tokenUsage,
            placeholder: nil,
            progressColor: .blue)
    }
}
