import { getErrorMessage } from "../lib/error-message.js";
import { useEffect, useRef, useState } from "react";
import type { HostData } from "@/types/index";
import { useTranslation } from "react-i18next";
import {
  ArrowUpDown,
  Check,
  ChevronsDownUp,
  ChevronsUpDown,
  Download,
  Filter,
  FolderPlus,
  GripVertical,
  Group,
  ListChecks,
  MoreHorizontal,
  Plus,
  RefreshCw,
  Search,
  Server,
  SlidersHorizontal,
  Upload,
  X,
} from "lucide-react";
import { toast } from "sonner";
import { SidebarTree, isFolder } from "@/sidebar/SidebarTree";
import { HostManager } from "@/sidebar/HostManager";
import { HostShareModal } from "@/sidebar/HostShareModal";
import { HostExportDialog } from "@/sidebar/HostExportDialog";
import { CustomizeSidebarPanel } from "@/sidebar/CustomizeSidebarPanel";
import { ProxmoxDiscoverDialog } from "@/components/proxmox/ProxmoxDiscoverDialog";
import { Button } from "@/components/button";
import {
  DropdownMenu,
  DropdownMenuCheckboxItem,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuSub,
  DropdownMenuSubContent,
  DropdownMenuSubTrigger,
  DropdownMenuTrigger,
} from "@/components/dropdown-menu";
import {
  getSSHHosts,
  bulkImportSSHHosts,
  importSSHConfigHosts,
} from "@/main-axios";
import type { SSHHostWithStatus } from "@/main-axios";
import type { Host, HostFolder, TabType } from "@/types/ui-types";
import { sortHostTree, type SortKey } from "@/sidebar/host-sort";
import { useHostSidebarPreferences } from "@/sidebar/tree/hooks/useHostSidebarPreferences";
import { useArrangeLock } from "@/sidebar/use-arrange-lock";
import type {
  HostGroupKey,
  HostSidebarFilterState,
} from "@/types/host-sidebar-preferences";

type FilterState = HostSidebarFilterState;

const DEFAULT_FILTERS: FilterState = {
  status: [],
  authType: [],
  protocol: [],
  features: [],
  tags: [],
};

type GroupKey = HostGroupKey;

function flattenHosts(folder: HostFolder): Host[] {
  const out: Host[] = [];
  for (const child of folder.children) {
    if (isFolder(child)) out.push(...flattenHosts(child));
    else out.push(child);
  }
  return out;
}

function hostGroupNames(host: Host, key: GroupKey): string[] {
  switch (key) {
    case "tag":
      return host.tags && host.tags.length > 0 ? host.tags : ["__none__"];
    case "status":
      return [host.online ? "online" : "offline"];
    case "protocol": {
      const protos: string[] = [];
      if (host.enableSsh) protos.push("ssh");
      if (host.enableRdp) protos.push("rdp");
      if (host.enableVnc) protos.push("vnc");
      if (host.enableTelnet) protos.push("telnet");
      return protos.length > 0 ? protos : ["__none__"];
    }
    case "auth":
      return [host.authType || "none"];
    default:
      return ["__none__"];
  }
}

function groupHosts(
  tree: HostFolder,
  key: GroupKey,
  labelFor: (key: GroupKey, group: string) => string,
): HostFolder {
  if (key === "folder") return tree;
  const hosts = flattenHosts(tree);
  const groups = new Map<string, Host[]>();
  for (const host of hosts) {
    for (const name of hostGroupNames(host, key)) {
      if (!groups.has(name)) groups.set(name, []);
      groups.get(name)!.push(host);
    }
  }
  const children: (Host | HostFolder)[] = [...groups.entries()]
    .sort((a, b) => labelFor(key, a[0]).localeCompare(labelFor(key, b[0])))
    .map(([group, members]) => ({
      name: labelFor(key, group),
      path: `__group__:${key}:${group}`,
      children: members,
    }));
  return { name: "root", children };
}

function hostPassesFilters(host: Host, filters: FilterState): boolean {
  if (filters.status.length > 0) {
    const ok =
      (filters.status.includes("online") && host.online) ||
      (filters.status.includes("offline") &&
        host.status !== "reachable" &&
        !host.online) ||
      (filters.status.includes("pinned") && !!host.pin);
    if (!ok) return false;
  }
  if (filters.authType.length > 0) {
    if (
      !filters.authType.includes(
        host.authType as FilterState["authType"][number],
      )
    )
      return false;
  }
  if (filters.protocol.length > 0) {
    const ok =
      (filters.protocol.includes("ssh") && host.enableSsh) ||
      (filters.protocol.includes("rdp") && host.enableRdp) ||
      (filters.protocol.includes("vnc") && host.enableVnc) ||
      (filters.protocol.includes("telnet") && host.enableTelnet);
    if (!ok) return false;
  }
  if (filters.features.length > 0) {
    const ok =
      (filters.features.includes("terminal") && host.enableTerminal) ||
      (filters.features.includes("fileManager") && host.enableFileManager) ||
      (filters.features.includes("tunnel") && host.enableTunnel) ||
      (filters.features.includes("docker") && host.enableDocker);
    if (!ok) return false;
  }
  if (filters.tags.length > 0) {
    const ok = filters.tags.some((tag) => host.tags?.includes(tag));
    if (!ok) return false;
  }
  return true;
}

function applyFilters(folder: HostFolder, filters: FilterState): HostFolder {
  const active = Object.values(filters).some((arr) => arr.length > 0);
  if (!active) return folder;

  const filteredChildren = folder.children
    .map((child) => {
      if (isFolder(child)) return applyFilters(child, filters);
      return hostPassesFilters(child, filters) ? child : null;
    })
    .filter((child): child is Host | HostFolder => {
      if (child === null) return false;
      if (isFolder(child)) return child.children.length > 0;
      return true;
    });
  return { ...folder, children: filteredChildren };
}

export function HostsPanel({
  onOpenTab,
  onEditHost,
  hostTree,
  loading,
  onEditingChange,
  active = true,
}: {
  onOpenTab: (host: Host, type: TabType) => void;
  onEditHost: (host: Host) => void;
  hostTree?: HostFolder;
  loading?: boolean;
  onEditingChange?: (editing: boolean) => void;
  active?: boolean;
}) {
  const { t } = useTranslation();
  const [hostSearch, setHostSearch] = useState("");
  const [managerEditing, setManagerEditing] = useState(false);
  const [customizePanelOpen, setCustomizePanelOpen] = useState(false);
  const [selectionMode, setSelectionMode] = useState(false);
  const [refreshing, setRefreshing] = useState(false);
  const [rawHosts, setRawHosts] = useState<SSHHostWithStatus[]>([]);
  const [shareModalHost, setShareModalHost] = useState<Host | null>(null);
  const [exportDialogOpen, setExportDialogOpen] = useState(false);
  const [exportPreselection, setExportPreselection] = useState<Set<string>>(
    new Set(),
  );
  const [proxmoxDialogOpen, setProxmoxDialogOpen] = useState(false);
  const [proxmoxHostId, setProxmoxHostId] = useState<number | undefined>(
    undefined,
  );
  const [proxmoxDefaultCredentialId, setProxmoxDefaultCredentialId] = useState<
    number | null
  >(null);
  const [proxmoxDefaultAuthType, setProxmoxDefaultAuthType] = useState<
    string | undefined
  >(undefined);
  const [proxmoxDefaultUsername, setProxmoxDefaultUsername] = useState<
    string | undefined
  >(undefined);
  const { preferences: sidebarPrefs, update: updateSidebarPrefs } =
    useHostSidebarPreferences();
  const sortKey = sidebarPrefs.sort.key;
  const pinnedFirst = sidebarPrefs.sort.pinnedFirst;
  const { arrangeLocked, toggleArrangeLock } = useArrangeLock(
    "hostSidebarArrangeLocked",
  );
  const groupKey = sidebarPrefs.groupKey;
  const filterState = sidebarPrefs.filters;
  const filterActive = Object.values(filterState).some((arr) => arr.length > 0);
  const fileInputRef = useRef<HTMLInputElement>(null);
  const sshConfigInputRef = useRef<HTMLInputElement>(null);
  const importOverwriteRef = useRef(false);
  const allTags = [...new Set(rawHosts.flatMap((h) => h.tags ?? []))];

  function handleSortChange(key: SortKey) {
    updateSidebarPrefs((prev) => ({
      ...prev,
      sort: { ...prev.sort, key },
    }));
  }

  function handleArrangeLockToggle() {
    const unlocking = arrangeLocked;
    toggleArrangeLock();
    // Reordering under name/IP/status sort is pointless -- the tree would
    // just re-sort the row away from where it was dropped.
    if (unlocking && sortKey !== "manual") {
      handleSortChange("manual");
      toast.info(t("hosts.arrangeUnlockedSwitchedToManual"));
    }
  }

  function handlePinnedFirstChange(enabled: boolean) {
    updateSidebarPrefs((prev) => ({
      ...prev,
      sort: { ...prev.sort, pinnedFirst: enabled },
    }));
  }

  function handleGroupChange(key: GroupKey) {
    updateSidebarPrefs((prev) => ({ ...prev, groupKey: key }));
  }

  function groupLabel(key: GroupKey, group: string): string {
    if (group === "__none__") return t("hosts.groupUngrouped");
    if (key === "status")
      return group === "online"
        ? t("hosts.filterOnline")
        : t("hosts.filterOffline");
    if (key === "protocol") return group.toUpperCase();
    if (key === "auth")
      return t(
        `hosts.filterAuth${group.charAt(0).toUpperCase() + group.slice(1)}`,
      );
    return group;
  }

  function handleFilterToggle<K extends keyof FilterState>(
    group: K,
    value: FilterState[K][number],
  ) {
    updateSidebarPrefs((prev) => {
      const arr = prev.filters[group] as string[];
      const next = arr.includes(value as string)
        ? arr.filter((v) => v !== value)
        : [...arr, value as string];
      return { ...prev, filters: { ...prev.filters, [group]: next } };
    });
  }

  function handleFilterClear() {
    updateSidebarPrefs((prev) => ({ ...prev, filters: DEFAULT_FILTERS }));
  }

  useEffect(() => {
    let cancelled = false;
    const load = () => {
      getSSHHosts()
        .then((hosts) => {
          if (!cancelled) setRawHosts(hosts);
        })
        .catch(() => {});
    };
    load();
    const onChanged = () => load();
    window.addEventListener("ssh-hosts:changed", onChanged);
    window.addEventListener("hosts:refresh", onChanged);
    window.addEventListener("termix:hosts-changed", onChanged);
    return () => {
      cancelled = true;
      window.removeEventListener("ssh-hosts:changed", onChanged);
      window.removeEventListener("hosts:refresh", onChanged);
      window.removeEventListener("termix:hosts-changed", onChanged);
    };
  }, []);

  function handleEditingChange(editing: boolean) {
    setManagerEditing(editing);
    onEditingChange?.(editing);
  }

  function toggleSelectionMode() {
    setSelectionMode((v) => !v);
  }

  async function handleRefresh() {
    setRefreshing(true);
    try {
      const hosts = await getSSHHosts();
      setRawHosts(hosts);
      window.dispatchEvent(new CustomEvent("termix:hosts-changed"));
    } catch {
      // best-effort
    } finally {
      setRefreshing(false);
    }
  }

  function handleDownloadSample() {
    const sample = JSON.stringify(
      {
        hosts: [
          {
            name: "Web Server (Production)",
            ip: "192.168.1.100",
            username: "admin",
            authType: "password",
            password: "your_secure_password_here",
            folder: "Production",
            tags: ["web", "production", "nginx"],
            pin: true,
            notes: "Main production web server running Nginx",
            enableSsh: true,
            enableRdp: false,
            enableVnc: false,
            enableTelnet: false,
            sshPort: 22,
            enableTerminal: true,
            enableTunnel: false,
            enableFileManager: true,
            enableDocker: false,
            defaultPath: "/var/www",
          },
          {
            name: "Database Server",
            ip: "192.168.1.101",
            username: "dbadmin",
            authType: "key",
            key: "-----BEGIN OPENSSH PRIVATE KEY-----\nYour SSH private key content here\n-----END OPENSSH PRIVATE KEY-----",
            keyPassword: "optional_key_passphrase",
            keyType: "ssh-ed25519",
            folder: "Production",
            tags: ["database", "production", "postgresql"],
            enableSsh: true,
            enableRdp: false,
            enableVnc: false,
            enableTelnet: false,
            sshPort: 22,
            enableTerminal: true,
            enableTunnel: true,
            enableFileManager: false,
            enableDocker: false,
          },
        ],
      },
      null,
      2,
    );
    const blob = new Blob([sample], { type: "application/json" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = "termix-hosts-sample.json";
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
  }

  return (
    <div className="relative flex flex-col flex-1 min-h-0 overflow-hidden">
      {!managerEditing && (
        <div className="flex flex-col px-2 py-1.5 shrink-0 border-b border-border/60 gap-1.5">
          <div className="flex items-center gap-2 px-2.5 h-7 bg-muted/60 border border-border/60 rounded-none">
            <Search className="size-3 text-muted-foreground/60 shrink-0" />
            <input
              value={hostSearch}
              onChange={(e) => setHostSearch(e.target.value)}
              placeholder={t("hosts.searchHosts")}
              className="flex-1 text-xs bg-transparent outline-none placeholder:text-muted-foreground/50 text-foreground min-w-0"
            />
            {hostSearch && (
              <button
                onClick={() => setHostSearch("")}
                className="text-muted-foreground/60 hover:text-muted-foreground transition-colors"
              >
                <X className="size-3" />
              </button>
            )}
          </div>

          <input
            ref={fileInputRef}
            type="file"
            accept=".json"
            className="hidden"
            onChange={async (e) => {
              const file = e.target.files?.[0];
              if (!file) return;
              e.target.value = "";
              try {
                const text = await file.text();
                const parsed = JSON.parse(text);
                const hostsArray = Array.isArray(parsed)
                  ? parsed
                  : (parsed.hosts ?? []);
                const credentialsArray =
                  !Array.isArray(parsed) && Array.isArray(parsed.credentials)
                    ? parsed.credentials
                    : undefined;
                if (!Array.isArray(hostsArray) || hostsArray.length === 0) {
                  toast.error("No hosts found in file");
                  return;
                }
                if (hostsArray.length > 100) {
                  toast.error("Cannot import more than 100 hosts at once");
                  return;
                }
                const normalized = hostsArray.map(
                  (h: Record<string, unknown>) => ({
                    ...h,
                    port: h.port ?? h.sshPort ?? 22,
                    enableSsh: h.enableSsh ?? h.connectionType === "ssh",
                    enableRdp: h.enableRdp ?? h.connectionType === "rdp",
                    enableVnc: h.enableVnc ?? h.connectionType === "vnc",
                    enableTelnet:
                      h.enableTelnet ?? h.connectionType === "telnet",
                  }),
                );
                const result = await bulkImportSSHHosts(
                  normalized as unknown as HostData[],
                  importOverwriteRef.current,
                  credentialsArray,
                );
                const hosts = await getSSHHosts();
                setRawHosts(hosts);
                window.dispatchEvent(new CustomEvent("termix:hosts-changed"));
                const msg = [
                  result.success ? `${result.success} imported` : null,
                  result.updated ? `${result.updated} updated` : null,
                  result.failed ? `${result.failed} failed` : null,
                ]
                  .filter(Boolean)
                  .join(", ");
                toast.success(`Import complete: ${msg}`);
              } catch (err: unknown) {
                toast.error(getErrorMessage(err, "Failed to import hosts"));
              }
            }}
          />

          <input
            ref={sshConfigInputRef}
            type="file"
            accept=".conf,.config,*"
            className="hidden"
            onChange={async (e) => {
              const file = e.target.files?.[0];
              if (!file) return;
              e.target.value = "";
              try {
                const text = await file.text();
                const result = await importSSHConfigHosts(
                  text,
                  importOverwriteRef.current,
                );
                const hosts = await getSSHHosts();
                setRawHosts(hosts);
                window.dispatchEvent(new CustomEvent("termix:hosts-changed"));
                const msg = [
                  result.success ? `${result.success} imported` : null,
                  result.updated ? `${result.updated} updated` : null,
                  result.failed ? `${result.failed} failed` : null,
                ]
                  .filter(Boolean)
                  .join(", ");
                toast.success(`${t("hosts.importSSHConfig")}: ${msg}`);
              } catch (err: unknown) {
                toast.error(
                  getErrorMessage(err, "Failed to import SSH config"),
                );
              }
            }}
          />

          <div className="flex items-center gap-1.5 overflow-x-auto overflow-y-hidden toolbar-scrollbar">
            <div className="flex items-center border border-border shrink-0">
              <Button
                variant="ghost"
                size="icon"
                className="size-7 text-muted-foreground hover:text-foreground"
                title={t("hosts.refreshBtn2")}
                onClick={handleRefresh}
                disabled={refreshing}
              >
                <RefreshCw
                  className={`size-3.5 ${refreshing ? "animate-spin" : ""}`}
                />
              </Button>
              <div className="w-px self-stretch bg-border" />
              <DropdownMenu>
                <DropdownMenuTrigger asChild>
                  <Button
                    variant="ghost"
                    size="icon"
                    className="size-7 text-muted-foreground hover:text-foreground"
                    title={t("hosts.importExportBtn")}
                  >
                    <Upload className="size-3.5" />
                  </Button>
                </DropdownMenuTrigger>
                <DropdownMenuContent align="start" className="text-xs">
                  <DropdownMenuItem
                    onClick={() => {
                      importOverwriteRef.current = false;
                      fileInputRef.current?.click();
                    }}
                  >
                    <Upload className="size-3.5 mr-2" />
                    {t("hosts.importSkipExisting")}
                  </DropdownMenuItem>
                  <DropdownMenuItem
                    onClick={() => {
                      importOverwriteRef.current = true;
                      fileInputRef.current?.click();
                    }}
                  >
                    <Upload className="size-3.5 mr-2" />
                    {t("hosts.importOverwrite")}
                  </DropdownMenuItem>
                  <DropdownMenuItem
                    onClick={() => {
                      importOverwriteRef.current = false;
                      sshConfigInputRef.current?.click();
                    }}
                  >
                    <Upload className="size-3.5 mr-2" />
                    {t("hosts.importSSHConfig")}
                  </DropdownMenuItem>
                  <DropdownMenuItem
                    onClick={() => {
                      setProxmoxHostId(undefined);
                      setProxmoxDialogOpen(true);
                    }}
                    disabled={!rawHosts.some((h) => h.enableProxmox)}
                  >
                    <Server className="size-3.5 mr-2" />
                    {t("hosts.proxmoxImportTitle")}
                  </DropdownMenuItem>
                  <DropdownMenuSeparator />
                  <DropdownMenuItem
                    onClick={() => {
                      setExportPreselection(new Set());
                      setExportDialogOpen(true);
                    }}
                    disabled={rawHosts.length === 0}
                  >
                    <Download className="size-3.5 mr-2" />
                    {t("hosts.export.menuItem")}
                  </DropdownMenuItem>
                  <DropdownMenuItem onClick={handleDownloadSample}>
                    <Download className="size-3.5 mr-2" />
                    {t("hosts.downloadSample")}
                  </DropdownMenuItem>
                </DropdownMenuContent>
              </DropdownMenu>
            </div>
            <div className="flex items-center border border-border shrink-0">
              <button
                title={
                  selectionMode
                    ? t("hosts.exitSelectionTitle")
                    : t("hosts.selectHosts")
                }
                onClick={toggleSelectionMode}
                className={`flex items-center justify-center size-7 shrink-0 transition-colors ${selectionMode ? "text-accent-brand bg-accent-brand/15" : "text-muted-foreground/60 hover:text-foreground hover:bg-muted/60"}`}
              >
                <ListChecks className="size-3.5" />
              </button>
              <div className="w-px self-stretch bg-border" />
              <DropdownMenu>
                <DropdownMenuTrigger asChild>
                  <Button
                    variant="ghost"
                    size="icon"
                    className={`size-7 ${sortKey !== "default" || pinnedFirst || !arrangeLocked ? "text-accent-brand" : "text-muted-foreground hover:text-foreground"}`}
                    title={t("hosts.sortHosts")}
                  >
                    <ArrowUpDown className="size-3.5" />
                  </Button>
                </DropdownMenuTrigger>
                <DropdownMenuContent
                  align="start"
                  className="text-xs min-w-[160px]"
                >
                  <DropdownMenuItem
                    onClick={() => handleSortChange("default")}
                    className="flex items-center gap-1.5"
                  >
                    {sortKey === "default" ? (
                      <Check className="size-3 shrink-0 text-accent-brand" />
                    ) : (
                      <span className="size-3 shrink-0 inline-block" />
                    )}
                    {t("hosts.sortDefault")}
                  </DropdownMenuItem>
                  <DropdownMenuSeparator />
                  {(["name-asc", "name-desc"] as const).map((key) => (
                    <DropdownMenuItem
                      key={key}
                      onClick={() => handleSortChange(key)}
                      className="flex items-center gap-1.5"
                    >
                      {sortKey === key ? (
                        <Check className="size-3 shrink-0 text-accent-brand" />
                      ) : (
                        <span className="size-3 shrink-0 inline-block" />
                      )}
                      {t(
                        `hosts.sort${key === "name-asc" ? "NameAsc" : "NameDesc"}`,
                      )}
                    </DropdownMenuItem>
                  ))}
                  <DropdownMenuSeparator />
                  {(["ip-asc", "ip-desc"] as const).map((key) => (
                    <DropdownMenuItem
                      key={key}
                      onClick={() => handleSortChange(key)}
                      className="flex items-center gap-1.5"
                    >
                      {sortKey === key ? (
                        <Check className="size-3 shrink-0 text-accent-brand" />
                      ) : (
                        <span className="size-3 shrink-0 inline-block" />
                      )}
                      {t(`hosts.sort${key === "ip-asc" ? "IpAsc" : "IpDesc"}`)}
                    </DropdownMenuItem>
                  ))}
                  <DropdownMenuSeparator />
                  {(["status-online", "status-offline"] as const).map((key) => (
                    <DropdownMenuItem
                      key={key}
                      onClick={() => handleSortChange(key)}
                      className="flex items-center gap-1.5"
                    >
                      {sortKey === key ? (
                        <Check className="size-3 shrink-0 text-accent-brand" />
                      ) : (
                        <span className="size-3 shrink-0 inline-block" />
                      )}
                      {t(
                        key === "status-online"
                          ? "hosts.sortOnlineFirst"
                          : "hosts.sortOfflineFirst",
                      )}
                    </DropdownMenuItem>
                  ))}
                  <DropdownMenuSeparator />
                  <DropdownMenuItem
                    onClick={() => handleSortChange("manual")}
                    className="flex items-center gap-1.5"
                  >
                    {sortKey === "manual" ? (
                      <Check className="size-3 shrink-0 text-accent-brand" />
                    ) : (
                      <GripVertical className="size-3 shrink-0 text-muted-foreground/40" />
                    )}
                    {t("hosts.sortManual")}
                  </DropdownMenuItem>
                  <DropdownMenuCheckboxItem
                    checked={!arrangeLocked}
                    onCheckedChange={handleArrangeLockToggle}
                    onSelect={(event) => event.preventDefault()}
                  >
                    {t("hosts.arrangeUnlocked")}
                  </DropdownMenuCheckboxItem>
                  <DropdownMenuSeparator />
                  <DropdownMenuCheckboxItem
                    checked={pinnedFirst}
                    onCheckedChange={handlePinnedFirstChange}
                    onSelect={(event) => event.preventDefault()}
                  >
                    {t("hosts.sortPinnedFirst")}
                  </DropdownMenuCheckboxItem>
                </DropdownMenuContent>
              </DropdownMenu>
              <div className="w-px self-stretch bg-border" />
              <DropdownMenu>
                <DropdownMenuTrigger asChild>
                  <Button
                    variant="ghost"
                    size="icon"
                    className={`size-7 ${filterActive ? "text-accent-brand" : "text-muted-foreground hover:text-foreground"}`}
                    title={t("hosts.filterHosts")}
                  >
                    <Filter className="size-3.5" />
                  </Button>
                </DropdownMenuTrigger>
                <DropdownMenuContent
                  align="start"
                  className="text-xs min-w-[180px]"
                >
                  {filterActive && (
                    <>
                      <DropdownMenuItem
                        onClick={handleFilterClear}
                        className="flex items-center gap-1.5 text-accent-brand"
                      >
                        <X className="size-3 shrink-0" />
                        {t("hosts.filterClearAll")}
                      </DropdownMenuItem>
                      <DropdownMenuSeparator />
                    </>
                  )}
                  <DropdownMenuLabel>
                    {t("hosts.filterStatusGroup")}
                  </DropdownMenuLabel>
                  {(["online", "offline", "pinned"] as const).map((val) => (
                    <DropdownMenuCheckboxItem
                      key={val}
                      checked={filterState.status.includes(val)}
                      onCheckedChange={() => handleFilterToggle("status", val)}
                      onSelect={(e) => e.preventDefault()}
                    >
                      {t(
                        `hosts.filter${val.charAt(0).toUpperCase() + val.slice(1)}`,
                      )}
                    </DropdownMenuCheckboxItem>
                  ))}
                  <DropdownMenuSeparator />
                  <DropdownMenuLabel>
                    {t("hosts.filterAuthGroup")}
                  </DropdownMenuLabel>
                  {(
                    ["password", "key", "credential", "none", "opkssh"] as const
                  ).map((val) => (
                    <DropdownMenuCheckboxItem
                      key={val}
                      checked={filterState.authType.includes(val)}
                      onCheckedChange={() =>
                        handleFilterToggle("authType", val)
                      }
                      onSelect={(e) => e.preventDefault()}
                    >
                      {t(
                        `hosts.filterAuth${val.charAt(0).toUpperCase() + val.slice(1)}`,
                      )}
                    </DropdownMenuCheckboxItem>
                  ))}
                  <DropdownMenuSeparator />
                  <DropdownMenuLabel>
                    {t("hosts.filterProtocolGroup")}
                  </DropdownMenuLabel>
                  {(
                    [
                      ["ssh", "Ssh"],
                      ["rdp", "Rdp"],
                      ["vnc", "Vnc"],
                      ["telnet", "Telnet"],
                    ] as const
                  ).map(([val, key]) => (
                    <DropdownMenuCheckboxItem
                      key={val}
                      checked={filterState.protocol.includes(val)}
                      onCheckedChange={() =>
                        handleFilterToggle("protocol", val)
                      }
                      onSelect={(e) => e.preventDefault()}
                    >
                      {t(`hosts.filterProtocol${key}`)}
                    </DropdownMenuCheckboxItem>
                  ))}
                  <DropdownMenuSeparator />
                  <DropdownMenuLabel>
                    {t("hosts.filterFeaturesGroup")}
                  </DropdownMenuLabel>
                  {(
                    [
                      ["terminal", "Terminal"],
                      ["fileManager", "FileManager"],
                      ["tunnel", "Tunnel"],
                      ["docker", "Docker"],
                    ] as const
                  ).map(([val, key]) => (
                    <DropdownMenuCheckboxItem
                      key={val}
                      checked={filterState.features.includes(val)}
                      onCheckedChange={() =>
                        handleFilterToggle("features", val)
                      }
                      onSelect={(e) => e.preventDefault()}
                    >
                      {t(`hosts.filterFeature${key}`)}
                    </DropdownMenuCheckboxItem>
                  ))}
                  {allTags.length > 0 && (
                    <>
                      <DropdownMenuSeparator />
                      <DropdownMenuLabel>
                        {t("hosts.filterTagsGroup")}
                      </DropdownMenuLabel>
                      {allTags.map((tag) => (
                        <DropdownMenuCheckboxItem
                          key={tag}
                          checked={filterState.tags.includes(tag)}
                          onCheckedChange={() =>
                            handleFilterToggle("tags", tag)
                          }
                          onSelect={(e) => e.preventDefault()}
                        >
                          {tag}
                        </DropdownMenuCheckboxItem>
                      ))}
                    </>
                  )}
                </DropdownMenuContent>
              </DropdownMenu>
              <div className="w-px self-stretch bg-border" />
              <DropdownMenu>
                <DropdownMenuTrigger asChild>
                  <Button
                    variant="ghost"
                    size="icon"
                    className={`size-7 ${groupKey !== "folder" || selectionMode ? "text-accent-brand" : "text-muted-foreground hover:text-foreground"}`}
                    title={t("hosts.moreActions")}
                  >
                    <MoreHorizontal className="size-3.5" />
                  </Button>
                </DropdownMenuTrigger>
                <DropdownMenuContent
                  align="start"
                  className="text-xs min-w-[180px]"
                >
                  <DropdownMenuSub>
                    <DropdownMenuSubTrigger className="flex items-center gap-2">
                      <Group className="size-3.5 shrink-0" />
                      {t("hosts.groupBy")}
                      {groupKey !== "folder" && (
                        <span className="ml-auto text-accent-brand">
                          {t(
                            `hosts.GroupBy${groupKey.charAt(0).toUpperCase() + groupKey.slice(1)}`,
                          )}
                        </span>
                      )}
                    </DropdownMenuSubTrigger>
                    <DropdownMenuSubContent className="text-xs min-w-[150px]">
                      {(
                        [
                          ["folder", "GroupByFolder"],
                          ["tag", "GroupByTag"],
                          ["status", "GroupByStatus"],
                          ["protocol", "GroupByProtocol"],
                          ["auth", "GroupByAuth"],
                        ] as const
                      ).map(([key, label]) => (
                        <DropdownMenuItem
                          key={key}
                          onClick={() => handleGroupChange(key)}
                          className="flex items-center gap-1.5"
                        >
                          {groupKey === key ? (
                            <Check className="size-3 shrink-0 text-accent-brand" />
                          ) : (
                            <span className="size-3 shrink-0 inline-block" />
                          )}
                          {t(`hosts.${label}`)}
                        </DropdownMenuItem>
                      ))}
                    </DropdownMenuSubContent>
                  </DropdownMenuSub>
                  <DropdownMenuSeparator />
                  <DropdownMenuItem
                    onClick={() =>
                      window.dispatchEvent(
                        new CustomEvent("hosts:create-folder"),
                      )
                    }
                  >
                    <FolderPlus className="size-3.5 mr-2" />
                    {t("hosts.newFolder")}
                  </DropdownMenuItem>
                  <DropdownMenuItem
                    onClick={() =>
                      window.dispatchEvent(new CustomEvent("hosts:expand-all"))
                    }
                  >
                    <ChevronsUpDown className="size-3.5 mr-2" />
                    {t("hosts.expandAll")}
                  </DropdownMenuItem>
                  <DropdownMenuItem
                    onClick={() =>
                      window.dispatchEvent(
                        new CustomEvent("hosts:collapse-all"),
                      )
                    }
                  >
                    <ChevronsDownUp className="size-3.5 mr-2" />
                    {t("hosts.collapseAll")}
                  </DropdownMenuItem>
                  <DropdownMenuSeparator />
                  <DropdownMenuItem
                    onClick={toggleSelectionMode}
                    className={selectionMode ? "text-accent-brand" : ""}
                  >
                    <ListChecks className="size-3.5 mr-2" />
                    {selectionMode
                      ? t("hosts.exitSelectionTitle")
                      : t("hosts.selectHosts")}
                  </DropdownMenuItem>
                  <DropdownMenuSub>
                    <DropdownMenuSubTrigger className="flex items-center gap-2">
                      <Upload className="size-3.5 shrink-0" />
                      {t("hosts.importExportBtn")}
                    </DropdownMenuSubTrigger>
                    <DropdownMenuSubContent className="text-xs min-w-[170px]">
                      <DropdownMenuItem
                        onClick={() => {
                          importOverwriteRef.current = false;
                          fileInputRef.current?.click();
                        }}
                      >
                        <Upload className="size-3.5 mr-2" />
                        {t("hosts.importSkipExisting")}
                      </DropdownMenuItem>
                      <DropdownMenuItem
                        onClick={() => {
                          importOverwriteRef.current = true;
                          fileInputRef.current?.click();
                        }}
                      >
                        <Upload className="size-3.5 mr-2" />
                        {t("hosts.importOverwrite")}
                      </DropdownMenuItem>
                      <DropdownMenuItem
                        onClick={() => {
                          importOverwriteRef.current = false;
                          sshConfigInputRef.current?.click();
                        }}
                      >
                        <Upload className="size-3.5 mr-2" />
                        {t("hosts.importSSHConfig")}
                      </DropdownMenuItem>
                      <DropdownMenuItem
                        onClick={() => {
                          setExportPreselection(new Set());
                          setExportDialogOpen(true);
                        }}
                        disabled={rawHosts.length === 0}
                      >
                        <Download className="size-3.5 mr-2" />
                        {t("hosts.export.menuItem")}
                      </DropdownMenuItem>
                      <DropdownMenuItem onClick={handleDownloadSample}>
                        <Download className="size-3.5 mr-2" />
                        {t("hosts.downloadSample")}
                      </DropdownMenuItem>
                    </DropdownMenuSubContent>
                  </DropdownMenuSub>
                </DropdownMenuContent>
              </DropdownMenu>
            </div>
            <div className="flex items-center border border-border shrink-0">
              <button
                onClick={() => setCustomizePanelOpen(true)}
                title={t("hosts.customizeSidebar")}
                className="flex items-center justify-center size-7 text-muted-foreground hover:text-foreground hover:bg-muted transition-colors shrink-0"
              >
                <SlidersHorizontal className="size-3.5" />
              </button>
            </div>
            <div className="flex items-center border border-accent-brand/30 ml-auto shrink-0">
              <button
                onClick={() =>
                  window.dispatchEvent(new CustomEvent("host-manager:add-host"))
                }
                title={t("hosts.addHost")}
                className="flex items-center justify-center gap-1 h-7 px-2 text-[10px] font-medium text-accent-brand hover:bg-accent-brand/10 transition-colors"
              >
                <Plus className="size-3 shrink-0" />
                <span className="hidden min-[280px]:inline">
                  {t("hosts.addHost")}
                </span>
              </button>
            </div>
          </div>
        </div>
      )}
      <CustomizeSidebarPanel
        open={customizePanelOpen}
        onOpenChange={setCustomizePanelOpen}
        preferences={sidebarPrefs}
        update={updateSidebarPrefs}
      />

      <div
        className={`flex flex-col flex-1 min-h-0 ${managerEditing ? "hidden" : ""}`}
      >
        <SidebarTree
          children={
            hostTree
              ? groupHosts(
                  applyFilters(
                    sortHostTree(hostTree, sortKey, pinnedFirst),
                    filterState,
                  ),
                  groupKey,
                  groupLabel,
                ).children
              : []
          }
          onOpenTab={onOpenTab}
          onEditHost={onEditHost}
          onShareHost={(host) => setShareModalHost(host)}
          onProxmoxDiscover={(host) => {
            const cfg = host.proxmoxConfig;
            setProxmoxHostId(Number(host.id));
            setProxmoxDefaultCredentialId(cfg?.defaultCredentialId ?? null);
            setProxmoxDefaultAuthType(cfg?.defaultAuthType ?? undefined);
            setProxmoxDefaultUsername(undefined);
            setProxmoxDialogOpen(true);
          }}
          query={hostSearch.trim().toLowerCase()}
          selectionMode={selectionMode}
          onToggleSelectionMode={toggleSelectionMode}
          loading={loading}
          onExportSelected={(ids) => {
            setExportPreselection(new Set(ids));
            setExportDialogOpen(true);
          }}
          arrangeLocked={arrangeLocked}
          density={sidebarPrefs.display.density}
          trayTrigger={sidebarPrefs.display.trayTrigger}
          showTags={sidebarPrefs.display.showTags}
          openOnDoubleClick={sidebarPrefs.display.openOnDoubleClick}
        />
      </div>

      <div
        className={managerEditing ? "flex flex-col flex-1 min-h-0" : "hidden"}
      >
        <HostManager onEditingChange={handleEditingChange} active={active} />
      </div>

      <HostShareModal
        open={shareModalHost !== null}
        onClose={() => setShareModalHost(null)}
        host={shareModalHost}
      />

      <HostExportDialog
        open={exportDialogOpen}
        onClose={() => setExportDialogOpen(false)}
        hosts={rawHosts}
        preselectedHostIds={exportPreselection}
      />

      <ProxmoxDiscoverDialog
        open={proxmoxDialogOpen}
        onClose={() => {
          setProxmoxDialogOpen(false);
          setProxmoxHostId(undefined);
        }}
        hosts={rawHosts}
        onHostsChanged={setRawHosts}
        preselectedHostId={proxmoxHostId}
        defaultCredentialId={proxmoxDefaultCredentialId}
        defaultAuthType={proxmoxDefaultAuthType}
        defaultUsername={proxmoxDefaultUsername}
      />
    </div>
  );
}
