import AppKit
import CodexBarCore
import SwiftUI

@MainActor
struct DebugPane: View {
    @Bindable var settings: SettingsStore
    @Bindable var store: UsageStore
    @AppStorage("debugFileLoggingEnabled") private var debugFileLoggingEnabled = false
    @AppStorage(ProviderPluginRuntime.javaScriptCoreRollbackDefaultsKey)
    private var useJavaScriptCorePluginEngine = false
    // Provider-specific by design: debug probe, fetch, and error pickers historically start on Codex.
    @State private var currentLogProvider: UsageProvider = .codex
    @State private var currentFetchProvider: UsageProvider = .codex
    @State private var isLoadingLog = false
    @State private var logText: String = ""
    @State private var isClearingCostCache = false
    @State private var costCacheStatus: String?
    @State private var cookieCacheStatus: String?
    #if DEBUG
    @State private var currentErrorProvider: UsageProvider = .codex
    @State private var simulatedErrorText: String = """
    Simulated error for testing layout.
    Second line.
    Third line.
    Fourth line.
    """
    #endif

    var body: some View {
        ScrollView(.vertical, showsIndicators: true) {
            VStack(alignment: .leading, spacing: 20) {
                SettingsSection(title: L("section_logging")) {
                    PreferenceToggleRow(
                        title: L("enable_file_logging"),
                        subtitle: String(format: L("enable_file_logging_subtitle"), self.fileLogPath),
                        binding: self.$debugFileLoggingEnabled)
                        .onChange(of: self.debugFileLoggingEnabled) { _, newValue in
                            if self.settings.debugFileLoggingEnabled != newValue {
                                self.settings.debugFileLoggingEnabled = newValue
                            }
                        }

                    HStack(alignment: .center, spacing: 12) {
                        VStack(alignment: .leading, spacing: 4) {
                            Text(L("verbosity_title"))
                                .font(.body)
                            Text(L("verbosity_subtitle"))
                                .font(.footnote)
                                .foregroundStyle(.tertiary)
                        }
                        Spacer()
                        Picker(L("Verbosity"), selection: self.$settings.debugLogLevel) {
                            ForEach(CodexBarLog.Level.allCases) { level in
                                Text(level.displayName).tag(level)
                            }
                        }
                        .labelsHidden()
                        .pickerStyle(.menu)
                        .frame(maxWidth: 160)
                    }

                    Button {
                        NSWorkspace.shared.open(CodexBarLog.fileLogURL)
                    } label: {
                        Label(L("open_log_file"), systemImage: "doc.text.magnifyingglass")
                    }
                    .controlSize(.small)
                }

                SettingsSection {
                    PreferenceToggleRow(
                        title: L("force_animation_next_refresh"),
                        subtitle: L("force_animation_next_refresh_subtitle"),
                        binding: self.$store.debugForceAnimation)
                }

                SettingsSection(title: L("Provider Plugins")) {
                    PreferenceToggleRow(
                        title: L("use_javascriptcore_plugin_engine"),
                        subtitle: L("use_javascriptcore_plugin_engine_subtitle"),
                        binding: self.$useJavaScriptCorePluginEngine)
                }

                SettingsSection(
                    title: L("section_loading_animations"),
                    caption: L("loading_animations_caption"))
                {
                    Picker(L("Animation pattern"), selection: self.animationPatternBinding) {
                        Text(L("animation_random_default")).tag(nil as LoadingPattern?)
                        ForEach(LoadingPattern.allCases) { pattern in
                            Text(pattern.displayName).tag(Optional(pattern))
                        }
                    }
                    .pickerStyle(.radioGroup)

                    Button(L("replay_selected_animation")) {
                        self.replaySelectedAnimation()
                    }
                    .keyboardShortcut(.defaultAction)

                    Button {
                        NotificationCenter.default.post(name: .codexbarDebugBlinkNow, object: nil)
                    } label: {
                        Label(L("blink_now"), systemImage: "eyes")
                    }
                    .controlSize(.small)
                }

                SettingsSection(
                    title: L("section_probe_logs"),
                    caption: L("probe_logs_caption"))
                {
                    Picker(L("Provider"), selection: self.$currentLogProvider) {
                        ForEach(Self.probeLogProviders, id: \.self) { provider in
                            Text(ProviderDescriptorRegistry.descriptor(for: provider).metadata.displayName)
                                .tag(provider)
                        }
                    }
                    .pickerStyle(.segmented)
                    .frame(width: 460)

                    HStack(spacing: 12) {
                        Button { self.loadLog(self.currentLogProvider) } label: {
                            Label(L("fetch_log"), systemImage: "arrow.clockwise")
                        }
                        .disabled(self.isLoadingLog)

                        Button { self.copyToPasteboard(self.logText) } label: {
                            Label(L("copy"), systemImage: "doc.on.doc")
                        }
                        .disabled(self.logText.isEmpty)

                        Button { self.saveLog(self.currentLogProvider) } label: {
                            Label(L("save_to_file"), systemImage: "externaldrive.badge.plus")
                        }
                        .disabled(self.isLoadingLog && self.logText.isEmpty)

                        // Provider-specific by design: only Claude exposes the raw parser dump diagnostic.
                        if self.currentLogProvider == .claude {
                            Button { self.loadClaudeDump() } label: {
                                Label(L("load_parse_dump"), systemImage: "doc.text.magnifyingglass")
                            }
                            .disabled(self.isLoadingLog)
                        }
                    }

                    Button {
                        self.settings.rerunProviderDetection()
                        self.loadLog(self.currentLogProvider)
                    } label: {
                        Label(L("rerun_provider_autodetect"), systemImage: "dot.radiowaves.left.and.right")
                    }
                    .controlSize(.small)

                    ZStack(alignment: .topLeading) {
                        ScrollView {
                            Text(self.displayedLog)
                                .font(.system(.footnote, design: .monospaced))
                                .textSelection(.enabled)
                                .frame(maxWidth: .infinity, alignment: .leading)
                                .padding(8)
                        }
                        .frame(minHeight: 160, maxHeight: 220)
                        .background(Color(NSColor.textBackgroundColor))
                        .cornerRadius(6)

                        if self.isLoadingLog {
                            ProgressView()
                                .progressViewStyle(.circular)
                                .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
                                .padding()
                        }
                    }
                }

                SettingsSection(
                    title: L("section_fetch_strategy"),
                    caption: L("fetch_strategy_caption"))
                {
                    Picker(L("Provider"), selection: self.$currentFetchProvider) {
                        ForEach(UsageProvider.allCases, id: \.self) { provider in
                            Text(provider.rawValue.capitalized).tag(provider)
                        }
                    }
                    .pickerStyle(.menu)
                    .frame(width: 240)

                    ScrollView {
                        Text(self.fetchAttemptsText(for: self.currentFetchProvider))
                            .font(.system(.footnote, design: .monospaced))
                            .textSelection(.enabled)
                            .frame(maxWidth: .infinity, alignment: .leading)
                            .padding(8)
                    }
                    .frame(minHeight: 120, maxHeight: 220)
                    .background(Color(NSColor.textBackgroundColor))
                    .cornerRadius(6)
                }

                if !self.settings.debugDisableKeychainAccess {
                    SettingsSection(
                        title: L("section_openai_cookies"),
                        caption: L("openai_cookies_caption"))
                    {
                        HStack(spacing: 12) {
                            Button {
                                self.copyToPasteboard(self.store.openAIDashboardCookieImportDebugLog ?? "")
                            } label: {
                                Label(L("copy"), systemImage: "doc.on.doc")
                            }
                            .disabled((self.store.openAIDashboardCookieImportDebugLog ?? "").isEmpty)
                        }

                        ScrollView {
                            Text(
                                self.store.openAIDashboardCookieImportDebugLog?.isEmpty == false
                                    ? (self.store.openAIDashboardCookieImportDebugLog ?? "")
                                    : L("no_log_yet"))
                                .font(.system(.footnote, design: .monospaced))
                                .textSelection(.enabled)
                                .frame(maxWidth: .infinity, alignment: .leading)
                                .padding(8)
                        }
                        .frame(minHeight: 120, maxHeight: 180)
                        .background(Color(NSColor.textBackgroundColor))
                        .cornerRadius(6)
                    }
                }

                SettingsSection(
                    title: L("section_caches"),
                    caption: L("caches_caption"))
                {
                    // Provider-specific by design: cache activity currently tracks the Codex/Claude scanners.
                    let isTokenRefreshActive = self.store.isTokenRefreshInFlight(for: .codex)
                        || self.store.isTokenRefreshInFlight(for: .claude)

                    HStack(spacing: 12) {
                        Button {
                            Task { await self.clearCostCache() }
                        } label: {
                            Label(L("clear_cost_cache"), systemImage: "trash")
                        }
                        .disabled(self.isClearingCostCache || isTokenRefreshActive)

                        if let status = self.costCacheStatus {
                            Text(status)
                                .font(.footnote)
                                .foregroundStyle(.tertiary)
                        }
                    }

                    HStack(spacing: 12) {
                        Button {
                            self.clearCookieCache()
                        } label: {
                            Label(L("clear_cookie_cache"), systemImage: "trash")
                        }

                        if let status = self.cookieCacheStatus {
                            Text(status)
                                .font(.footnote)
                                .foregroundStyle(.tertiary)
                        }
                    }
                }

                SettingsSection(
                    title: L("section_notifications"),
                    caption: L("notifications_caption"))
                {
                    Picker(L("Provider"), selection: self.$currentLogProvider) {
                        ForEach(Self.notificationSimulationProviders, id: \.self) { provider in
                            Text(ProviderDescriptorRegistry.descriptor(for: provider).metadata.displayName)
                                .tag(provider)
                        }
                    }
                    .pickerStyle(.segmented)
                    .frame(width: 240)

                    HStack(spacing: 12) {
                        Button {
                            self.postSessionNotification(.depleted, provider: self.currentLogProvider)
                        } label: {
                            Label(L("post_depleted"), systemImage: "bell.badge")
                        }
                        .controlSize(.small)

                        Button {
                            self.postSessionNotification(.restored, provider: self.currentLogProvider)
                        } label: {
                            Label(L("post_restored"), systemImage: "bell")
                        }
                        .controlSize(.small)
                    }
                }

                SettingsSection(
                    title: L("section_cli_sessions"),
                    caption: L("cli_sessions_caption"))
                {
                    PreferenceToggleRow(
                        title: L("keep_cli_sessions_alive"),
                        subtitle: L("keep_cli_sessions_alive_subtitle"),
                        binding: self.$settings.debugKeepCLISessionsAlive)

                    Button {
                        Task {
                            await CLIProbeSessionResetter.resetAll()
                        }
                    } label: {
                        Label(L("reset_cli_sessions"), systemImage: "arrow.counterclockwise")
                    }
                    .controlSize(.small)
                }

                #if DEBUG
                SettingsSection(
                    title: L("section_error_simulation"),
                    caption: L("error_simulation_caption"))
                {
                    Picker(L("Provider"), selection: self.$currentErrorProvider) {
                        ForEach(Self.errorSimulationProviders, id: \.self) { provider in
                            Text(ProviderDescriptorRegistry.descriptor(for: provider).metadata.displayName)
                                .tag(provider)
                        }
                    }
                    .pickerStyle(.segmented)
                    .frame(width: 360)

                    TextField(L("Simulated error text"), text: self.$simulatedErrorText, axis: .vertical)
                        .lineLimit(4)

                    HStack(spacing: 12) {
                        Button {
                            self.store._setErrorForTesting(
                                self.simulatedErrorText,
                                provider: self.currentErrorProvider)
                        } label: {
                            Label(L("set_menu_error"), systemImage: "exclamationmark.triangle")
                        }
                        .controlSize(.small)

                        Button {
                            self.store._setErrorForTesting(nil, provider: self.currentErrorProvider)
                        } label: {
                            Label(L("clear_menu_error"), systemImage: "xmark.circle")
                        }
                        .controlSize(.small)
                    }

                    // Provider-specific by design: only Codex/Claude expose scanner-owned token errors here.
                    let supportsTokenError = self.currentErrorProvider == .codex || self.currentErrorProvider == .claude
                    HStack(spacing: 12) {
                        Button {
                            self.store._setTokenErrorForTesting(
                                self.simulatedErrorText,
                                provider: self.currentErrorProvider)
                        } label: {
                            Label(L("set_cost_error"), systemImage: "banknote")
                        }
                        .controlSize(.small)
                        .disabled(!supportsTokenError)

                        Button {
                            self.store._setTokenErrorForTesting(nil, provider: self.currentErrorProvider)
                        } label: {
                            Label(L("clear_cost_error"), systemImage: "xmark.circle")
                        }
                        .controlSize(.small)
                        .disabled(!supportsTokenError)
                    }
                }
                #endif

                SettingsSection(
                    title: L("section_cli_paths"),
                    caption: L("cli_paths_caption"))
                {
                    // Provider-specific by design: PathDebugInfo only records the Codex and Claude binaries.
                    self.binaryRow(title: L("codex_binary"), value: self.store.pathDebugInfo.codexBinary)
                    self.binaryRow(title: L("claude_binary"), value: self.store.pathDebugInfo.claudeBinary)

                    VStack(alignment: .leading, spacing: 6) {
                        Text(L("effective_path"))
                            .font(.callout.weight(.semibold))
                        ScrollView {
                            Text(
                                self.store.pathDebugInfo.effectivePATH.isEmpty
                                    ? L("unavailable")
                                    : self.store.pathDebugInfo.effectivePATH)
                                .font(.system(.footnote, design: .monospaced))
                                .textSelection(.enabled)
                                .frame(maxWidth: .infinity, alignment: .leading)
                                .padding(6)
                        }
                        .frame(minHeight: 60, maxHeight: 110)
                        .background(Color(NSColor.textBackgroundColor))
                        .cornerRadius(6)
                    }

                    if let loginPATH = self.store.pathDebugInfo.loginShellPATH {
                        VStack(alignment: .leading, spacing: 6) {
                            Text(L("login_shell_path"))
                                .font(.callout.weight(.semibold))
                            ScrollView {
                                Text(loginPATH)
                                    .font(.system(.footnote, design: .monospaced))
                                    .textSelection(.enabled)
                                    .frame(maxWidth: .infinity, alignment: .leading)
                                    .padding(6)
                            }
                            .frame(minHeight: 60, maxHeight: 110)
                            .background(Color(NSColor.textBackgroundColor))
                            .cornerRadius(6)
                        }
                    }
                }
            }
            .frame(maxWidth: .infinity, alignment: .leading)
            .padding(.horizontal, 20)
            .padding(.vertical, 12)
        }
    }

    private var fileLogPath: String {
        CodexBarLog.fileLogURL.path
    }

    private static let probeLogProviders = Self.providers { $0.probeLogOrder }
    private static let notificationSimulationProviders = Self.providers { $0.notificationSimulationOrder }
    #if DEBUG
    private static let errorSimulationProviders = Self.providers { $0.errorSimulationOrder }
    #endif

    private static func providers(order: (ProviderDebugPaneCapabilities) -> Int?) -> [UsageProvider] {
        ProviderDescriptorRegistry.all.compactMap { descriptor -> (UsageProvider, Int)? in
            guard let rank = order(descriptor.metadata.debugPane) else { return nil }
            return (descriptor.id, rank)
        }
        .sorted { $0.1 < $1.1 }
        .map(\.0)
    }

    private var animationPatternBinding: Binding<LoadingPattern?> {
        Binding(
            get: { self.settings.debugLoadingPattern },
            set: { self.settings.debugLoadingPattern = $0 })
    }

    private func replaySelectedAnimation() {
        var userInfo: [AnyHashable: Any] = [:]
        if let pattern = self.settings.debugLoadingPattern {
            userInfo["pattern"] = pattern.rawValue
        }
        NotificationCenter.default.post(
            name: .codexbarDebugReplayAllAnimations,
            object: nil,
            userInfo: userInfo.isEmpty ? nil : userInfo)
        self.store.replayLoadingAnimation(duration: 4)
    }

    private var displayedLog: String {
        if self.logText.isEmpty {
            return self.isLoadingLog ? L("loading") : L("no_log_yet_fetch")
        }
        return self.logText
    }

    private func loadLog(_ provider: UsageProvider) {
        self.isLoadingLog = true
        Task {
            let text = await ProviderInteractionContext.$current.withValue(.userInitiated) {
                await ProviderRefreshContext.$current.withValue(.regular) {
                    await self.store.debugLog(for: provider)
                }
            }
            await MainActor.run {
                self.logText = text
                self.isLoadingLog = false
            }
        }
    }

    private func saveLog(_ provider: UsageProvider) {
        Task {
            if self.logText.isEmpty {
                self.isLoadingLog = true
                let text = await ProviderInteractionContext.$current.withValue(.userInitiated) {
                    await ProviderRefreshContext.$current.withValue(.regular) {
                        await self.store.debugLog(for: provider)
                    }
                }
                await MainActor.run { self.logText = text }
                self.isLoadingLog = false
            }
            _ = await ProviderInteractionContext.$current.withValue(.userInitiated) {
                await ProviderRefreshContext.$current.withValue(.regular) {
                    await self.store.dumpLog(toFileFor: provider)
                }
            }
        }
    }

    private func copyToPasteboard(_ text: String) {
        let pb = NSPasteboard.general
        pb.clearContents()
        pb.setString(text, forType: .string)
    }

    private func binaryRow(title: String, value: String?) -> some View {
        VStack(alignment: .leading, spacing: 6) {
            Text(title)
                .font(.callout.weight(.semibold))
            Text(value ?? L("not_found"))
                .font(.system(.footnote, design: .monospaced))
                .foregroundStyle(value == nil ? .secondary : .primary)
        }
    }

    private func loadClaudeDump() {
        self.isLoadingLog = true
        Task {
            let text = await self.store.debugClaudeDump()
            await MainActor.run {
                self.logText = text
                self.isLoadingLog = false
            }
        }
    }

    private func postSessionNotification(_ transition: SessionQuotaTransition, provider: UsageProvider) {
        SessionQuotaNotifier().post(transition: transition, provider: provider, badge: 1)
    }

    private func clearCostCache() async {
        guard !self.isClearingCostCache else { return }
        self.isClearingCostCache = true
        self.costCacheStatus = nil
        defer { self.isClearingCostCache = false }

        if let error = await self.store.clearCostUsageCache() {
            self.costCacheStatus = "Failed: \(error)"
            return
        }

        self.costCacheStatus = L("cleared")
    }

    private func clearCookieCache() {
        let summary = CookieHeaderCache.clearAllDetailed()
        if summary.failedCount > 0 {
            self.cookieCacheStatus = "Cookie cache cleanup failed for \(summary.failedCount) "
                + "operation\(summary.failedCount == 1 ? "" : "s")."
        } else if summary.clearedCount > 0 {
            self.cookieCacheStatus = "Cleared \(summary.clearedCount) "
                + "provider\(summary.clearedCount == 1 ? "" : "s")."
        } else {
            self.cookieCacheStatus = "No cached cookies found."
        }
    }

    private func fetchAttemptsText(for provider: UsageProvider) -> String {
        let attempts = self.store.fetchAttempts(for: provider)
        guard !attempts.isEmpty else { return L("no_fetch_attempts") }
        return attempts.map { attempt in
            let kind = Self.fetchKindLabel(attempt.kind)
            var line = "\(attempt.strategyID) (\(kind))"
            line += attempt.wasAvailable ? " available" : " unavailable"
            if let error = attempt.errorDescription, !error.isEmpty {
                line += " error=\(error)"
            }
            return line
        }.joined(separator: "\n")
    }

    private static func fetchKindLabel(_ kind: ProviderFetchKind) -> String {
        switch kind {
        case .cli: "cli"
        case .web: "web"
        case .oauth: "oauth"
        case .apiToken: "api"
        case .localProbe: "local"
        case .webDashboard: "web"
        }
    }
}
