import AppKit
import CodexBarCore
import SwiftUI

@MainActor
struct MenuContent: View {
    @Bindable var store: UsageStore
    @Bindable var settings: SettingsStore
    let account: AccountInfo
    let updater: UpdaterProviding
    let provider: UsageProvider?
    let actions: MenuActions

    var body: some View {
        let descriptor = MenuDescriptor.build(
            provider: self.provider,
            store: self.store,
            settings: self.settings,
            account: self.account,
            updateReady: self.updater.updateStatus.isUpdateReady)

        VStack(alignment: .leading, spacing: 8) {
            ForEach(Array(descriptor.sections.enumerated()), id: \.offset) { index, section in
                VStack(alignment: .leading, spacing: 4) {
                    ForEach(Array(section.entries.enumerated()), id: \.offset) { _, entry in
                        self.row(for: entry)
                    }
                }
                if index < descriptor.sections.count - 1 {
                    Divider()
                }
            }
        }
        .padding(.horizontal, 10)
        .padding(.vertical, 6)
        .frame(minWidth: 260, alignment: .leading)
    }

    @ViewBuilder
    private func row(for entry: MenuDescriptor.Entry) -> some View {
        switch entry {
        case let .text(text, style):
            switch style {
            case .headline:
                Text(text).font(.headline)
                    .accessibilityLabel(text)
            case .primary:
                Text(text)
                    .accessibilityLabel(text)
            case .secondary:
                Text(text).foregroundStyle(.secondary).font(.footnote)
                    .accessibilityLabel(text)
            }
        case let .action(title, action):
            Button {
                self.perform(action)
            } label: {
                if let icon = self.iconName(for: action) {
                    HStack(spacing: 8) {
                        Image(systemName: icon)
                            .imageScale(.medium)
                            .frame(width: 18, alignment: .center)
                        Text(title)
                    }
                    .foregroundStyle(.primary)
                    .accessibilityElement(children: .combine)
                    .accessibilityLabel(title)
                } else {
                    Text(title)
                        .accessibilityLabel(title)
                }
            }
            .buttonStyle(.plain)
        case let .unavailable(title, tooltip):
            Text(title)
                .foregroundStyle(.secondary)
                .help(tooltip ?? "")
        case let .submenu(title, systemImageName, submenuItems):
            VStack(alignment: .leading, spacing: 4) {
                HStack(spacing: 8) {
                    if let systemImageName {
                        Image(systemName: systemImageName)
                    }
                    Text(title).font(.headline)
                }
                .accessibilityElement(children: .combine)
                .accessibilityLabel(title)
                ForEach(Array(submenuItems.enumerated()), id: \.offset) { _, submenuItem in
                    HStack(spacing: 8) {
                        if submenuItem.isChecked {
                            Image(systemName: "checkmark")
                                .imageScale(.small)
                                .frame(width: 18, alignment: .center)
                        } else {
                            Spacer().frame(width: 18)
                        }
                        Text(submenuItem.title)
                            .foregroundStyle(submenuItem.isEnabled ? .primary : .secondary)
                    }
                    .accessibilityElement(children: .combine)
                    .accessibilityLabel(submenuItem.title)
                }
            }
        case .divider:
            Divider()
        }
    }

    private func iconName(for action: MenuDescriptor.MenuAction) -> String? {
        action.systemImageName
    }

    private func perform(_ action: MenuDescriptor.MenuAction) {
        switch action {
        case .refresh:
            self.actions.refresh()
        case .refreshAugmentSession:
            self.actions.refreshAugmentSession()
        case .installUpdate:
            self.actions.installUpdate()
        case .dashboard:
            self.actions.openDashboard()
        case .statusPage:
            self.actions.openStatusPage()
        case .changelog:
            self.actions.openChangelog()
        case .addCodexAccount:
            self.actions.addCodexAccount()
        case .requestCodexSystemPromotion:
            return
        case let .addProviderAccount(provider):
            self.actions.switchAccount(provider)
        case let .switchAccount(provider):
            self.actions.switchAccount(provider)
        case let .openTerminal(command):
            self.actions.openTerminal(command)
        case let .loginToProvider(url):
            if let urlObj = URL(string: url) {
                NSWorkspace.shared.open(urlObj)
            }
        case .settings:
            self.actions.openSettings()
        case .about:
            self.actions.openAbout()
        case .quit:
            self.actions.quit()
        case let .copyError(message):
            self.actions.copyError(message)
        case .focusAgentSession:
            return
        }
    }
}

struct MenuActions {
    let installUpdate: () -> Void
    let refresh: () -> Void
    let refreshAugmentSession: () -> Void
    let openDashboard: () -> Void
    let openStatusPage: () -> Void
    let openChangelog: () -> Void
    let addCodexAccount: () -> Void
    let switchAccount: (UsageProvider) -> Void
    let openTerminal: (String) -> Void
    let openSettings: () -> Void
    let openAbout: () -> Void
    let quit: () -> Void
    let copyError: (String) -> Void
}

struct PersistentRefreshRowMetrics: Equatable {
    static let defaults = Self(
        rowHeight: 24,
        selectionHorizontalInset: 5,
        selectionVerticalInset: 0,
        selectionCornerRadius: 7,
        // Align the custom row's image/title frames with native NSMenuItem columns.
        leadingPadding: 15,
        trailingPadding: 8,
        iconWidth: 16,
        iconSymbolPointSize: 16,
        iconSymbolWeight: .regular,
        iconTitleSpacing: 4.5,
        shortcutFontSize: 13,
        shortcutXOffset: -9.5,
        shortcutYOffset: 0)

    let rowHeight: CGFloat
    let selectionHorizontalInset: CGFloat
    let selectionVerticalInset: CGFloat
    let selectionCornerRadius: CGFloat
    let leadingPadding: CGFloat
    let trailingPadding: CGFloat
    let iconWidth: CGFloat
    let iconSymbolPointSize: CGFloat
    let iconSymbolWeight: NSFont.Weight
    let iconTitleSpacing: CGFloat
    let shortcutFontSize: CGFloat
    let shortcutXOffset: CGFloat
    let shortcutYOffset: CGFloat
}

@MainActor
struct StatusIconView: View {
    @Bindable var store: UsageStore
    let provider: UsageProvider

    var body: some View {
        Image(nsImage: self.icon)
            .renderingMode(.template)
            .interpolation(.none)
            .accessibilityLabel(self.accessibilityLabel)
            .accessibilityValue(self.accessibilityValue)
    }

    private var accessibilityLabel: String {
        let descriptor = ProviderDescriptorRegistry.descriptor(for: self.provider)
        return descriptor.metadata.displayName
    }

    private var accessibilityValue: String {
        let snapshot = self.store.snapshot(for: self.provider.instanceID)
        guard let snap = snapshot else {
            return L("No data")
        }
        let remaining = IconRemainingResolver.resolvedRemaining(
            snapshot: snap,
            style: self.store.style(for: self.provider))
        let primary = remaining.primary
        let percent = primary.map(Self.accessibilityPercentRemaining) ?? L("Unknown")
        let stale = self.store.isStale(provider: self.provider)
        return stale ? "\(percent), \(L("stale data"))" : percent
    }

    static func accessibilityPercentRemaining(_ remaining: Double) -> String {
        String(format: L("%d percent remaining"), Int(remaining.rounded()))
    }

    private var icon: NSImage {
        let now = Date()
        let snapshot = self.store.snapshot(for: self.provider.instanceID)
        let remaining = snapshot.map {
            IconRemainingResolver.resolvedRemaining(
                snapshot: $0,
                style: self.store.style(for: self.provider),
                now: now)
        }
        let creditsProjection = self.store.codexConsumerProjectionIfNeeded(
            for: self.provider,
            surface: .menuBar,
            snapshotOverride: snapshot,
            now: now)
        let creditsRemaining = creditsProjection?.menuBarFallback == .creditsBalance
            ? self.store.codexMenuBarCreditsRemaining(
                snapshotOverride: snapshot,
                now: now)
            : nil
        return IconRenderer.makeIcon(
            primaryRemaining: remaining?.primary,
            weeklyRemaining: remaining?.secondary,
            creditsRemaining: creditsRemaining,
            stale: self.store.isStale(provider: self.provider),
            style: self.store.style(for: self.provider),
            statusIndicator: self.store.statusIndicator(for: self.provider),
            hideCritters: self.store.settings.menuBarHidesCritters)
    }
}
