import CodexBarCore
import Foundation

/// App-side provider implementation.
///
/// Rules:
/// - Provider implementations return *data/behavior descriptors*; the app owns UI.
/// - Do not mix identity fields across providers (email/org/plan/loginMethod stays siloed).
protocol ProviderImplementation: Sendable {
    var id: UsageProvider { get }
    var supportsLoginFlow: Bool { get }

    @MainActor
    func presentation(context: ProviderPresentationContext) -> ProviderPresentation

    @MainActor
    func observeSettings(_ settings: SettingsStore)

    @MainActor
    func isAvailable(context: ProviderAvailabilityContext) -> Bool

    @MainActor
    func defaultSourceLabel(context: ProviderSourceLabelContext) -> String?

    @MainActor
    func decorateSourceLabel(context: ProviderSourceLabelContext, baseLabel: String) -> String

    @MainActor
    func sourceMode(context: ProviderSourceModeContext) -> ProviderSourceMode

    func detectVersion(context: ProviderVersionContext) async -> String?

    func makeRuntime() -> (any ProviderRuntime)?

    /// Optional provider-specific settings toggles to render in the Providers pane.
    ///
    /// Important: Providers must not return custom SwiftUI views here. Only shared toggle/action descriptors.
    @MainActor
    func settingsToggles(context: ProviderSettingsContext) -> [ProviderSettingsToggleDescriptor]

    /// Optional provider-specific settings fields to render in the Providers pane.
    @MainActor
    func settingsFields(context: ProviderSettingsContext) -> [ProviderSettingsFieldDescriptor]

    /// Optional provider-specific settings action rows to render in the Providers pane.
    @MainActor
    func settingsActions(context: ProviderSettingsContext) -> [ProviderSettingsActionsDescriptor]

    /// Optional provider-specific settings pickers to render in the Providers pane.
    @MainActor
    func settingsPickers(context: ProviderSettingsContext) -> [ProviderSettingsPickerDescriptor]

    /// Optional provider-specific organizations selection rendered in the Providers pane.
    @MainActor
    func settingsOrganizations(context: ProviderSettingsContext) -> ProviderSettingsOrganizationsDescriptor?

    /// Optional visibility gate for token account settings.
    @MainActor
    func tokenAccountsVisibility(context: ProviderSettingsContext, support: TokenAccountSupport) -> Bool

    /// Optional provider-specific settings snapshot contribution.
    @MainActor
    func settingsSnapshot(context: ProviderSettingsSnapshotContext) -> ProviderSettingsSnapshotContribution?

    /// Optional primary action for the shared token-account editor.
    @MainActor
    func runTokenAccountPrimaryAction(context: ProviderSettingsContext) async

    /// Optional hook to update provider settings when token accounts change.
    @MainActor
    func applyTokenAccountCookieSource(settings: SettingsStore)

    /// Optional provider-specific menu entries for the usage section.
    @MainActor
    func appendUsageMenuEntries(context: ProviderMenuUsageContext, entries: inout [ProviderMenuEntry])

    /// Optional provider-specific menu entries for the actions section.
    @MainActor
    func appendActionMenuEntries(context: ProviderMenuActionContext, entries: inout [ProviderMenuEntry])

    /// Optional override for the login/switch account menu action.
    @MainActor
    func loginMenuAction(context: ProviderMenuLoginContext) -> (label: String, action: MenuDescriptor.MenuAction)?

    /// Optional provider-specific login flow. Returns whether to refresh after completion.
    @MainActor
    func runLoginFlow(context: ProviderLoginContext) async -> Bool
}

extension ProviderImplementation {
    var supportsLoginFlow: Bool {
        false
    }

    @MainActor
    func presentation(context _: ProviderPresentationContext) -> ProviderPresentation {
        ProviderPresentation(detailLine: ProviderPresentation.standardDetailLine)
    }

    @MainActor
    func observeSettings(_ settings: SettingsStore) {
        _ = settings
    }

    @MainActor
    func isAvailable(context _: ProviderAvailabilityContext) -> Bool {
        true
    }

    @MainActor
    func defaultSourceLabel(context _: ProviderSourceLabelContext) -> String? {
        nil
    }

    @MainActor
    func decorateSourceLabel(context _: ProviderSourceLabelContext, baseLabel: String) -> String {
        baseLabel
    }

    @MainActor
    func sourceMode(context _: ProviderSourceModeContext) -> ProviderSourceMode {
        .auto
    }

    func detectVersion(context: ProviderVersionContext) async -> String? {
        let detector = ProviderDescriptorRegistry.descriptor(for: self.id).cli.versionDetector
        return detector?(context.browserDetection)
    }

    func makeRuntime() -> (any ProviderRuntime)? {
        nil
    }

    @MainActor
    func settingsToggles(context _: ProviderSettingsContext) -> [ProviderSettingsToggleDescriptor] {
        []
    }

    @MainActor
    func settingsFields(context _: ProviderSettingsContext) -> [ProviderSettingsFieldDescriptor] {
        []
    }

    @MainActor
    func settingsActions(context _: ProviderSettingsContext) -> [ProviderSettingsActionsDescriptor] {
        []
    }

    @MainActor
    func settingsPickers(context _: ProviderSettingsContext) -> [ProviderSettingsPickerDescriptor] {
        []
    }

    @MainActor
    func settingsOrganizations(context _: ProviderSettingsContext) -> ProviderSettingsOrganizationsDescriptor? {
        nil
    }

    @MainActor
    func tokenAccountsVisibility(context: ProviderSettingsContext, support: TokenAccountSupport) -> Bool {
        guard support.requiresManualCookieSource else { return true }
        return !context.settings.tokenAccounts(for: context.provider).isEmpty
    }

    @MainActor
    func settingsSnapshot(context _: ProviderSettingsSnapshotContext) -> ProviderSettingsSnapshotContribution? {
        ProviderDescriptorRegistry.descriptor(for: self.id).settingsSection.defaultContribution
    }

    @MainActor
    func runTokenAccountPrimaryAction(context _: ProviderSettingsContext) async {}

    @MainActor
    func applyTokenAccountCookieSource(settings _: SettingsStore) {}

    @MainActor
    func appendUsageMenuEntries(context _: ProviderMenuUsageContext, entries _: inout [ProviderMenuEntry]) {}

    @MainActor
    func appendActionMenuEntries(context _: ProviderMenuActionContext, entries _: inout [ProviderMenuEntry]) {}

    @MainActor
    func loginMenuAction(context _: ProviderMenuLoginContext)
        -> (label: String, action: MenuDescriptor.MenuAction)?
    {
        nil
    }

    @MainActor
    func runLoginFlow(context _: ProviderLoginContext) async -> Bool {
        false
    }
}

struct ProviderLoginContext {
    unowned let controller: StatusItemController
}
