import AppKit
import Observation
import OpenClawDiscovery
import OpenClawIPC
import OpenClawKit
import SwiftUI

struct GeneralSettings: View {
    enum Page {
        case general
        case connection
    }

    private static let remoteFieldWidth: CGFloat = 320
    private static let remoteSecretFieldWidth: CGFloat = 300

    @Bindable var state: AppState
    @AppStorage(cameraEnabledKey) private var cameraEnabled: Bool = false
    let page: Page
    let isActive: Bool
    private let healthStore = HealthStore.shared
    private let gatewayManager = GatewayProcessManager.shared
    @State private var gatewayDiscovery = GatewayDiscoveryModel(
        localDisplayName: InstanceIdentity.displayName)
    @State private var gatewayStatus: GatewayEnvironmentStatus = .checking
    @State private var remoteStatus: RemoteStatus = .idle
    @State private var showRemoteAdvanced = false
    private let isPreview = ProcessInfo.processInfo.isPreview
    private var isNixMode: Bool {
        ProcessInfo.processInfo.isNixMode
    }

    init(state: AppState, page: Page = .general, isActive: Bool = true) {
        self.state = state
        self.page = page
        self.isActive = isActive
    }

    var body: some View {
        ScrollView(.vertical) {
            VStack(alignment: .leading, spacing: 20) {
                switch self.page {
                case .general:
                    self.generalPage
                case .connection:
                    self.connectionPage
                }
            }
            .settingsDetailContent()
        }
        .onAppear {
            self.updateActiveWork(active: self.isActive)
        }
        .onChange(of: self.isActive) { _, active in
            self.updateActiveWork(active: active)
        }
        .onChange(of: self.state.canvasEnabled) { _, enabled in
            if !enabled {
                CanvasManager.shared.hideAll()
            }
        }
        .onDisappear { self.gatewayDiscovery.stop() }
    }

    private var generalPage: some View {
        VStack(alignment: .leading, spacing: 20) {
            SettingsPageHeader(
                title: "General",
                subtitle: "Everyday OpenClaw app behavior.")

            self.openClawStatusPanel

            SettingsCardGroup("App") {
                SettingsCardToggleRow(
                    title: "Launch at login",
                    subtitle: "Automatically start OpenClaw after you sign in.",
                    binding: self.$state.launchAtLogin)

                SettingsCardToggleRow(
                    title: "Show Dock icon",
                    subtitle: "Keep OpenClaw visible in the Dock. When off, windows still show the Dock icon while open.",
                    binding: self.$state.showDockIcon)

                SettingsCardToggleRow(
                    title: "Play menu bar icon animations",
                    subtitle: "Enable idle blinks and wiggles on the status icon.",
                    binding: self.$state.iconAnimationsEnabled,
                    showsDivider: false)
            }

            SettingsCardGroup("Capabilities") {
                SettingsCardToggleRow(
                    title: "Allow Canvas",
                    subtitle: "Allow the agent to show and control the Canvas panel.",
                    binding: self.$state.canvasEnabled)

                SettingsCardToggleRow(
                    title: "Allow Camera",
                    subtitle: "Allow the agent to capture a photo or short video via the built-in camera.",
                    binding: self.$cameraEnabled)

                SettingsCardToggleRow(
                    title: "Enable Peekaboo Bridge",
                    subtitle: "Allow signed tools (e.g. `peekaboo`) to drive UI automation via PeekabooBridge.",
                    binding: self.$state.peekabooBridgeEnabled,
                    showsDivider: false)
            }

            SettingsCardGroup("Developer") {
                SettingsCardToggleRow(
                    title: "Enable debug tools",
                    subtitle: "Show the Debug page with development utilities.",
                    binding: self.$state.debugPaneEnabled,
                    showsDivider: false)
            }

            HStack(alignment: .center, spacing: 12) {
                VStack(alignment: .leading, spacing: 3) {
                    Text("App session")
                        .font(.callout.weight(.medium))
                    Text("Quit only when you want to stop the menu bar app completely.")
                        .font(.footnote)
                        .foregroundStyle(.secondary)
                }
                Spacer(minLength: 18)
                Button("Quit") { NSApp.terminate(nil) }
                    .buttonStyle(.bordered)
                    .controlSize(.small)
            }
            .padding(.top, 2)
        }
    }

    private var openClawStatusPanel: some View {
        HStack(alignment: .center, spacing: 14) {
            ZStack {
                Circle()
                    .fill(self.state.isPaused ? Color.orange.opacity(0.18) : Color.green.opacity(0.18))
                Image(systemName: self.state.isPaused ? "pause.fill" : "checkmark")
                    .font(.system(size: 16, weight: .bold))
                    .foregroundStyle(self.state.isPaused ? .orange : .green)
            }
            .frame(width: 42, height: 42)

            VStack(alignment: .leading, spacing: 4) {
                Text(self.state.isPaused ? "OpenClaw paused" : "OpenClaw active")
                    .font(.headline)
                Text(self.generalStatusSubtitle)
                    .font(.footnote)
                    .foregroundStyle(.secondary)
                    .fixedSize(horizontal: false, vertical: true)
            }

            Spacer(minLength: 20)

            Toggle("OpenClaw active", isOn: self.activeBinding)
                .labelsHidden()
                .toggleStyle(.switch)
        }
        .padding(.horizontal, 16)
        .padding(.vertical, 14)
        .background(.quaternary.opacity(0.45), in: RoundedRectangle(cornerRadius: 12, style: .continuous))
        .overlay {
            RoundedRectangle(cornerRadius: 12, style: .continuous)
                .strokeBorder(.white.opacity(0.06))
        }
    }

    private var generalStatusSubtitle: String {
        if self.state.isPaused {
            return "Gateway work is paused; incoming messages will wait."
        }
        switch self.state.connectionMode {
        case .local:
            return "Processing messages through the local Gateway on this Mac."
        case .remote:
            return "Connected to a remote Gateway configuration."
        case .unconfigured:
            return "Ready to run after you choose a Gateway connection."
        }
    }

    private var connectionPage: some View {
        VStack(alignment: .leading, spacing: 20) {
            SettingsPageHeader(
                title: "Connection",
                subtitle: "Choose where the Gateway runs and how this Mac app reaches it.")

            self.connectionStatusPanel
            self.gatewayModeGroup

            switch self.state.connectionMode {
            case .unconfigured:
                EmptyView()
            case .local:
                self.localGatewayGroup
            case .remote:
                self.remoteCard
            }
        }
    }

    private var activeBinding: Binding<Bool> {
        Binding(
            get: { !self.state.isPaused },
            set: { self.state.isPaused = !$0 })
    }

    private func updateActiveWork(active: Bool) {
        guard !self.isPreview else { return }
        if active {
            self.refreshGatewayStatus()
            if self.page == .connection {
                self.gatewayDiscovery.start()
            }
        } else {
            self.gatewayDiscovery.stop()
        }
    }

    private var connectionStatusPanel: some View {
        HStack(alignment: .center, spacing: 14) {
            ZStack {
                RoundedRectangle(cornerRadius: 16, style: .continuous)
                    .fill(self.connectionStatusTint.opacity(0.18))
                Image(systemName: self.connectionStatusIcon)
                    .font(.system(size: 18, weight: .semibold))
                    .foregroundStyle(self.connectionStatusTint)
            }
            .frame(width: 46, height: 46)

            VStack(alignment: .leading, spacing: 4) {
                Text(self.connectionStatusTitle)
                    .font(.headline)
                Text(self.connectionStatusSubtitle)
                    .font(.footnote)
                    .foregroundStyle(.secondary)
                    .fixedSize(horizontal: false, vertical: true)
            }

            Spacer(minLength: 18)

            if let ping = ControlChannel.shared.lastPingMs {
                Text("\(Int(ping)) ms")
                    .font(.caption.weight(.semibold))
                    .padding(.horizontal, 8)
                    .padding(.vertical, 4)
                    .background(.green.opacity(0.16), in: Capsule())
                    .foregroundStyle(.green)
            }
        }
        .padding(.horizontal, 16)
        .padding(.vertical, 14)
        .background(.quaternary.opacity(0.45), in: RoundedRectangle(cornerRadius: 12, style: .continuous))
        .overlay {
            RoundedRectangle(cornerRadius: 12, style: .continuous)
                .strokeBorder(.white.opacity(0.06))
        }
    }

    private var connectionStatusIcon: String {
        switch self.state.connectionMode {
        case .local: "desktopcomputer"
        case .remote: self.state.remoteTransport == .ssh ? "point.3.connected.trianglepath.dotted" : "network"
        case .unconfigured: "questionmark.circle"
        }
    }

    private var connectionStatusTint: Color {
        switch ControlChannel.shared.state {
        case .connected: .green
        case .connecting, .disconnected, .degraded: .orange
        }
    }

    private var connectionStatusTitle: String {
        switch self.state.connectionMode {
        case .local: "Local Gateway"
        case .remote: self.state.remoteTransport == .ssh ? "Remote Gateway via SSH" : "Remote Gateway direct"
        case .unconfigured: "Gateway not configured"
        }
    }

    private var connectionStatusSubtitle: String {
        switch self.state.connectionMode {
        case .local:
            return "OpenClaw starts and monitors the Gateway on this Mac."
        case .remote:
            let target = self.state.remoteTransport == .ssh ? self.state.remoteTarget : self.state.remoteUrl
            let trimmed = target.trimmingCharacters(in: .whitespacesAndNewlines)
            if trimmed.isEmpty {
                return "Enter a remote endpoint so this Mac app can attach cleanly."
            }
            return "\(self.controlStatusLine) · \(trimmed)"
        case .unconfigured:
            return "Choose local or remote before the app can attach to a Gateway."
        }
    }

    private var gatewayModeGroup: some View {
        SettingsCardGroup("Gateway") {
            SettingsCardRow(
                title: "OpenClaw runs",
                subtitle: "Pick whether this app owns a local Gateway or attaches to another host.",
                showsDivider: self.state.connectionMode == .unconfigured)
            {
                Picker("Gateway location", selection: self.$state.connectionMode) {
                    Text("Not configured").tag(AppState.ConnectionMode.unconfigured)
                    Text("Local (this Mac)").tag(AppState.ConnectionMode.local)
                    Text("Remote (another host)").tag(AppState.ConnectionMode.remote)
                }
                .pickerStyle(.menu)
                .labelsHidden()
                .frame(width: 260, alignment: .trailing)
            }

            if self.state.connectionMode == .unconfigured {
                SettingsCardRow(
                    title: "Setup needed",
                    subtitle: "Local is best for this Mac. Remote is best when the Gateway already runs on a Mac Studio or server.",
                    showsDivider: false)
                {
                    Image(systemName: "exclamationmark.triangle.fill")
                        .foregroundStyle(.orange)
                }
            }
        }
    }

    private var localGatewayGroup: some View {
        VStack(alignment: .leading, spacing: 20) {
            SettingsCardGroup("Local Gateway") {
                if !self.isNixMode {
                    self.gatewayInstallerCard
                }
                self.healthRow
                    .padding(.horizontal, 14)
                    .padding(.vertical, 11)
            }

            TailscaleIntegrationSection(
                connectionMode: self.state.connectionMode,
                isPaused: self.state.isPaused)
        }
    }

    private var remoteCard: some View {
        VStack(alignment: .leading, spacing: 20) {
            SettingsCardGroup("Remote Access") {
                self.remoteTransportRow

                if self.state.remoteTransport == .ssh {
                    self.remoteSshRow
                } else {
                    self.remoteDirectRow
                }
                self.remoteTokenRow
            }

            SettingsCardGroup("Discovery & Status") {
                self.remoteDiscoveryRow
                self.remoteStatusRow
                self.controlChannelRow
                self.remoteTipRow
            }

            if self.state.remoteTransport == .ssh {
                self.remoteAdvancedGroup
            }
        }
        .transition(.opacity)
    }

    private var remoteDiscoveryRow: some View {
        VStack(alignment: .leading, spacing: 8) {
            Text("Nearby gateways")
                .font(.callout.weight(.medium))
            GatewayDiscoveryInlineList(
                discovery: self.gatewayDiscovery,
                currentTarget: self.state.remoteTarget,
                currentUrl: self.state.remoteUrl,
                transport: self.state.remoteTransport)
            { gateway in
                self.applyDiscoveredGateway(gateway)
            }
        }
        .frame(maxWidth: .infinity, alignment: .leading)
        .padding(.horizontal, 14)
        .padding(.vertical, 11)
        .overlay(alignment: .bottom) {
            Divider()
                .padding(.leading, 14)
        }
    }

    @ViewBuilder
    private var remoteStatusRow: some View {
        if self.remoteStatus != .idle {
            SettingsCardRow(title: "Remote test") {
                self.remoteStatusView
            }
        }
    }

    private var controlChannelRow: some View {
        SettingsCardRow(title: "Control channel", subtitle: self.controlChannelSubtitle) {
            Text(self.controlStatusLine)
                .font(.caption.weight(.semibold))
                .padding(.horizontal, 8)
                .padding(.vertical, 4)
                .background(self.connectionStatusTint.opacity(0.16), in: Capsule())
                .foregroundStyle(self.connectionStatusTint)
        }
    }

    private var controlChannelSubtitle: String? {
        var parts: [String] = []
        if let ping = ControlChannel.shared.lastPingMs {
            parts.append("Ping \(Int(ping)) ms")
        }
        if let hb = HeartbeatStore.shared.lastEvent {
            let ageText = age(from: Date(timeIntervalSince1970: hb.ts / 1000))
            parts.append("Last heartbeat \(hb.status) · \(ageText)")
        }
        if let authLabel = ControlChannel.shared.authSourceLabel {
            parts.append(authLabel)
        }
        return parts.isEmpty ? nil : parts.joined(separator: "\n")
    }

    private var remoteTipRow: some View {
        SettingsCardRow(
            title: "Recommended setup",
            subtitle: self.state.remoteTransport == .ssh
                ? "Use Tailscale plus an SSH tunnel for stable private access."
                : "Use Tailscale Serve so the gateway has a valid HTTPS certificate.",
            showsDivider: false)
        {
            Image(systemName: "lightbulb.fill")
                .foregroundStyle(.yellow)
        }
    }

    private var remoteAdvancedGroup: some View {
        SettingsCardGroup("Advanced") {
            DisclosureGroup(isExpanded: self.$showRemoteAdvanced) {
                VStack(alignment: .leading, spacing: 12) {
                    self.advancedTextField(
                        "Identity file",
                        placeholder: "/Users/you/.ssh/id_ed25519",
                        text: self.$state.remoteIdentity)
                    self.advancedTextField(
                        "Project root",
                        placeholder: "/home/you/Projects/openclaw",
                        text: self.$state.remoteProjectRoot)
                    self.advancedTextField(
                        "CLI path",
                        placeholder: "/Applications/OpenClaw.app/.../openclaw",
                        text: self.$state.remoteCliPath)
                }
                .padding(.top, 10)
            } label: {
                Text("SSH command details")
                    .font(.callout.weight(.medium))
            }
            .padding(.horizontal, 14)
            .padding(.vertical, 11)
        }
    }

    private func advancedTextField(_ title: String, placeholder: String, text: Binding<String>) -> some View {
        VStack(alignment: .leading, spacing: 4) {
            Text(title)
                .font(.caption.weight(.semibold))
                .foregroundStyle(.secondary)
            TextField(placeholder, text: text)
                .textFieldStyle(.roundedBorder)
        }
    }

    private var remoteTransportRow: some View {
        SettingsCardRow(
            title: "Transport",
            subtitle: "SSH keeps the Gateway private; direct is best for HTTPS or Tailscale Serve.")
        {
            Picker("Transport", selection: self.$state.remoteTransport) {
                Text("SSH tunnel").tag(AppState.RemoteTransport.ssh)
                Text("Direct (ws/wss)").tag(AppState.RemoteTransport.direct)
            }
            .pickerStyle(.segmented)
            .frame(width: 320)
        }
    }

    private var remoteSshRow: some View {
        let trimmedTarget = self.state.remoteTarget.trimmingCharacters(in: .whitespacesAndNewlines)
        let validationMessage = CommandResolver.sshTargetValidationMessage(trimmedTarget)
        let canTest = !trimmedTarget.isEmpty && validationMessage == nil

        return VStack(alignment: .leading, spacing: 0) {
            SettingsCardRow(title: "SSH target", subtitle: "User and host for the remote Gateway machine.") {
                TextField("user@host[:22]", text: self.$state.remoteTarget)
                    .textFieldStyle(.roundedBorder)
                    .frame(width: Self.remoteFieldWidth)
                self.remoteTestButton(disabled: !canTest)
            }
            if let validationMessage {
                Text(validationMessage)
                    .font(.caption)
                    .foregroundStyle(.red)
                    .padding(.horizontal, 14)
                    .padding(.bottom, 10)
            }
        }
    }

    private var remoteDirectRow: some View {
        SettingsCardRow(title: "Gateway URL", subtitle: "The WebSocket URL exposed by the remote Gateway.") {
            VStack(alignment: .leading, spacing: 6) {
                HStack(spacing: 8) {
                    TextField("wss://gateway.example.ts.net", text: self.$state.remoteUrl)
                        .textFieldStyle(.roundedBorder)
                        .frame(width: Self.remoteFieldWidth)
                    self.remoteTestButton(
                        disabled: self.state.remoteUrl.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
                }
                Text("Use wss:// for public hosts. ws:// is allowed for localhost, LAN, .local, and Tailnet hosts.")
                    .font(.caption)
                    .foregroundStyle(.secondary)
                    .fixedSize(horizontal: false, vertical: true)
            }
        }
    }

    private var remoteTokenRow: some View {
        VStack(alignment: .leading, spacing: 0) {
            SettingsCardRow(
                title: "Gateway token",
                subtitle: "Used when the remote gateway requires token auth.",
                showsDivider: false)
            {
                SecureField("remote gateway auth token (gateway.remote.token)", text: self.$state.remoteToken)
                    .textFieldStyle(.roundedBorder)
                    .frame(width: Self.remoteSecretFieldWidth)
            }
            if self.state.remoteTokenUnsupported {
                Text(
                    "The current gateway.remote.token value is not plain text. "
                        + "OpenClaw for macOS cannot use it directly; "
                        + "enter a plaintext token here to replace it.")
                    .font(.caption)
                    .foregroundStyle(.orange)
                    .padding(.horizontal, 14)
                    .padding(.bottom, 10)
            }
        }
    }

    private func remoteTestButton(disabled: Bool) -> some View {
        Button {
            Task { await self.testRemote() }
        } label: {
            if self.remoteStatus == .checking {
                ProgressView().controlSize(.small)
            } else {
                Text("Test remote")
            }
        }
        .buttonStyle(.borderedProminent)
        .frame(minWidth: 116)
        .disabled(self.remoteStatus == .checking || disabled)
    }

    private var controlStatusLine: String {
        switch ControlChannel.shared.state {
        case .connected: "Connected"
        case .connecting: "Connecting…"
        case .disconnected: "Disconnected"
        case let .degraded(msg): msg
        }
    }

    @ViewBuilder
    private var remoteStatusView: some View {
        switch self.remoteStatus {
        case .idle:
            EmptyView()
        case .checking:
            Text("Testing…")
                .font(.caption)
                .foregroundStyle(.secondary)
        case let .ok(success):
            VStack(alignment: .leading, spacing: 2) {
                Label(success.title, systemImage: "checkmark.circle.fill")
                    .font(.caption)
                    .foregroundStyle(.green)
                if let detail = success.detail {
                    Text(detail)
                        .font(.caption)
                        .foregroundStyle(.secondary)
                        .fixedSize(horizontal: false, vertical: true)
                }
            }
        case let .failed(message):
            Text(message)
                .font(.caption)
                .foregroundStyle(.secondary)
                .lineLimit(2)
        }
    }

    private var gatewayInstallerCard: some View {
        VStack(alignment: .leading, spacing: 8) {
            HStack(spacing: 10) {
                Circle()
                    .fill(self.gatewayStatusColor)
                    .frame(width: 10, height: 10)
                Text(self.gatewayStatus.message)
                    .font(.callout)
                    .frame(maxWidth: .infinity, alignment: .leading)
            }

            if let gatewayVersion = self.gatewayStatus.gatewayVersion,
               let required = self.gatewayStatus.requiredGateway,
               gatewayVersion != required
            {
                Text("Installed: \(gatewayVersion) · Required: \(required)")
                    .font(.caption)
                    .foregroundStyle(.secondary)
            } else if let gatewayVersion = self.gatewayStatus.gatewayVersion {
                Text("Gateway \(gatewayVersion) detected")
                    .font(.caption)
                    .foregroundStyle(.secondary)
            }

            if let node = self.gatewayStatus.nodeVersion {
                Text("Node \(node)")
                    .font(.caption)
                    .foregroundStyle(.secondary)
            }

            if case let .attachedExisting(details) = self.gatewayManager.status {
                Text(details ?? "Using existing gateway instance")
                    .font(.caption)
                    .foregroundStyle(.secondary)
            }

            if let failure = self.gatewayManager.lastFailureReason {
                Text("Last failure: \(failure)")
                    .font(.caption)
                    .foregroundStyle(.red)
            }

            Button("Recheck") { self.refreshGatewayStatus() }
                .buttonStyle(.bordered)

            Text("Gateway auto-starts in local mode via launchd (\(gatewayLaunchdLabel)).")
                .font(.caption)
                .foregroundStyle(.secondary)
                .lineLimit(2)
        }
        .padding(12)
        .background(Color.gray.opacity(0.08))
        .cornerRadius(10)
    }

    private func refreshGatewayStatus() {
        Task {
            let status = await Task.detached(priority: .utility) {
                GatewayEnvironment.check()
            }.value
            self.gatewayStatus = status
        }
    }

    private var gatewayStatusColor: Color {
        switch self.gatewayStatus.kind {
        case .ok: .green
        case .checking: .secondary
        case .missingNode, .missingGateway, .incompatible, .error: .orange
        }
    }

    private var healthCard: some View {
        let snapshot = self.healthStore.snapshot
        return VStack(alignment: .leading, spacing: 6) {
            HStack(spacing: 8) {
                Circle()
                    .fill(self.healthStore.state.tint)
                    .frame(width: 10, height: 10)
                Text(self.healthStore.summaryLine)
                    .font(.callout.weight(.semibold))
            }

            if let snap = snapshot {
                let linkId = snap.channelOrder?.first(where: {
                    if let summary = snap.channels[$0] { return summary.linked != nil }
                    return false
                }) ?? snap.channels.keys.first(where: {
                    if let summary = snap.channels[$0] { return summary.linked != nil }
                    return false
                })
                let linkLabel =
                    linkId.flatMap { snap.channelLabels?[$0] } ??
                    linkId?.capitalized ??
                    "Link channel"
                let linkAge = linkId.flatMap { snap.channels[$0]?.authAgeMs }
                Text("\(linkLabel) auth age: \(healthAgeString(linkAge))")
                    .font(.caption)
                    .foregroundStyle(.secondary)
                Text("Session store: \(snap.sessions.path) (\(snap.sessions.count) entries)")
                    .font(.caption)
                    .foregroundStyle(.secondary)
                if let recent = snap.sessions.recent.first {
                    let lastActivity = recent.updatedAt != nil
                        ? relativeAge(from: Date(timeIntervalSince1970: (recent.updatedAt ?? 0) / 1000))
                        : "unknown"
                    Text("Last activity: \(recent.key) \(lastActivity)")
                        .font(.caption)
                        .foregroundStyle(.secondary)
                }
                Text("Last check: \(relativeAge(from: self.healthStore.lastSuccess))")
                    .font(.caption)
                    .foregroundStyle(.secondary)
            } else if let error = self.healthStore.lastError {
                Text(error)
                    .font(.caption)
                    .foregroundStyle(.red)
            } else {
                Text("Health check pending…")
                    .font(.caption)
                    .foregroundStyle(.secondary)
            }

            HStack(spacing: 12) {
                Button {
                    Task { await self.healthStore.refresh(onDemand: true) }
                } label: {
                    if self.healthStore.isRefreshing {
                        ProgressView().controlSize(.small)
                    } else {
                        Label("Run Health Check", systemImage: "arrow.clockwise")
                    }
                }
                .disabled(self.healthStore.isRefreshing)

                Divider().frame(height: 18)

                Button {
                    self.revealLogs()
                } label: {
                    Label("Reveal Logs", systemImage: "doc.text.magnifyingglass")
                }
            }
        }
        .padding(12)
        .background(Color.gray.opacity(0.08))
        .cornerRadius(10)
    }
}

private enum RemoteStatus: Equatable {
    case idle
    case checking
    case ok(RemoteGatewayProbeSuccess)
    case failed(String)
}

extension GeneralSettings {
    private var healthRow: some View {
        VStack(alignment: .leading, spacing: 6) {
            HStack(spacing: 10) {
                Circle()
                    .fill(self.healthStore.state.tint)
                    .frame(width: 10, height: 10)
                Text(self.healthStore.summaryLine)
                    .font(.callout)
                    .frame(maxWidth: .infinity, alignment: .leading)
            }

            if let detail = self.healthStore.detailLine {
                Text(detail)
                    .font(.caption)
                    .foregroundStyle(.secondary)
                    .fixedSize(horizontal: false, vertical: true)
            }

            HStack(spacing: 10) {
                Button("Retry now") {
                    Task { await HealthStore.shared.refresh(onDemand: true) }
                }
                .disabled(self.healthStore.isRefreshing)

                Button("Open logs") { self.revealLogs() }
                    .buttonStyle(.link)
                    .foregroundStyle(.secondary)
            }
            .font(.caption)
        }
    }

    @MainActor
    func testRemote() async {
        self.remoteStatus = .checking
        switch await RemoteGatewayProbe.run() {
        case let .ready(success):
            self.remoteStatus = .ok(success)
        case let .authIssue(issue):
            self.remoteStatus = .failed(issue.statusMessage)
        case let .failed(message):
            self.remoteStatus = .failed(message)
        }
    }

    private func revealLogs() {
        let target = LogLocator.bestLogFile()

        if let target {
            NSWorkspace.shared.selectFile(
                target.path,
                inFileViewerRootedAtPath: target.deletingLastPathComponent().path)
            return
        }

        let alert = NSAlert()
        alert.messageText = "Log file not found"
        alert.informativeText = """
        Looked for openclaw logs in /tmp/openclaw/.
        Run a health check or send a message to generate activity, then try again.
        """
        alert.alertStyle = .informational
        alert.addButton(withTitle: "OK")
        alert.runModal()
    }

    private func applyDiscoveredGateway(_ gateway: GatewayDiscoveryModel.DiscoveredGateway) {
        MacNodeModeCoordinator.shared.setPreferredGatewayStableID(gateway.stableID)
        GatewayDiscoverySelectionSupport.applyRemoteSelection(gateway: gateway, state: self.state)
    }
}

private func healthAgeString(_ ms: Double?) -> String {
    guard let ms else { return "unknown" }
    return msToAge(ms)
}

#if DEBUG
struct GeneralSettings_Previews: PreviewProvider {
    static var previews: some View {
        GeneralSettings(state: .preview)
            .frame(width: SettingsTab.windowWidth, height: SettingsTab.windowHeight)
            .environment(TailscaleService.shared)
    }
}

@MainActor
extension GeneralSettings {
    static func exerciseForTesting() {
        let state = AppState(preview: true)
        state.connectionMode = .remote
        state.remoteTransport = .ssh
        state.remoteTarget = "user@host:2222"
        state.remoteUrl = "wss://gateway.example.ts.net"
        state.remoteToken = "example-token"
        state.remoteIdentity = "/tmp/id_ed25519"
        state.remoteProjectRoot = "/tmp/openclaw"
        state.remoteCliPath = "/tmp/openclaw"

        let view = GeneralSettings(state: state)
        view.gatewayStatus = GatewayEnvironmentStatus(
            kind: .ok,
            nodeVersion: "1.0.0",
            gatewayVersion: "1.0.0",
            requiredGateway: nil,
            message: "Gateway ready")
        view.remoteStatus = .failed("SSH failed")
        view.showRemoteAdvanced = true
        _ = view.body

        state.connectionMode = .unconfigured
        _ = view.body

        state.connectionMode = .local
        view.gatewayStatus = GatewayEnvironmentStatus(
            kind: .error("Gateway offline"),
            nodeVersion: nil,
            gatewayVersion: nil,
            requiredGateway: nil,
            message: "Gateway offline")
        _ = view.body
    }
}
#endif
