import QRCode from "qrcode";
import { type Component, createEffect, createResource, createSignal, For, onCleanup, onMount, Show } from "solid-js";
import { t } from "../../../i18n";
import { appLogger } from "../../../stores/appLogger";
import {
	type ConnectionState,
	type RemoteConnection,
	type RemoteTransport,
	remoteConnectionsStore,
} from "../../../stores/remoteConnections";
import { rpc, type UpstreamMcpConfig, type UpstreamMcpServer, type UpstreamTransport } from "../../../transport";
import { cx } from "../../../utils";
import { handleOpenUrl } from "../../../utils/openUrl";
import { SettingInput, SettingSelect, SettingToggle } from "../SettingFields";
import s from "../Settings.module.css";

/** Pure helper: should the Authorize button be shown for this server+status? */
export function shouldShowAuthorize(authType: string | undefined, status: string | undefined): boolean {
	return authType === "oauth2" || status === "needs_auth" || status === "authenticating";
}

interface StartOAuthResponse {
	authorization_url: string;
	state: string;
}

async function startAuthorizeFlow(name: string): Promise<void> {
	let resp: StartOAuthResponse;
	try {
		resp = await rpc<StartOAuthResponse>("start_mcp_upstream_oauth", { name });
	} catch (e) {
		const msg = String(e);
		appLogger.warn("network", `start_mcp_upstream_oauth failed: ${msg}`);
		const { message } = await import("@tauri-apps/plugin-dialog");
		await message(msg, { title: `Authorization failed for "${name}"`, kind: "error" });
		return;
	}

	// Surface the AS origin before opening the browser so the user sees where
	// they're being sent (AS mix-up defence, #1268-40e8). The backend already
	// bails on cross-domain mismatches, but showing the hostname makes phishing
	// attempts visible even when the mismatch check is bypassed by an explicit
	// authorization_endpoint override.
	let asOrigin = resp.authorization_url;
	try {
		asOrigin = new URL(resp.authorization_url).origin;
	} catch {
		// keep full URL if parsing fails
	}
	const { confirm } = await import("@tauri-apps/plugin-dialog");
	const proceed = await confirm(`About to open ${asOrigin} to authorize "${name}".\n\nContinue?`, {
		title: "Authorize MCP server",
		kind: "info",
	});
	if (!proceed) {
		rpc("cancel_mcp_upstream_oauth", { name }).catch((e) =>
			appLogger.warn("network", `cancel_mcp_upstream_oauth failed: ${String(e)}`),
		);
		return;
	}
	handleOpenUrl(resp.authorization_url);
}

interface McpStatus {
	enabled: boolean;
	running: boolean;
	remote_port: number | null;
	active_sessions: number;
	/** Connected MCP protocol clients (reaped after 1h idle) */
	mcp_clients: number;
	max_sessions: number;
	/** null = remote disabled, true = TCP reachable, false = likely firewalled */
	reachable?: boolean | null;
}

interface ServerConfig {
	enabled: boolean;
	port: number;
	ipv6_enabled: boolean;
}

interface AuthConfig {
	username: string;
	password_hash: string;
	session_token_duration_secs: number;
	lan_auth_bypass: boolean;
}

interface RelayConfig {
	enabled: boolean;
	url: string;
	token: string;
	session_id: string;
}

interface ServicesConfig {
	server: ServerConfig;
	auth: AuthConfig;
	relay: RelayConfig;
}

interface AppConfig {
	shell: string | null;
	font_family: string;
	font_size: number;
	theme: string;
	mcp_server_enabled: boolean;
	services: ServicesConfig;
	disabled_native_tools: string[];
	collapse_tools: boolean;
}

interface RelayStatus {
	enabled: boolean;
	connected: boolean;
	url: string;
	session_id: string;
}

interface LocalIpEntry {
	ip: string;
	label: string;
}

interface UpstreamStatusEntry {
	name: string;
	status: "connecting" | "ready" | "circuit_open" | "disabled" | "failed" | "authenticating" | "needs_auth";
	transport: { type: string; url?: string; command?: string; args?: string[] };
	tool_count: number;
	tools: string[];
	metrics: { call_count: number; error_count: number; last_latency_ms: number };
	last_error?: string | null;
}

/** Static definition of native TUIC tools exposed via MCP */
const NATIVE_TOOLS: { name: string; description: string; actions: string }[] = [
	{
		name: "session",
		description: "PTY terminal panes (tmux replacement)",
		actions: "list, create, input, output, resize, close, kill, pause, resume",
	},
	{
		name: "agent",
		description: "AI agents + inter-agent messaging",
		actions: "spawn, detect, stats, metrics, register, list_peers, send, inbox",
	},
	{
		name: "repo",
		description: "Repos, GitHub PRs, worktrees",
		actions: "list, active, prs, status, worktree_list, worktree_create, worktree_remove",
	},
	{ name: "ui", description: "Panel tabs + notifications", actions: "tab, toast, confirm" },
	{
		name: "plugin_dev_guide",
		description: "Plugin authoring reference",
		actions: "Returns full plugin authoring guide",
	},
	{ name: "config", description: "Read and write app config", actions: "get, save" },
	{ name: "knowledge", description: "Cross-repo knowledge base (mdkb)", actions: "search, code_graph, status, setup" },
	{
		name: "debug",
		description: "Diagnostics + plugin guide",
		actions: "agent_detection, logs, invoke_js, plugin_guide",
	},
];

export const ServicesTab: Component = () => {
	const [status, setStatus] = createSignal<McpStatus | null>(null);
	const [localIps] = createResource(() => rpc<LocalIpEntry[]>("get_local_ips"));
	const [selectedIp, setSelectedIp] = createSignal<string>("");

	// Remote access form state
	const [raEnabled, setRaEnabled] = createSignal(false);
	const [raPort, setRaPort] = createSignal(9876);
	const [raUsername, setRaUsername] = createSignal("");
	const [raPassword, setRaPassword] = createSignal("");
	const [raHasPassword, setRaHasPassword] = createSignal(false);
	const [raShowPassword, setRaShowPassword] = createSignal(false);
	const [qrDataUrl, setQrDataUrl] = createSignal<string | null>(null);
	const [tokenDuration, setTokenDuration] = createSignal(86400);
	const [ipv6Enabled, setIpv6Enabled] = createSignal(false);
	const [lanAuthBypass, setLanAuthBypass] = createSignal(false);
	const [urlCopied, setUrlCopied] = createSignal(false);
	const [regenerating, setRegenerating] = createSignal(false);
	const [disabledNativeTools, setDisabledNativeTools] = createSignal<string[]>([]);
	const [collapseTools, setCollapseTools] = createSignal<boolean>(false);
	const [upstreamStatus, setUpstreamStatus] = createSignal<UpstreamStatusEntry[]>([]);
	const [bridgeInfo, setBridgeInfo] = createSignal<{ bridge_path: string; config_snippet: string } | null>(null);
	const [bridgeInfoOpen, setBridgeInfoOpen] = createSignal(false);
	const [snippetCopied, setSnippetCopied] = createSignal(false);

	// Tailscale state (mirrors Rust TailscaleState enum serialization)
	type TailscaleStatus =
		| { state: "NotInstalled" }
		| { state: "NotRunning" }
		| { state: "Running"; fqdn: string; https_enabled: boolean };

	const [tailscaleState, setTailscaleState] = createSignal<TailscaleStatus | null>(null);

	// Relay state
	const [relayEnabled, setRelayEnabled] = createSignal(false);
	const [relayUrl, setRelayUrl] = createSignal("wss://relay.tuicommander.com");
	const [relayToken, setRelayToken] = createSignal("");
	const [relaySessionId, setRelaySessionId] = createSignal("");
	const [relayConnected, setRelayConnected] = createSignal(false);

	// Auto-select best IP when list loads (prefer Tailscale, then LAN/Wi-Fi)
	createEffect(() => {
		const ips = localIps();
		if (!ips?.length || selectedIp()) return;
		const preferred =
			ips.find((e) => e.label.includes("Tailscale")) ??
			ips.find((e) => e.label.includes("Wi-Fi") || e.label.includes("LAN")) ??
			ips[0];
		setSelectedIp(preferred.ip);
	});

	const activeIp = () => selectedIp() || localIps()?.[0]?.ip;

	// Connect URL fetched from backend (token never reaches JS)
	const [connectUrl, setConnectUrl] = createSignal<string | null>(null);
	// Bumped after token regeneration to re-fetch the URL
	const [qrVersion, setQrVersion] = createSignal(0);

	/** Fetch connect URL from backend so the raw token stays server-side. */
	createEffect(() => {
		const ip = activeIp();
		void qrVersion(); // subscribe — re-run after token regeneration
		if (!ip) {
			setConnectUrl(null);
			setQrDataUrl(null);
			return;
		}
		let cancelled = false;
		onCleanup(() => {
			cancelled = true;
		});
		rpc<string>("get_connect_url", { ip })
			.then((url) => {
				if (cancelled) return;
				setConnectUrl(url);
				return QRCode.toDataURL(url, { width: 160, margin: 2, color: { dark: "#ffffff", light: "#1e1e1e" } });
			})
			.then((dataUrl) => {
				if (!cancelled && dataUrl) setQrDataUrl(dataUrl);
			})
			.catch(() => {
				if (cancelled) return;
				setConnectUrl(null);
				setQrDataUrl(null);
			});
	});

	const refreshStatus = async () => {
		try {
			const s = await rpc<McpStatus>("get_mcp_status");
			setStatus(s);
		} catch (e) {
			// Transient poll failures are normal during app startup
			appLogger.debug("config", "MCP status refresh failed", e);
		}
		try {
			const snap = await rpc<{ upstreams: UpstreamStatusEntry[] }>("get_mcp_upstream_status");
			setUpstreamStatus(snap.upstreams ?? []);
		} catch {
			// Upstream status not available (e.g. server not running)
		}
		try {
			const rs = await rpc<RelayStatus>("get_relay_status");
			setRelayConnected(rs.connected);
		} catch {
			// Relay status not available
		}
	};

	const loadRemoteConfig = async () => {
		try {
			const config = await rpc<AppConfig>("load_config");
			setRaEnabled(config.services.server.enabled);
			setRaPort(config.services.server.port);
			setRaUsername(config.services.auth.username);
			setRaHasPassword(config.services.auth.password_hash.length > 0);
			setTokenDuration(config.services.auth.session_token_duration_secs ?? 86400);
			setIpv6Enabled(config.services.server.ipv6_enabled ?? false);
			setLanAuthBypass(config.services.auth.lan_auth_bypass ?? false);
			setDisabledNativeTools(config.disabled_native_tools ?? []);
			setCollapseTools(config.collapse_tools ?? false);
			setRelayEnabled(config.services.relay.enabled ?? false);
			setRelayUrl(config.services.relay.url || "wss://relay.tuicommander.com");
			setRelayToken(config.services.relay.token ?? "");
			setRelaySessionId(config.services.relay.session_id ?? "");
		} catch (e) {
			appLogger.warn("config", "Failed to load remote access config, using defaults", e);
		}
	};

	onMount(async () => {
		refreshStatus();
		loadRemoteConfig();
		const interval = setInterval(refreshStatus, 3000);
		onCleanup(() => clearInterval(interval));

		// Load Tailscale status
		try {
			const ts = await rpc<TailscaleStatus>("get_tailscale_status");
			setTailscaleState(ts);
		} catch {
			/* Tailscale detection not available */
		}
	});

	/** Save a single config field (load-modify-save pattern matching other tabs) */
	const saveConfigField = async (updater: (config: AppConfig) => void) => {
		try {
			const config = await rpc<AppConfig>("load_config");
			updater(config);
			await rpc("save_config", { config });
		} catch (e) {
			appLogger.error("config", "Failed to save config", e);
		}
	};

	/** Hash and save a new password */
	const savePassword = async (password: string) => {
		if (!password) return;
		try {
			const hash = await rpc<string>("hash_password", { password });
			await saveConfigField((c) => {
				c.services.auth.password_hash = hash;
			});
			setRaPassword("");
			setRaHasPassword(true);
		} catch (e) {
			appLogger.error("config", "Failed to hash password", e);
		}
	};

	const copyUrl = async () => {
		const url = connectUrl();
		if (!url) return;
		try {
			await navigator.clipboard.writeText(url);
			setUrlCopied(true);
			setTimeout(() => setUrlCopied(false), 2000);
		} catch {
			// Fallback: select text for manual copy
		}
	};

	const regenerateToken = async () => {
		setRegenerating(true);
		try {
			await rpc("regenerate_session_token");
			setQrVersion((v) => v + 1); // re-fetch QR URL with new token
			await refreshStatus();
		} catch (e) {
			appLogger.error("config", "Failed to regenerate token", e);
		} finally {
			setRegenerating(false);
		}
	};

	/** Token duration options */
	const TOKEN_DURATIONS = [
		{ value: 3600, label: t("services.tokenDuration.1h", "1 hour") },
		{ value: 86400, label: t("services.tokenDuration.24h", "24 hours") },
		{ value: 604800, label: t("services.tokenDuration.7d", "7 days") },
		{ value: 31536000, label: t("services.tokenDuration.never", "Never") },
	];

	return (
		<div class={s.section}>
			<h3>{t("services.heading.httpApiServer", "HTTP API Server")}</h3>

			<div class={s.group}>
				<p class={s.hint}>
					{t(
						"services.hint.httpDescription",
						"Serves the REST API and MCP protocol for AI agents and automation tools",
					)}
				</p>
			</div>

			<div class={s.group}>
				<label>{t("services.label.serverStatus", "Server Status")}</label>
				<div class={s.mcpStatusRow}>
					<span class={cx(s.mcpStatusDot, status()?.running && s.running)} />
					<span class={s.mcpStatusText}>
						{status()?.running ? t("services.status.running", "Running") : t("services.status.starting", "Starting...")}
					</span>
					<Show when={status()?.running}>
						<span class={s.mcpStatusPort}>{t("services.label.socket", "Socket")}</span>
					</Show>
				</div>
			</div>

			<div class={s.group}>
				<label>{t("services.label.mcpConnection", "MCP Connection")}</label>
				<p class={s.hint}>
					{t(
						"services.hint.mcpConnection",
						"AI agents connect via the tuic-bridge sidecar. MCP configs are auto-installed in supported agents (Claude Code, Cursor, etc.).",
					)}
				</p>
			</div>

			<h3>{t("services.heading.remoteAccess", "Remote Access")}</h3>

			<SettingToggle
				checked={raEnabled()}
				onChange={(val) => {
					setRaEnabled(val);
					saveConfigField((c) => {
						c.services.server.enabled = val;
					});
				}}
				label={t("services.toggle.enableRemoteAccess", "Enable remote access")}
				hint={t(
					"services.hint.remoteAccessWarning",
					"Warning: exposes a web interface on your local network. Secure with a strong password.",
				)}
				hintStyle={{ color: "var(--warning, #e5c07b)" }}
			/>

			<Show when={raEnabled()}>
				<div class={s.raBody}>
					<div class={s.raFields}>
						<div class={s.group}>
							<label>{t("services.label.port", "Port")}</label>
							<input
								type="number"
								class={s.input}
								value={raPort()}
								min={1024}
								max={65535}
								onInput={(e) => setRaPort(parseInt(e.currentTarget.value, 10) || 9876)}
								onChange={() =>
									saveConfigField((c) => {
										c.services.server.port = raPort();
									})
								}
							/>
							<p class={s.hint}>{t("services.hint.port", "TCP port for the remote access web server")}</p>
						</div>

						{/* Username + Password side by side */}
						<div class={s.credentialsRow}>
							<div class={s.group} style={{ flex: "1", "min-width": 0 }}>
								<label>{t("services.label.username", "Username")}</label>
								<input
									type="text"
									class={s.input}
									value={raUsername()}
									placeholder={t("services.placeholder.username", "admin")}
									onInput={(e) => setRaUsername(e.currentTarget.value)}
									onChange={() =>
										saveConfigField((c) => {
											c.services.auth.username = raUsername();
										})
									}
								/>
							</div>
							<div class={s.group} style={{ flex: "1", "min-width": 0 }}>
								<label>{t("services.label.password", "Password")}</label>
								<div class={s.passwordRow}>
									<input
										type={raShowPassword() ? "text" : "password"}
										class={s.input}
										value={raPassword()}
										placeholder={
											raHasPassword()
												? t("services.placeholder.passwordSet", "Password set — enter to change")
												: t("services.placeholder.passwordEnter", "Enter password")
										}
										onInput={(e) => setRaPassword(e.currentTarget.value)}
										onChange={() => savePassword(raPassword())}
									/>
									<button
										class={s.toggleBtn}
										onClick={() => setRaShowPassword(!raShowPassword())}
										title={
											raShowPassword()
												? t("services.btn.hidePassword", "Hide password")
												: t("services.btn.showPassword", "Show password")
										}
									>
										{raShowPassword() ? (
											/* eye-slash */ <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
												<path d="M13.359 11.238C15.06 9.72 16 8 16 8s-3-5.5-8-5.5a7.028 7.028 0 0 0-2.79.588l.77.771A5.944 5.944 0 0 1 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.134 13.134 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755-.165.165-.337.328-.517.486l.708.709z" />
												<path d="M11.297 9.176a3.5 3.5 0 0 0-4.474-4.474l.823.823a2.5 2.5 0 0 1 2.829 2.829l.822.822zm-2.943 1.299.822.822a3.5 3.5 0 0 1-4.474-4.474l.823.823a2.5 2.5 0 0 0 2.829 2.829z" />
												<path d="M3.35 5.47c-.18.16-.353.322-.518.487A13.134 13.134 0 0 0 1.172 8l.195.288c.335.48.83 1.12 1.465 1.755C4.121 11.332 5.881 12.5 8 12.5c.716 0 1.39-.133 2.02-.36l.77.772A7.029 7.029 0 0 1 8 13.5C3 13.5 0 8 0 8s.939-1.721 2.641-3.238l.708.709zm10.296 8.884-12-12 .708-.708 12 12-.708.708z" />
											</svg>
										) : (
											/* eye */ <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
												<path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z" />
												<path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z" />
											</svg>
										)}
									</button>
								</div>
								<p class={s.hint}>
									{t("services.hint.passwordLeaveBlank", "Leave blank to keep the current password")}
								</p>
							</div>
						</div>

						<Show when={raEnabled()}>
							<div class={s.group}>
								<label>{t("services.label.networkInterface", "Network Interface")}</label>
								<Show when={(localIps()?.length ?? 0) > 1} fallback={<code class={s.url}>{activeIp() ?? "…"}</code>}>
									<select class={s.input} value={selectedIp()} onChange={(e) => setSelectedIp(e.currentTarget.value)}>
										<For each={localIps()}>
											{(entry) => (
												<option value={entry.ip}>
													{entry.label} — {entry.ip}
												</option>
											)}
										</For>
									</select>
								</Show>
								<p class={s.hint} style={{ "margin-top": "4px" }}>
									{t("services.hint.qrScan", "Scan the QR code to connect from another device")}
								</p>
								<Show when={status()?.reachable === false}>
									<p class={s.hint} style={{ color: "var(--warning, #e5c07b)", "margin-top": "4px" }}>
										{t("services.hint.firewallWarning", "Port may be blocked by a firewall")}
									</p>
								</Show>
								<Show when={status()?.reachable === true}>
									<p class={s.hint} style={{ color: "var(--green, #98c379)", "margin-top": "4px" }}>
										{t("services.hint.serverReachable", "Server is reachable from the network")}
									</p>
								</Show>
							</div>
						</Show>

						<SettingSelect
							label={t("services.label.tokenDuration", "Session Token Duration")}
							value={String(tokenDuration())}
							onChange={(v) => {
								const val = parseInt(v, 10);
								setTokenDuration(val);
								saveConfigField((c) => {
									c.services.auth.session_token_duration_secs = val;
								});
							}}
							options={TOKEN_DURATIONS.map((o) => ({ value: String(o.value), label: o.label }))}
							hint={t(
								"services.hint.tokenDuration",
								"How long remote sessions stay authenticated. Token always resets on app restart.",
							)}
						/>

						<div class={s.group}>
							<button class={s.testBtn} disabled={regenerating()} onClick={regenerateToken}>
								{regenerating()
									? t("services.btn.regenerating", "Regenerating...")
									: t("services.btn.regenerateToken", "Regenerate Token")}
							</button>
							<p class={s.hint}>
								{t("services.hint.regenerateToken", "Generates a new token, disconnecting all active remote sessions")}
							</p>
						</div>
					</div>

					<Show when={connectUrl()}>
						<div class={s.qr}>
							<Show when={qrDataUrl()}>
								{(url) => (
									<img
										src={url()}
										width={120}
										height={120}
										alt={t("services.alt.qrCode", "QR code")}
										title={t("services.title.qrCode", "Scan to connect")}
									/>
								)}
							</Show>
							<span class={s.qrLabel}>{t("services.label.scanToConnect", "Scan to connect")}</span>
							{/* Connection URL right under QR code */}
							<div class={s.urlCopyRow} style={{ "margin-top": "8px", "max-width": "200px" }}>
								<code class={s.urlFull} style={{ "font-size": "10px", "word-break": "break-all" }}>
									{connectUrl()}
								</code>
								<button class={s.copyBtn} onClick={copyUrl} title={t("services.btn.copyUrl", "Copy URL to clipboard")}>
									{urlCopied() ? (
										<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
											<path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.75.75 0 0 1 1.06-1.06L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0z" />
										</svg>
									) : (
										<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
											<path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z" />
											<path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z" />
										</svg>
									)}
								</button>
							</div>
						</div>
					</Show>
				</div>

				<SettingToggle
					checked={ipv6Enabled()}
					onChange={(val) => {
						setIpv6Enabled(val);
						saveConfigField((c) => {
							c.services.server.ipv6_enabled = val;
						});
					}}
					label={t("services.toggle.enableIpv6", "Enable IPv6 (dual-stack)")}
					hint={t(
						"services.hint.ipv6Description",
						"Binds the server to both IPv4 and IPv6 addresses. Requires save + server restart.",
					)}
				/>

				<SettingToggle
					checked={lanAuthBypass()}
					onChange={(val) => {
						setLanAuthBypass(val);
						saveConfigField((c) => {
							c.services.auth.lan_auth_bypass = val;
						});
					}}
					label={t("services.toggle.lanAuthBypass", "Allow LAN access without authentication")}
					hint={
						lanAuthBypass()
							? t(
									"services.hint.lanAuthBypassWarning",
									"Devices on your local network can access without a password. Only use on trusted networks.",
								)
							: t(
									"services.hint.lanAuthBypassDescription",
									"Skips authentication for private/LAN IP addresses (RFC1918, Tailscale, IPv6 ULA)",
								)
					}
					hintStyle={lanAuthBypass() ? { color: "var(--warning, #e5c07b)" } : undefined}
				/>
			</Show>

			{/* ── Tailscale TLS ── */}
			<Show when={tailscaleState()}>
				{(() => {
					const ts = tailscaleState()!;
					const statusText =
						ts.state === "Running"
							? ts.https_enabled
								? `HTTPS active (${ts.fqdn})`
								: "Running (HTTPS not enabled)"
							: ts.state === "NotRunning"
								? "Not running"
								: "Not installed";
					const showHint = ts.state === "Running" && !ts.https_enabled;
					return (
						<>
							<h3>Tailscale HTTPS</h3>
							<div class={s.group}>
								<div class={s.row}>
									<span class={s.label}>{t("services.label.tailscaleStatus", "Status")}</span>
									<span class={s.value}>
										{statusText}
										<button
											class={s.inlineBtn}
											onClick={async () => {
												try {
													const updated = await rpc<TailscaleStatus>("recheck_tailscale_status");
													setTailscaleState(updated);
												} catch (e) {
													appLogger.error("tailscale", "Recheck failed", e);
												}
											}}
											title={t("services.action.recheckTailscale", "Recheck Tailscale status")}
										>
											{t("services.action.recheck", "Recheck")}
										</button>
									</span>
								</div>
								<Show when={showHint}>
									<p class={s.hint}>
										{t(
											"services.hint.tailscaleEnableHttps",
											"Enable HTTPS certificates in your Tailscale admin console to serve the PWA over HTTPS.",
										)}
									</p>
								</Show>
							</div>
						</>
					);
				})()}
			</Show>

			{/* ── Cloud Relay ── */}
			<h3>{t("services.heading.cloudRelay", "Cloud Relay")}</h3>

			<SettingToggle
				checked={relayEnabled()}
				onChange={(val) => {
					setRelayEnabled(val);
					if (val && !relaySessionId()) {
						const id = crypto.randomUUID();
						setRelaySessionId(id);
						saveConfigField((c) => {
							c.services.relay.enabled = val;
							c.services.relay.session_id = id;
						});
					} else {
						saveConfigField((c) => {
							c.services.relay.enabled = val;
						});
					}
				}}
				label={t("services.toggle.enableRelay", "Enable cloud relay")}
				hint={t(
					"services.hint.relayDescription",
					"Connect from anywhere via an E2E-encrypted WebSocket relay. No port forwarding or VPN needed.",
				)}
			/>

			<Show when={relayEnabled()}>
				<div class={s.group}>
					<div class={s.mcpStatusRow}>
						<span class={cx(s.mcpStatusDot, relayConnected() && s.running)} />
						<span class={s.mcpStatusText}>
							{relayConnected()
								? t("services.relay.connected", "Connected")
								: t("services.relay.disconnected", "Disconnected")}
						</span>
					</div>
					<p class={s.hint} style={{ color: "var(--warning, #e5c07b)" }}>
						{t("services.hint.relayRestart", "Changes require an app restart to take effect.")}
					</p>
				</div>

				<SettingInput
					label={t("services.label.relayUrl", "Relay Server URL")}
					value={relayUrl()}
					onInput={(v) => {
						setRelayUrl(v);
						saveConfigField((c) => {
							c.services.relay.url = v;
						});
					}}
					placeholder="wss://relay.tuicommander.com"
				/>

				<SettingInput
					label={t("services.label.relayToken", "Bearer Token")}
					value={relayToken()}
					onInput={(v) => {
						setRelayToken(v);
						saveConfigField((c) => {
							c.services.relay.token = v;
						});
					}}
					type="password"
					placeholder={t("services.placeholder.relayToken", "Paste token from relay server registration")}
					hint={t(
						"services.hint.relayToken",
						"Obtained from the relay server's /register endpoint. Used for both authentication and E2E encryption key derivation.",
					)}
				/>

				<div class={s.group}>
					<label>{t("services.label.relaySessionId", "Session ID")}</label>
					<div class={s.passwordRow}>
						<input type="text" class={s.input} value={relaySessionId()} readOnly />
						<button
							class={s.toggleBtn}
							onClick={() => {
								const id = crypto.randomUUID();
								setRelaySessionId(id);
								saveConfigField((c) => {
									c.services.relay.session_id = id;
								});
							}}
							title={t("services.btn.regenerateSessionId", "Generate new session ID")}
						>
							<svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor">
								<path d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2z" />
								<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466" />
							</svg>
						</button>
					</div>
					<p class={s.hint}>
						{t(
							"services.hint.relaySessionId",
							"Shared with the mobile client to join the same relay room. Regenerating disconnects the current mobile session.",
						)}
					</p>
				</div>
			</Show>

			{/* ── TUIC Tools ── */}
			<h3>TUIC Tools</h3>
			<div class={s.group}>
				<p class={s.hint}>Native tools exposed via MCP. Disable tools to restrict what AI agents can access.</p>
			</div>

			<div class={s.group}>
				<button
					class={s.mcpDisclosure}
					onClick={() => {
						const opening = !bridgeInfoOpen();
						setBridgeInfoOpen(opening);
						if (opening && !bridgeInfo()) {
							rpc<{ bridge_path: string; config_snippet: string }>("get_mcp_bridge_info")
								.then(setBridgeInfo)
								.catch((e) => appLogger.warn("config", "Failed to fetch bridge info", e));
						}
					}}
				>
					<span class={s.mcpDisclosureArrow}>{bridgeInfoOpen() ? "▼" : "▶"}</span>
					Manual MCP configuration
				</button>
				<Show when={bridgeInfoOpen() && bridgeInfo()}>
					<div class={s.mcpDisclosureBody}>
						<p class={s.hint} style={{ margin: "0 0 4px" }}>
							Bridge path: <code class={s.mcpCode}>{bridgeInfo()!.bridge_path}</code>
						</p>
						<p class={s.hint} style={{ margin: "0 0 6px" }}>
							Add this to your MCP client config (e.g. <code class={s.mcpCode}>~/.claude.json</code> under{" "}
							<code class={s.mcpCode}>mcpServers</code>):
						</p>
						<div class={s.mcpSnippetWrap}>
							<pre class={s.mcpSnippetPre}>{bridgeInfo()!.config_snippet}</pre>
							<button
								class={s.mcpSnippetCopy}
								onClick={() => {
									navigator.clipboard
										.writeText(bridgeInfo()!.config_snippet)
										.then(() => {
											setSnippetCopied(true);
											setTimeout(() => setSnippetCopied(false), 2000);
										})
										.catch((err) => {
											appLogger.warn("settings", "Clipboard write failed", { error: String(err) });
										});
								}}
							>
								{snippetCopied() ? "Copied" : "Copy"}
							</button>
						</div>
					</div>
				</Show>
			</div>

			<div class={s.group} style={{ display: "flex", "align-items": "center", gap: "8px", padding: "4px 0" }}>
				<div class={s.toggle} style={{ "margin-right": "4px" }}>
					<input
						type="checkbox"
						checked={collapseTools()}
						onChange={(e) => {
							const enabled = e.currentTarget.checked;
							setCollapseTools(enabled);
							saveConfigField((c) => {
								(c as AppConfig & { collapse_tools: boolean }).collapse_tools = enabled;
							});
						}}
					/>
				</div>
				<div style={{ display: "flex", "align-items": "center", gap: "6px" }}>
					<span style={{ "font-weight": 500, "font-size": "13px" }}>
						Collapse tools — Speakeasy MCP (reduces AI context ~98%)
					</span>
					<span class={s.infoBadge}>
						?
						<span class={s.infoBadgeTip}>
							When enabled, MCP clients only see three meta-tools (search_tools, get_tool_schema, call_tool) and
							discover the full tool set on demand. Drastically reduces token usage for clients that don't need every
							tool upfront.
						</span>
					</span>
				</div>
			</div>
			<For each={NATIVE_TOOLS}>
				{(tool) => {
					const disabled = () => disabledNativeTools().includes(tool.name);
					return (
						<div class={s.group} style={{ display: "flex", "align-items": "center", gap: "8px", padding: "4px 0" }}>
							<div class={s.toggle} style={{ "margin-right": "4px" }}>
								<input
									type="checkbox"
									checked={!disabled()}
									onChange={(e) => {
										const enabled = e.currentTarget.checked;
										const updated = enabled
											? disabledNativeTools().filter((n) => n !== tool.name)
											: [...disabledNativeTools(), tool.name];
										setDisabledNativeTools(updated);
										saveConfigField((c) => {
											(c as AppConfig & { disabled_native_tools: string[] }).disabled_native_tools = updated;
										});
									}}
								/>
							</div>
							<div style={{ display: "flex", "align-items": "center", gap: "6px" }}>
								<span style={{ "font-weight": 500, "font-size": "13px", "font-family": "monospace" }}>{tool.name}</span>
								<span class={s.hint} style={{ margin: 0 }}>
									{tool.description}
								</span>
								<span class={s.infoBadge}>
									?<span class={s.infoBadgeTip}>{tool.actions}</span>
								</span>
							</div>
						</div>
					);
				}}
			</For>

			<UpstreamMcpPanel upstreamStatus={upstreamStatus()} />

			<RemoteMachinesPanel />

			<p class={s.hint} style={{ "margin-top": "16px", color: "var(--text-dimmed)" }}>
				{t("services.hint.autoSave", "Settings are saved automatically when changed")}
			</p>
		</div>
	);
};

// ---------------------------------------------------------------------------
// Upstream MCP Servers panel (Tauri-only — uses OS keyring)
// ---------------------------------------------------------------------------

/** Blank form state for adding a new upstream */
function emptyForm() {
	return {
		name: "",
		transportType: "http" as "http" | "stdio",
		url: "",
		command: "",
		args: "",
		cwd: "",
		credential: "",
		timeout: 30,
		authMethod: "bearer" as "bearer" | "oauth2",
		oauthClientId: "",
		oauthClientSecret: "",
		oauthScopes: "",
	};
}

const UpstreamMcpPanel: Component<{ upstreamStatus: UpstreamStatusEntry[] }> = (props) => {
	const [upstreams, setUpstreams] = createSignal<UpstreamMcpServer[]>([]);
	const [showAdd, setShowAdd] = createSignal(false);
	const [form, setForm] = createSignal(emptyForm());
	const [saving, setSaving] = createSignal(false);
	const [error, setError] = createSignal("");
	const [editingId, setEditingId] = createSignal<string | null>(null);
	const [editForm, setEditForm] = createSignal(emptyForm());

	// Load upstream config on mount (Tauri-only)
	onMount(async () => {
		try {
			const cfg = await rpc<UpstreamMcpConfig>("load_mcp_upstreams");
			setUpstreams(cfg.servers ?? []);
		} catch {
			// Not in Tauri — silently skip
		}
	});

	async function saveUpstreams(servers: UpstreamMcpServer[]): Promise<boolean> {
		setSaving(true);
		setError("");
		try {
			appLogger.info("mcp", "saveUpstreams: calling RPC", {
				serverCount: servers.length,
				names: servers.map((s) => s.name),
			});
			await rpc("save_mcp_upstreams", { config: { servers } });
			appLogger.info("mcp", "saveUpstreams: RPC succeeded");
			setUpstreams(servers);
			return true;
		} catch (e) {
			appLogger.error("mcp", "saveUpstreams: RPC failed", { error: String(e) });
			setError(String(e));
			return false;
		} finally {
			setSaving(false);
		}
	}

	async function addUpstream() {
		const f = form();
		const name = f.name.trim();
		if (!name) {
			setError("Name is required");
			return;
		}
		if (!/^[a-z0-9_-]+$/.test(name)) {
			setError("Name must be lowercase letters, digits, hyphens, or underscores only");
			return;
		}
		if (f.transportType === "http" && !f.url.trim()) {
			setError("URL is required for HTTP transport");
			return;
		}
		if (f.transportType === "stdio" && !f.command.trim()) {
			setError("Command is required for stdio transport");
			return;
		}
		const transport: UpstreamTransport =
			f.transportType === "http"
				? { type: "http", url: f.url.trim() }
				: {
						type: "stdio",
						command: f.command.trim(),
						args: f.args.trim() ? f.args.trim().split(/\s+/) : [],
						...(f.cwd.trim() ? { cwd: f.cwd.trim() } : {}),
					};

		const server: UpstreamMcpServer = {
			id: crypto.randomUUID(),
			name: f.name.trim(),
			transport,
			enabled: true,
			timeout_secs: f.timeout,
		};

		if (f.transportType === "http" && f.authMethod === "oauth2" && f.oauthClientId.trim()) {
			const scopes = f.oauthScopes.trim()
				? f.oauthScopes
						.trim()
						.split(/[\s,]+/)
						.filter(Boolean)
				: undefined;
			const secret = f.oauthClientSecret.trim() || undefined;
			server.auth = {
				type: "oauth2",
				client_id: f.oauthClientId.trim(),
				...(secret ? { client_secret: secret } : {}),
				...(scopes?.length ? { scopes } : {}),
			};
		}

		// Save credential before persisting config (ignored if empty)
		if (f.credential && f.authMethod === "bearer") {
			try {
				await rpc("save_mcp_upstream_credential", { name: server.name, token: f.credential });
			} catch {
				// Non-fatal — credential might not be needed
			}
		}

		const ok = await saveUpstreams([...upstreams(), server]);
		if (!ok) return;
		setForm(emptyForm());
		setShowAdd(false);
	}

	/** Get live status entry for an upstream by name */
	function getStatus(name: string): UpstreamStatusEntry | undefined {
		return props.upstreamStatus.find((u) => u.name === name);
	}

	/** Status dot color based on upstream connection state */
	function statusColor(st: string | undefined): string {
		switch (st) {
			case "ready":
				return "var(--green, #98c379)";
			case "connecting":
				return "var(--warning, #e5c07b)";
			case "authenticating":
				return "var(--info, #61afef)";
			case "needs_auth":
				return "var(--warning, #e5c07b)";
			case "circuit_open":
			case "failed":
				return "var(--error, #e06c75)";
			default:
				return "var(--text-dimmed)";
		}
	}

	/** Human-readable label for a status string. */
	function statusLabel(st: string | undefined): string {
		switch (st) {
			case "ready":
				return "Connected";
			case "connecting":
				return "Connecting…";
			case "authenticating":
				return "Awaiting authorization…";
			case "needs_auth":
				return "Authorize to connect";
			case "circuit_open":
				return "Retrying…";
			case "failed":
				return "Failed";
			case "disabled":
				return "Disabled";
			default:
				return st ?? "Unknown";
		}
	}

	function startEdit(server: UpstreamMcpServer) {
		setEditingId(server.id);
		const isOAuth = server.auth?.type === "oauth2";
		setEditForm({
			name: server.name,
			transportType: server.transport.type,
			url: server.transport.type === "http" ? server.transport.url : "",
			command: server.transport.type === "stdio" ? server.transport.command : "",
			args: server.transport.type === "stdio" ? (server.transport.args?.join(" ") ?? "") : "",
			cwd: server.transport.type === "stdio" ? (server.transport.cwd ?? "") : "",
			credential: "",
			timeout: server.timeout_secs,
			authMethod: isOAuth ? "oauth2" : "bearer",
			oauthClientId: isOAuth ? (server.auth as { client_id: string }).client_id : "",
			oauthClientSecret: isOAuth ? ((server.auth as { client_secret?: string }).client_secret ?? "") : "",
			oauthScopes: isOAuth ? ((server.auth as { scopes?: string[] }).scopes?.join(" ") ?? "") : "",
		});
	}

	async function saveEdit(server: UpstreamMcpServer) {
		const f = editForm();
		const transport: UpstreamTransport =
			f.transportType === "http"
				? { type: "http", url: f.url.trim() }
				: {
						type: "stdio",
						command: f.command.trim(),
						args: f.args.trim() ? f.args.trim().split(/\s+/) : [],
						...(f.cwd.trim() ? { cwd: f.cwd.trim() } : {}),
					};

		const updated: UpstreamMcpServer = {
			...server,
			transport,
			timeout_secs: f.timeout,
		};

		if (f.transportType === "http" && f.authMethod === "oauth2" && f.oauthClientId.trim()) {
			const scopes = f.oauthScopes.trim()
				? f.oauthScopes
						.trim()
						.split(/[\s,]+/)
						.filter(Boolean)
				: undefined;
			const secret = f.oauthClientSecret.trim() || undefined;
			const existingOAuth =
				server.auth?.type === "oauth2"
					? (server.auth as { authorization_endpoint?: string; token_endpoint?: string })
					: undefined;
			updated.auth = {
				type: "oauth2" as const,
				client_id: f.oauthClientId.trim(),
				...(secret ? { client_secret: secret } : {}),
				...(scopes?.length ? { scopes } : {}),
				...(existingOAuth?.authorization_endpoint
					? { authorization_endpoint: existingOAuth.authorization_endpoint }
					: {}),
				...(existingOAuth?.token_endpoint ? { token_endpoint: existingOAuth.token_endpoint } : {}),
			};
		} else if (f.authMethod === "bearer") {
			delete updated.auth;
		}

		if (f.credential && f.authMethod === "bearer") {
			try {
				await rpc("save_mcp_upstream_credential", { name: server.name, token: f.credential });
			} catch {
				/* non-fatal */
			}
		}

		await saveUpstreams(upstreams().map((s) => (s.id === server.id ? updated : s)));
		setEditingId(null);
	}

	async function toggleUpstream(id: string, enabled: boolean) {
		const updated = upstreams().map((s) => (s.id === id ? { ...s, enabled } : s));
		await saveUpstreams(updated);
	}

	async function removeUpstream(id: string, name: string) {
		let confirmed: boolean;
		try {
			const { confirm } = await import("@tauri-apps/plugin-dialog");
			confirmed = await confirm(`Remove upstream "${name}"?`, { title: "Remove MCP upstream", kind: "warning" });
		} catch {
			confirmed = window.confirm(`Remove upstream "${name}"?`);
		}
		if (!confirmed) return;
		await rpc("delete_mcp_upstream_credential", { name }).catch((e) =>
			appLogger.error("settings", "Failed to delete MCP upstream credential", { error: String(e) }),
		);
		await saveUpstreams(upstreams().filter((s) => s.id !== id));
	}

	return (
		<div style={{ "margin-top": "24px", "border-top": "1px solid var(--border)", "padding-top": "16px" }}>
			<div class={s.group}>
				<label style={{ display: "flex", "align-items": "center", gap: "8px", "justify-content": "space-between" }}>
					<span>Upstream MCP Servers</span>
					<button
						class={s.copyBtn}
						onClick={() => {
							setShowAdd((v) => !v);
							setError("");
						}}
						title="Add upstream server"
						style={{ "font-size": "18px", "line-height": 1 }}
					>
						{showAdd() ? "−" : "+"}
					</button>
				</label>
				<p class={s.hint}>
					Proxy external MCP servers through TUIC. Their tools appear prefixed as <code>{"{name}__{tool}"}</code>.
				</p>
			</div>

			{/* Add upstream form */}
			<Show when={showAdd()}>
				<div
					class={s.group}
					style={{ background: "var(--bg-secondary, rgba(255,255,255,0.03))", padding: "12px", "border-radius": "6px" }}
				>
					<div style={{ display: "grid", gap: "8px" }}>
						<input
							type="text"
							class={s.input}
							placeholder="Name (e.g. context7, github)"
							value={form().name}
							onInput={(e) => {
								const normalized = e.currentTarget.value.toLowerCase().replace(/[^a-z0-9_-]/g, "-");
								e.currentTarget.value = normalized;
								setForm((f) => ({ ...f, name: normalized }));
							}}
						/>
						<select
							class={s.input}
							value={form().transportType}
							onChange={(e) => setForm((f) => ({ ...f, transportType: e.currentTarget.value as "http" | "stdio" }))}
						>
							<option value="http">HTTP (Streamable MCP)</option>
							<option value="stdio">stdio (process)</option>
						</select>
						<Show when={form().transportType === "http"}>
							<input
								type="text"
								class={s.input}
								placeholder="URL (e.g. http://localhost:8080/mcp)"
								value={form().url}
								onInput={(e) => setForm((f) => ({ ...f, url: e.currentTarget.value }))}
							/>
							<div style={{ display: "flex", gap: "8px", "align-items": "center" }}>
								<label style={{ "font-size": "12px", color: "var(--text-dimmed)", "min-width": "90px" }}>
									Authentication
								</label>
								<select
									class={s.input}
									value={form().authMethod}
									onChange={(e) => setForm((f) => ({ ...f, authMethod: e.currentTarget.value as "bearer" | "oauth2" }))}
									style={{ flex: 1 }}
								>
									<option value="bearer">Bearer token</option>
									<option value="oauth2">OAuth 2.1</option>
								</select>
							</div>
							<Show when={form().authMethod === "bearer"}>
								<input
									type="password"
									class={s.input}
									placeholder="API key for remote MCP servers (optional, stored in OS keychain)"
									value={form().credential}
									onInput={(e) => setForm((f) => ({ ...f, credential: e.currentTarget.value }))}
								/>
							</Show>
							<Show when={form().authMethod === "oauth2"}>
								<input
									type="text"
									class={s.input}
									placeholder="Client ID (leave blank to auto-register)"
									value={form().oauthClientId}
									onInput={(e) => setForm((f) => ({ ...f, oauthClientId: e.currentTarget.value }))}
								/>
								<input
									type="password"
									class={s.input}
									placeholder="Client Secret (optional, for confidential clients)"
									value={form().oauthClientSecret}
									onInput={(e) => setForm((f) => ({ ...f, oauthClientSecret: e.currentTarget.value }))}
								/>
								<input
									type="text"
									class={s.input}
									placeholder="Scopes (optional, space-separated)"
									value={form().oauthScopes}
									onInput={(e) => setForm((f) => ({ ...f, oauthScopes: e.currentTarget.value }))}
								/>
								<p class={s.hint} style={{ margin: "2px 0 0" }}>
									Authorization will begin after saving. The browser opens for sign-in.
								</p>
							</Show>
						</Show>
						<Show when={form().transportType === "stdio"}>
							<input
								type="text"
								class={s.input}
								placeholder="Command (e.g. npx)"
								value={form().command}
								onInput={(e) => setForm((f) => ({ ...f, command: e.currentTarget.value }))}
							/>
							<input
								type="text"
								class={s.input}
								placeholder="Args (space-separated, e.g. -y @modelcontextprotocol/server-filesystem /path)"
								value={form().args}
								onInput={(e) => setForm((f) => ({ ...f, args: e.currentTarget.value }))}
							/>
							<input
								type="text"
								class={s.input}
								placeholder="Working directory (optional)"
								value={form().cwd}
								onInput={(e) => setForm((f) => ({ ...f, cwd: e.currentTarget.value }))}
							/>
						</Show>
						<div style={{ display: "flex", gap: "8px", "align-items": "center" }}>
							<label style={{ "font-size": "12px", color: "var(--text-dimmed)" }}>Timeout (s):</label>
							<input
								type="number"
								class={s.input}
								value={form().timeout}
								min={0}
								max={300}
								style={{ width: "70px" }}
								onInput={(e) => setForm((f) => ({ ...f, timeout: parseInt(e.currentTarget.value, 10) || 30 }))}
							/>
							<button class={s.copyBtn} onClick={addUpstream} disabled={saving()} style={{ "margin-left": "auto" }}>
								{saving() ? "Adding…" : "Add"}
							</button>
							<button
								class={s.copyBtn}
								onClick={() => {
									setShowAdd(false);
									setForm(emptyForm());
									setError("");
								}}
							>
								Cancel
							</button>
						</div>
						<Show when={error()}>
							<p class={s.hint} style={{ color: "var(--error, #e06c75)" }}>
								{error()}
							</p>
						</Show>
					</div>
				</div>
			</Show>

			{/* Upstream list */}
			<Show when={upstreams().length === 0 && !showAdd()}>
				<p class={s.hint} style={{ color: "var(--text-dimmed)" }}>
					No upstream servers configured. Click <strong>+</strong> to add one.
				</p>
			</Show>

			<For each={upstreams()}>
				{(server) => {
					const st = () => getStatus(server.name);
					const isEditing = () => editingId() === server.id;
					return (
						<div style={{ "border-bottom": "1px solid var(--border-subtle, rgba(255,255,255,0.06))" }}>
							<div class={s.group} style={{ display: "flex", "align-items": "center", gap: "8px", padding: "8px 0" }}>
								{/* Enable toggle */}
								<div class={s.toggle} style={{ "margin-right": "4px" }}>
									<input
										type="checkbox"
										checked={server.enabled}
										onChange={(e) => toggleUpstream(server.id, e.currentTarget.checked)}
									/>
								</div>
								{/* Info */}
								<div style={{ flex: 1, "min-width": 0 }}>
									<div style={{ display: "flex", "align-items": "center", gap: "6px", "flex-wrap": "wrap" }}>
										<span style={{ "font-weight": 500, "font-size": "13px" }}>{server.name}</span>
										<span
											style={{
												"font-size": "10px",
												padding: "1px 5px",
												"border-radius": "3px",
												background:
													server.transport.type === "http" ? "rgba(97,175,239,0.15)" : "rgba(152,195,121,0.15)",
												color: server.transport.type === "http" ? "#61afef" : "#98c379",
											}}
										>
											{server.transport.type.toUpperCase()}
										</span>
										{/* Status dot + label */}
										<Show when={st()}>
											{(entry) => (
												<>
													<span
														style={{
															display: "inline-block",
															width: "7px",
															height: "7px",
															"border-radius": "50%",
															background: statusColor(entry().status),
														}}
														title={statusLabel(entry().status)}
													/>
													<Show when={entry().status === "authenticating" || entry().status === "needs_auth"}>
														<span style={{ "font-size": "11px", color: statusColor(entry().status) }}>
															{statusLabel(entry().status)}
														</span>
													</Show>
												</>
											)}
										</Show>
										<Show when={!server.enabled}>
											<span
												style={{
													"font-size": "10px",
													padding: "1px 5px",
													"border-radius": "3px",
													background: "rgba(255,255,255,0.05)",
													color: "var(--text-dimmed)",
												}}
											>
												Disabled
											</span>
										</Show>
									</div>
									<div
										class={s.hint}
										style={{
											margin: 0,
											"font-family": "monospace",
											"font-size": "11px",
											overflow: "hidden",
											"text-overflow": "ellipsis",
											"white-space": "nowrap",
										}}
									>
										{server.transport.type === "http"
											? server.transport.url
											: server.transport.command +
												(server.transport.args?.length ? " " + server.transport.args.join(" ") : "")}
									</div>
									{/* Metrics line */}
									<Show when={st()?.metrics}>
										{(m) => (
											<div class={s.hint} style={{ margin: 0, "font-size": "11px" }}>
												{st()!.tool_count} tools · {m().call_count} calls · {m().error_count} errors
												{m().last_latency_ms > 0 ? ` · ${m().last_latency_ms}ms` : ""}
											</div>
										)}
									</Show>
								</div>
								{/* Action buttons — never shrink */}
								{/* Authorize — show for explicit OAuth2 config OR when server auto-detected needs_auth (DCR case) */}
								<Show
									when={
										server.auth?.type === "oauth2" || st()?.status === "needs_auth" || st()?.status === "authenticating"
									}
								>
									<Show
										when={st()?.status === "authenticating"}
										fallback={
											<button
												class={s.copyBtn}
												style={{
													width: "auto",
													padding: "0 10px",
													"flex-shrink": 0,
													color: st()?.status === "needs_auth" ? "var(--warning, #e5c07b)" : "var(--info, #61afef)",
													"font-weight": st()?.status === "needs_auth" ? "600" : undefined,
												}}
												title={
													st()?.status === "needs_auth"
														? "Upstream requires authorization — click to open the provider's consent page"
														: "Authorize via OAuth 2.1"
												}
												onClick={() => {
													startAuthorizeFlow(server.name).catch((e) => appLogger.warn("network", String(e)));
												}}
											>
												Authorize
											</button>
										}
									>
										<button
											class={s.copyBtn}
											style={{ width: "auto", padding: "0 10px", "flex-shrink": 0, color: "var(--warning, #e5c07b)" }}
											title="Cancel authorization"
											onClick={() =>
												rpc("cancel_mcp_upstream_oauth", { name: server.name }).catch((e) =>
													appLogger.warn("network", String(e)),
												)
											}
										>
											Cancel
										</button>
									</Show>
								</Show>
								<button
									class={s.copyBtn}
									style={{ "flex-shrink": 0 }}
									title="Edit"
									onClick={() => (isEditing() ? setEditingId(null) : startEdit(server))}
								>
									<svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor">
										<path d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293z" />
									</svg>
								</button>
								{/* Reconnect */}
								<button
									class={s.copyBtn}
									style={{ "flex-shrink": 0 }}
									title="Reconnect"
									onClick={() =>
										rpc("reconnect_mcp_upstream", { name: server.name }).catch((e) =>
											appLogger.warn("network", String(e)),
										)
									}
								>
									<svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor">
										<path d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2z" />
										<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466" />
									</svg>
								</button>
								{/* Remove */}
								<button
									class={s.copyBtn}
									title="Remove"
									onClick={() => removeUpstream(server.id, server.name)}
									style={{ color: "var(--error, #e06c75)", "flex-shrink": 0 }}
								>
									<svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor">
										<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0z" />
										<path d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4zM2.5 3h11V2h-11z" />
									</svg>
								</button>
							</div>
							{/* Edit inline panel */}
							<Show when={isEditing()}>
								<div
									style={{
										background: "var(--bg-secondary, rgba(255,255,255,0.03))",
										padding: "12px",
										"border-radius": "6px",
										"margin-bottom": "8px",
									}}
								>
									<div style={{ display: "grid", gap: "8px" }}>
										<Show when={editForm().transportType === "http"}>
											<div>
												<label style={{ "font-size": "12px", color: "var(--text-dimmed)" }}>URL</label>
												<input
													type="text"
													class={s.input}
													value={editForm().url}
													onInput={(e) => setEditForm((f) => ({ ...f, url: e.currentTarget.value }))}
												/>
											</div>
											<div>
												<label style={{ "font-size": "12px", color: "var(--text-dimmed)" }}>Authentication</label>
												<select
													class={s.input}
													value={editForm().authMethod}
													onChange={(e) =>
														setEditForm((f) => ({ ...f, authMethod: e.currentTarget.value as "bearer" | "oauth2" }))
													}
												>
													<option value="bearer">Bearer token</option>
													<option value="oauth2">OAuth 2.1</option>
												</select>
											</div>
											<Show when={editForm().authMethod === "bearer"}>
												<div>
													<label style={{ "font-size": "12px", color: "var(--text-dimmed)" }}>Bearer token</label>
													<input
														type="password"
														class={s.input}
														placeholder="Enter new token (leave blank to keep current)"
														value={editForm().credential}
														onInput={(e) => setEditForm((f) => ({ ...f, credential: e.currentTarget.value }))}
													/>
													<p class={s.hint} style={{ margin: "2px 0 0" }}>
														Stored in OS keychain. Leave blank to keep current token.
													</p>
												</div>
											</Show>
											<Show when={editForm().authMethod === "oauth2"}>
												<div>
													<label style={{ "font-size": "12px", color: "var(--text-dimmed)" }}>OAuth client ID</label>
													<input
														type="text"
														class={s.input}
														placeholder="Leave blank to auto-register"
														value={editForm().oauthClientId}
														onInput={(e) => setEditForm((f) => ({ ...f, oauthClientId: e.currentTarget.value }))}
													/>
												</div>
												<div>
													<label style={{ "font-size": "12px", color: "var(--text-dimmed)" }}>Client Secret</label>
													<input
														type="password"
														class={s.input}
														placeholder="Optional, for confidential clients"
														value={editForm().oauthClientSecret}
														onInput={(e) => setEditForm((f) => ({ ...f, oauthClientSecret: e.currentTarget.value }))}
													/>
												</div>
												<div>
													<label style={{ "font-size": "12px", color: "var(--text-dimmed)" }}>Scopes</label>
													<input
														type="text"
														class={s.input}
														placeholder="Optional, space-separated"
														value={editForm().oauthScopes}
														onInput={(e) => setEditForm((f) => ({ ...f, oauthScopes: e.currentTarget.value }))}
													/>
												</div>
											</Show>
										</Show>
										<Show when={editForm().transportType === "stdio"}>
											<div>
												<label style={{ "font-size": "12px", color: "var(--text-dimmed)" }}>Command</label>
												<input
													type="text"
													class={s.input}
													value={editForm().command}
													onInput={(e) => setEditForm((f) => ({ ...f, command: e.currentTarget.value }))}
												/>
											</div>
											<div>
												<label style={{ "font-size": "12px", color: "var(--text-dimmed)" }}>Args</label>
												<input
													type="text"
													class={s.input}
													value={editForm().args}
													onInput={(e) => setEditForm((f) => ({ ...f, args: e.currentTarget.value }))}
												/>
											</div>
											<div>
												<label style={{ "font-size": "12px", color: "var(--text-dimmed)" }}>Working directory</label>
												<input
													type="text"
													class={s.input}
													value={editForm().cwd}
													placeholder="Optional"
													onInput={(e) => setEditForm((f) => ({ ...f, cwd: e.currentTarget.value }))}
												/>
											</div>
										</Show>
										<div style={{ display: "flex", gap: "8px", "align-items": "center" }}>
											<label style={{ "font-size": "12px", color: "var(--text-dimmed)" }}>Timeout (s):</label>
											<input
												type="number"
												class={s.input}
												value={editForm().timeout}
												min={0}
												max={300}
												style={{ width: "70px" }}
												onInput={(e) =>
													setEditForm((f) => ({ ...f, timeout: parseInt(e.currentTarget.value, 10) || 30 }))
												}
											/>
										</div>
										{/* Discovered tools */}
										<Show when={st()?.tools?.length}>
											<div>
												<label style={{ "font-size": "12px", color: "var(--text-dimmed)" }}>
													Discovered tools ({st()!.tools.length})
												</label>
												<div
													style={{
														"font-family": "monospace",
														"font-size": "11px",
														color: "var(--text-dimmed)",
														"margin-top": "4px",
														"line-height": "1.6",
													}}
												>
													{st()!.tools.join(", ")}
												</div>
											</div>
										</Show>
										<div style={{ display: "flex", gap: "8px", "justify-content": "flex-end" }}>
											<button class={s.saveBtn} onClick={() => saveEdit(server)} disabled={saving()}>
												{saving() ? "Saving…" : "Save"}
											</button>
											<button
												class={s.testBtn}
												onClick={() => {
													setEditingId(null);
													setError("");
												}}
											>
												Cancel
											</button>
										</div>
										<Show when={error()}>
											<p class={s.hint} style={{ color: "var(--error, #e06c75)", margin: "4px 0 0" }}>
												{error()}
											</p>
										</Show>
									</div>
								</div>
							</Show>
						</div>
					);
				}}
			</For>
		</div>
	);
};

// ---------------------------------------------------------------------------
// Remote Machines panel
// ---------------------------------------------------------------------------

/** Status dot color for remote connection state */
function remoteStatusColor(status: string): string {
	switch (status) {
		case "connected":
			return "var(--accent-green, #22c55e)";
		case "connecting":
			return "var(--fg-warning, #e5a100)";
		case "error":
			return "var(--accent-red, #ef4444)";
		default:
			return "var(--fg-muted)";
	}
}

/** Human-readable label for remote connection status */
function remoteStatusLabel(status: string): string {
	switch (status) {
		case "connected":
			return "Connected";
		case "connecting":
			return "Connecting...";
		case "error":
			return "Error";
		default:
			return "Disconnected";
	}
}

/** Transport summary string */
function transportSummary(transport: RemoteTransport): string {
	if (transport.type === "Ssh") {
		return `${transport.ssh_user}@${transport.ssh_host}:${transport.ssh_port}`;
	}
	return transport.url;
}

/** Blank form state for adding/editing a remote machine */
function emptyRemoteForm() {
	return {
		name: "",
		transportType: "Ssh" as "Ssh" | "Direct",
		sshHost: "",
		sshPort: 22,
		sshUser: "",
		identityFile: "",
		remoteDaemonPort: 9876,
		directUrl: "",
		authUsername: "",
	};
}

const RemoteMachinesPanel: Component = () => {
	const [showAdd, setShowAdd] = createSignal(false);
	const [form, setForm] = createSignal(emptyRemoteForm());
	const [saving, setSaving] = createSignal(false);
	const [error, setError] = createSignal("");
	const [editingId, setEditingId] = createSignal<string | null>(null);
	const [editForm, setEditForm] = createSignal(emptyRemoteForm());

	onMount(() => {
		remoteConnectionsStore.hydrate();
	});

	function connectionList(): ConnectionState[] {
		const conns = remoteConnectionsStore.getConnections();
		return Object.values(conns);
	}

	async function addConnection() {
		const f = form();
		const name = f.name.trim();
		if (!name) {
			setError("Name is required");
			return;
		}
		if (f.transportType === "Ssh" && !f.sshHost.trim()) {
			setError("SSH host is required");
			return;
		}
		if (f.transportType === "Direct" && !f.directUrl.trim()) {
			setError("URL is required");
			return;
		}

		const transport: RemoteTransport =
			f.transportType === "Ssh"
				? {
						type: "Ssh",
						ssh_host: f.sshHost.trim(),
						ssh_port: f.sshPort,
						ssh_user: f.sshUser.trim(),
						identity_file: f.identityFile.trim() || null,
						remote_daemon_port: f.remoteDaemonPort,
					}
				: { type: "Direct", url: f.directUrl.trim() };

		const conn: RemoteConnection = {
			id: crypto.randomUUID(),
			name,
			transport,
			auth_username: f.authUsername.trim(),
			enabled: true,
		};

		setSaving(true);
		setError("");
		try {
			await remoteConnectionsStore.addConnection(conn);
			setForm(emptyRemoteForm());
			setShowAdd(false);
		} catch (e) {
			setError(String(e));
		} finally {
			setSaving(false);
		}
	}

	function startEdit(conn: RemoteConnection) {
		setEditingId(conn.id);
		setEditForm({
			name: conn.name,
			transportType: conn.transport.type,
			sshHost: conn.transport.type === "Ssh" ? conn.transport.ssh_host : "",
			sshPort: conn.transport.type === "Ssh" ? conn.transport.ssh_port : 22,
			sshUser: conn.transport.type === "Ssh" ? conn.transport.ssh_user : "",
			identityFile: conn.transport.type === "Ssh" ? (conn.transport.identity_file ?? "") : "",
			remoteDaemonPort: conn.transport.type === "Ssh" ? conn.transport.remote_daemon_port : 9876,
			directUrl: conn.transport.type === "Direct" ? conn.transport.url : "",
			authUsername: conn.auth_username,
		});
	}

	async function saveEdit(connState: ConnectionState) {
		const f = editForm();
		const transport: RemoteTransport =
			f.transportType === "Ssh"
				? {
						type: "Ssh",
						ssh_host: f.sshHost.trim(),
						ssh_port: f.sshPort,
						ssh_user: f.sshUser.trim(),
						identity_file: f.identityFile.trim() || null,
						remote_daemon_port: f.remoteDaemonPort,
					}
				: { type: "Direct", url: f.directUrl.trim() };

		const updated: RemoteConnection = {
			...connState.connection,
			name: f.name.trim(),
			transport,
			auth_username: f.authUsername.trim(),
		};

		setSaving(true);
		setError("");
		try {
			// Disconnect first if connected, then save the updated connection
			if (connState.status === "connecting" || connState.status === "connected") {
				await remoteConnectionsStore.disconnect(connState.connection.id);
			}
			await remoteConnectionsStore.addConnection(updated);
			setEditingId(null);
		} catch (e) {
			setError(String(e));
		} finally {
			setSaving(false);
		}
	}

	async function removeConnection(id: string, name: string) {
		let confirmed: boolean;
		try {
			const { confirm } = await import("@tauri-apps/plugin-dialog");
			confirmed = await confirm(`Remove remote machine "${name}"?`, {
				title: "Remove remote machine",
				kind: "warning",
			});
		} catch {
			confirmed = window.confirm(`Remove remote machine "${name}"?`);
		}
		if (!confirmed) return;
		try {
			await remoteConnectionsStore.removeConnection(id);
		} catch (e) {
			appLogger.error("settings", "Failed to remove remote connection", { error: String(e) });
		}
	}

	/** Render transport-specific form fields */
	function TransportFields(props: {
		formData: ReturnType<typeof emptyRemoteForm>;
		setFormData: (updater: (f: ReturnType<typeof emptyRemoteForm>) => ReturnType<typeof emptyRemoteForm>) => void;
	}) {
		return (
			<>
				<select
					class={s.input}
					value={props.formData.transportType}
					onChange={(e) =>
						props.setFormData((f) => ({ ...f, transportType: e.currentTarget.value as "Ssh" | "Direct" }))
					}
				>
					<option value="Ssh">SSH</option>
					<option value="Direct">Direct</option>
				</select>
				<Show when={props.formData.transportType === "Ssh"}>
					<input
						type="text"
						class={s.input}
						placeholder="Host (e.g. 192.168.1.100)"
						value={props.formData.sshHost}
						onInput={(e) => props.setFormData((f) => ({ ...f, sshHost: e.currentTarget.value }))}
					/>
					<div style={{ display: "flex", gap: "8px" }}>
						<div style={{ flex: 1 }}>
							<label style={{ "font-size": "12px", color: "var(--text-dimmed)" }}>Port</label>
							<input
								type="number"
								class={s.input}
								value={props.formData.sshPort}
								min={1}
								max={65535}
								onInput={(e) =>
									props.setFormData((f) => ({ ...f, sshPort: parseInt(e.currentTarget.value, 10) || 22 }))
								}
							/>
						</div>
						<div style={{ flex: 2 }}>
							<label style={{ "font-size": "12px", color: "var(--text-dimmed)" }}>User</label>
							<input
								type="text"
								class={s.input}
								placeholder="SSH user"
								value={props.formData.sshUser}
								onInput={(e) => props.setFormData((f) => ({ ...f, sshUser: e.currentTarget.value }))}
							/>
						</div>
					</div>
					<input
						type="text"
						class={s.input}
						placeholder="Identity file (optional, e.g. ~/.ssh/id_rsa)"
						value={props.formData.identityFile}
						onInput={(e) => props.setFormData((f) => ({ ...f, identityFile: e.currentTarget.value }))}
					/>
					<div style={{ display: "flex", gap: "8px", "align-items": "center" }}>
						<label style={{ "font-size": "12px", color: "var(--text-dimmed)", "white-space": "nowrap" }}>
							Remote daemon port
						</label>
						<input
							type="number"
							class={s.input}
							value={props.formData.remoteDaemonPort}
							min={1}
							max={65535}
							style={{ width: "90px" }}
							onInput={(e) =>
								props.setFormData((f) => ({ ...f, remoteDaemonPort: parseInt(e.currentTarget.value, 10) || 9876 }))
							}
						/>
					</div>
				</Show>
				<Show when={props.formData.transportType === "Direct"}>
					<input
						type="text"
						class={s.input}
						placeholder="URL (e.g. http://192.168.1.100:9876)"
						value={props.formData.directUrl}
						onInput={(e) => props.setFormData((f) => ({ ...f, directUrl: e.currentTarget.value }))}
					/>
				</Show>
				<input
					type="text"
					class={s.input}
					placeholder="Auth username (for TUIC API authentication)"
					value={props.formData.authUsername}
					onInput={(e) => props.setFormData((f) => ({ ...f, authUsername: e.currentTarget.value }))}
				/>
			</>
		);
	}

	return (
		<div style={{ "margin-top": "24px", "border-top": "1px solid var(--border)", "padding-top": "16px" }}>
			<div class={s.group}>
				<label style={{ display: "flex", "align-items": "center", gap: "8px", "justify-content": "space-between" }}>
					<span>Remote Machines</span>
					<button
						class={s.copyBtn}
						onClick={() => {
							setShowAdd((v) => !v);
							setError("");
						}}
						title="Add remote machine"
						style={{ "font-size": "18px", "line-height": 1 }}
					>
						{showAdd() ? "−" : "+"}
					</button>
				</label>
				<p class={s.hint}>
					Connect to TUIC instances running on other machines. SSH connections create an encrypted tunnel automatically.
				</p>
			</div>

			{/* Add form */}
			<Show when={showAdd()}>
				<div
					class={s.group}
					style={{ background: "var(--bg-secondary, rgba(255,255,255,0.03))", padding: "12px", "border-radius": "6px" }}
				>
					<div style={{ display: "grid", gap: "8px" }}>
						<input
							type="text"
							class={s.input}
							placeholder="Name (e.g. dev-server, staging)"
							value={form().name}
							onInput={(e) => setForm((f) => ({ ...f, name: e.currentTarget.value }))}
						/>
						<TransportFields formData={form()} setFormData={setForm} />
						<div style={{ display: "flex", gap: "8px", "justify-content": "flex-end" }}>
							<button class={s.copyBtn} onClick={addConnection} disabled={saving()}>
								{saving() ? "Saving..." : "Save"}
							</button>
							<button
								class={s.copyBtn}
								onClick={() => {
									setShowAdd(false);
									setForm(emptyRemoteForm());
									setError("");
								}}
							>
								Cancel
							</button>
						</div>
						<Show when={error()}>
							<p class={s.hint} style={{ color: "var(--error, #e06c75)" }}>
								{error()}
							</p>
						</Show>
					</div>
				</div>
			</Show>

			{/* Empty state */}
			<Show when={connectionList().length === 0 && !showAdd()}>
				<p class={s.hint} style={{ color: "var(--text-dimmed)" }}>
					No remote machines configured. Click <strong>+</strong> to add one.
				</p>
			</Show>

			{/* Connection list */}
			<For each={connectionList()}>
				{(connState) => {
					const conn = () => connState.connection;
					const isEditing = () => editingId() === conn().id;
					return (
						<div style={{ "border-bottom": "1px solid var(--border-subtle, rgba(255,255,255,0.06))" }}>
							<div class={s.group} style={{ display: "flex", "align-items": "center", gap: "8px", padding: "8px 0" }}>
								{/* Status dot */}
								<span
									style={{
										display: "inline-block",
										width: "8px",
										height: "8px",
										"border-radius": "50%",
										background: remoteStatusColor(connState.status),
										"flex-shrink": "0",
									}}
									title={remoteStatusLabel(connState.status)}
								/>
								{/* Info */}
								<div style={{ flex: 1, "min-width": 0 }}>
									<div style={{ display: "flex", "align-items": "center", gap: "6px", "flex-wrap": "wrap" }}>
										<span style={{ "font-weight": 500, "font-size": "13px" }}>{conn().name}</span>
										<span
											style={{
												"font-size": "10px",
												padding: "1px 5px",
												"border-radius": "3px",
												background:
													conn().transport.type === "Ssh" ? "rgba(97,175,239,0.15)" : "rgba(152,195,121,0.15)",
												color: conn().transport.type === "Ssh" ? "#61afef" : "#98c379",
											}}
										>
											{conn().transport.type === "Ssh" ? "SSH" : "DIRECT"}
										</span>
										<Show when={connState.status !== "disconnected"}>
											<span style={{ "font-size": "11px", color: remoteStatusColor(connState.status) }}>
												{remoteStatusLabel(connState.status)}
											</span>
										</Show>
									</div>
									<div
										class={s.hint}
										style={{
											margin: 0,
											"font-family": "monospace",
											"font-size": "11px",
											overflow: "hidden",
											"text-overflow": "ellipsis",
											"white-space": "nowrap",
										}}
									>
										{transportSummary(conn().transport)}
									</div>
									<Show when={connState.error}>
										<div class={s.hint} style={{ margin: 0, "font-size": "11px", color: "var(--accent-red, #ef4444)" }}>
											{connState.error}
										</div>
									</Show>
								</div>
								{/* Connect / Disconnect */}
								<button
									class={s.copyBtn}
									style={{ "flex-shrink": 0, "white-space": "nowrap" }}
									onClick={() => {
										if (connState.status === "connected" || connState.status === "connecting") {
											remoteConnectionsStore.disconnect(conn().id);
										} else {
											remoteConnectionsStore.connect(conn().id);
										}
									}}
								>
									{connState.status === "connected" || connState.status === "connecting" ? "Disconnect" : "Connect"}
								</button>
								{/* Edit */}
								<button
									class={s.copyBtn}
									style={{ "flex-shrink": 0 }}
									title="Edit"
									onClick={() => (isEditing() ? setEditingId(null) : startEdit(conn()))}
								>
									<svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor">
										<path d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293z" />
									</svg>
								</button>
								{/* Delete */}
								<button
									class={s.copyBtn}
									title="Remove"
									onClick={() => removeConnection(conn().id, conn().name)}
									style={{ color: "var(--error, #e06c75)", "flex-shrink": 0 }}
								>
									<svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor">
										<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0z" />
										<path d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4zM2.5 3h11V2h-11z" />
									</svg>
								</button>
							</div>
							{/* Edit inline panel */}
							<Show when={isEditing()}>
								<div
									style={{
										background: "var(--bg-secondary, rgba(255,255,255,0.03))",
										padding: "12px",
										"border-radius": "6px",
										"margin-bottom": "8px",
									}}
								>
									<div style={{ display: "grid", gap: "8px" }}>
										<TransportFields formData={editForm()} setFormData={setEditForm} />
										<div style={{ display: "flex", gap: "8px", "justify-content": "flex-end" }}>
											<button class={s.copyBtn} onClick={() => saveEdit(connState)} disabled={saving()}>
												{saving() ? "Saving..." : "Save"}
											</button>
											<button
												class={s.copyBtn}
												onClick={() => {
													setEditingId(null);
													setError("");
												}}
											>
												Cancel
											</button>
										</div>
										<Show when={error()}>
											<p class={s.hint} style={{ color: "var(--error, #e06c75)", margin: "4px 0 0" }}>
												{error()}
											</p>
										</Show>
									</div>
								</div>
							</Show>
						</div>
					);
				}}
			</For>
		</div>
	);
};
