import {
  ArrowRight,
  ChevronDown,
  FolderOpen,
  Import,
  LayoutGrid,
  List as ListIcon,
  RefreshCw,
  Server,
  Shield,
  Trash2,
} from "lucide-react";
import React, {
  memo,
  useCallback,
  useDeferredValue,
  useEffect,
  useMemo,
  useState,
} from "react";
import { useI18n } from "../application/i18n/I18nProvider";
import { useKnownHostsBackend } from "../application/state/useKnownHostsBackend";
import { useStoredViewMode, ViewMode } from "../application/state/useStoredViewMode";
import { fingerprintFromPublicKey } from "../domain/knownHosts";
import { reorderVaultItems, sortByVaultOrder } from "../domain/vaultOrder";
import { STORAGE_KEY_VAULT_KNOWN_HOSTS_VIEW_MODE } from "../infrastructure/config/storageKeys";
import { logger } from "../lib/logger";
import { cn } from "../lib/utils";
import { Host, KnownHost } from "../types";
import { Button } from "./ui/button";
import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuTrigger,
} from "./ui/context-menu";
import { Dropdown, DropdownContent, DropdownTrigger } from "./ui/dropdown";
import { ScrollArea } from "./ui/scroll-area";
import { SortDropdown, SortMode } from "./ui/sort-dropdown";
import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";
import { toast } from "./ui/toast";
import {
  VaultHeaderSearch,
  VaultPageHeader,
  vaultHeaderIconButtonClass,
  vaultHeaderSecondaryButtonClass,
} from "./vault/VaultPageHeader";
import { VaultEntityIcon, vaultPrimaryIconClass } from "./vault/VaultEntityIcon";
import { useVaultItemReorder } from "./vault/vaultReorderDrag";

interface KnownHostsManagerProps {
  knownHosts: KnownHost[];
  hosts: Host[];
  onSave: (knownHost: KnownHost) => void;
  onUpdate: (knownHost: KnownHost) => void;
  onReorder: (knownHosts: KnownHost[]) => void;
  onDelete: (id: string) => void;
  onConvertToHost: (knownHost: KnownHost) => void;
  onImportFromFile: (hosts: KnownHost[]) => void;
  onRefresh: () => void;
}

// Parse known_hosts file content - pure function, moved outside component
const parseKnownHostsFile = (content: string): KnownHost[] => {
  const lines = content
    .split("\n")
    .filter((line) => line.trim() && !line.startsWith("#"));
  const parsed: KnownHost[] = [];

  for (const line of lines) {
    try {
      const parts = line.trim().split(/\s+/);
      if (parts.length < 3) continue;

      const [hostPattern, keyType, publicKey] = parts;

      let hostname = hostPattern;
      let port = 22;

      const bracketMatch = hostPattern.match(/^\[([^\]]+)\]:(\d+)$/);
      if (bracketMatch) {
        hostname = bracketMatch[1];
        port = parseInt(bracketMatch[2], 10);
      } else if (hostPattern.includes(",")) {
        hostname = hostPattern.split(",")[0];
      }

      if (hostname.startsWith("|1|")) {
        hostname = "(hashed)";
      }

      const fullPublicKey = `${keyType} ${publicKey}`;
      // Compute the fingerprint up front so the SSH host verifier can match
      // against this record directly instead of re-deriving on every connect —
      // the re-derivation path is where the false "fingerprint changed"
      // warnings in #972 originated.
      const fingerprint = fingerprintFromPublicKey(fullPublicKey);

      parsed.push({
        id: `kh-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
        hostname,
        port,
        keyType,
        publicKey: fullPublicKey,
        fingerprint: fingerprint || undefined,
        discoveredAt: Date.now(),
      });
    } catch {
      logger.warn("Failed to parse known_hosts line:", line);
    }
  }

  return parsed;
};

// Memoized Grid Item Component
interface HostItemProps {
  knownHost: KnownHost;
  converted: boolean;
  viewMode: ViewMode;
  reorderProps?: React.HTMLAttributes<HTMLDivElement>;
  onDelete: (id: string) => void;
  onConvertToHost: (knownHost: KnownHost) => void;
}

const HostItem = React.memo<HostItemProps>(
  ({ knownHost, converted, viewMode, reorderProps, onDelete, onConvertToHost }) => {
    const { t } = useI18n();
    // Disabled to reduce log noise - uncomment for debugging
    // console.log('[HostItem] render:', knownHost.hostname);
    if (viewMode === "grid") {
      return (
        <ContextMenu>
          <ContextMenuTrigger asChild>
            <div
              {...reorderProps}
              className={cn(
                reorderProps && "vault-drop-indicator-row",
                "group cursor-pointer soft-card elevate rounded-xl h-[68px] px-3 py-2",
                converted && "opacity-60",
                reorderProps?.className,
              )}
            >
              {/* Quick action buttons on hover */}
              <div className="absolute top-1 right-1 flex gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity">
                {!converted && (
                  <Tooltip>
                    <TooltipTrigger asChild>
                      <button
                        className="p-1 rounded hover:bg-primary/20 text-primary"
                        onClick={(e) => {
                          e.stopPropagation();
                          onConvertToHost(knownHost);
                        }}
                      >
                        <ArrowRight size={12} />
                      </button>
                    </TooltipTrigger>
                    <TooltipContent>{t("action.convertToHost")}</TooltipContent>
                  </Tooltip>
                )}
                <Tooltip>
                  <TooltipTrigger asChild>
                    <button
                      className="p-1 rounded hover:bg-destructive/20 text-destructive"
                      onClick={(e) => {
                        e.stopPropagation();
                        onDelete(knownHost.id);
                      }}
                    >
                      <Trash2 size={12} />
                    </button>
                  </TooltipTrigger>
                  <TooltipContent>{t("action.remove")}</TooltipContent>
                </Tooltip>
              </div>
              <div className="flex items-center gap-3 h-full">
                <VaultEntityIcon
                  className={vaultPrimaryIconClass}
                  icon={<Server size={18} />}
                />
                <div className="flex-1 min-w-0">
                  <span className="text-sm font-semibold truncate block">
                    {knownHost.hostname}
                  </span>
                </div>
              </div>
            </div>
          </ContextMenuTrigger>
          <ContextMenuContent>
            {!converted && (
              <ContextMenuItem onClick={() => onConvertToHost(knownHost)}>
                <ArrowRight className="mr-2 h-4 w-4" /> {t("action.convertToHost")}
              </ContextMenuItem>
            )}
            <ContextMenuItem
              className="text-destructive"
              onClick={() => onDelete(knownHost.id)}
            >
              <Trash2 className="mr-2 h-4 w-4" /> {t("action.remove")}
            </ContextMenuItem>
          </ContextMenuContent>
        </ContextMenu>
      );
    }

    // List view
    return (
      <ContextMenu>
        <ContextMenuTrigger asChild>
          <div
            {...reorderProps}
            className={cn(
              reorderProps && "vault-drop-indicator-row",
              "group flex items-center gap-3 px-3 py-2 h-14 rounded-lg hover:bg-secondary/60 transition-colors cursor-pointer",
              converted && "opacity-60",
              reorderProps?.className,
            )}
          >
            <VaultEntityIcon
              className={vaultPrimaryIconClass}
              icon={<Server size={18} />}
            />
            <div className="flex-1 min-w-0">
              <span className="text-sm font-semibold truncate block">
                {knownHost.hostname}
              </span>
            </div>
            <div className="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
              {!converted && (
                <Tooltip>
                  <TooltipTrigger asChild>
                    <Button
                      variant="ghost"
                      size="icon"
                      className="h-8 w-8"
                      onClick={(e) => {
                        e.stopPropagation();
                        onConvertToHost(knownHost);
                      }}
                    >
                      <ArrowRight size={14} />
                    </Button>
                  </TooltipTrigger>
                  <TooltipContent>{t("action.convertToHost")}</TooltipContent>
                </Tooltip>
              )}
            </div>
          </div>
        </ContextMenuTrigger>
        <ContextMenuContent>
          {!converted && (
            <ContextMenuItem onClick={() => onConvertToHost(knownHost)}>
              <ArrowRight className="mr-2 h-4 w-4" /> {t("action.convertToHost")}
            </ContextMenuItem>
          )}
          <ContextMenuItem
            className="text-destructive"
            onClick={() => onDelete(knownHost.id)}
          >
            <Trash2 className="mr-2 h-4 w-4" /> {t("action.remove")}
          </ContextMenuItem>
        </ContextMenuContent>
      </ContextMenu>
    );
  },
);

HostItem.displayName = "HostItem";

const KnownHostsManager: React.FC<KnownHostsManagerProps> = ({
  knownHosts,
  hosts,
  onSave: _onSave,
  onUpdate: _onUpdate,
  onReorder,
  onDelete,
  onConvertToHost,
  onImportFromFile,
  onRefresh,
}) => {
  const { t } = useI18n();
  const { readKnownHosts } = useKnownHostsBackend();
  const [search, setSearch] = useState("");
  const deferredSearch = useDeferredValue(search);
  const [isScanning, setIsScanning] = useState(false);
  const [viewMode, setViewMode] = useStoredViewMode(
    STORAGE_KEY_VAULT_KNOWN_HOSTS_VIEW_MODE,
    "grid",
  );
  const [sortMode, setSortMode] = useState<SortMode>("manual");
  const fileInputRef = React.useRef<HTMLInputElement>(null);
  const hasScannedRef = React.useRef(false);
  const listRef = React.useRef<HTMLDivElement | null>(null);
  const RENDER_LIMIT = 100; // Limit rendered items for performance

  // Define handleScanSystem before useEffect that depends on it
  const handleScanSystem = useCallback(async (silent = false) => {
    setIsScanning(true);
    try {
      const content = await readKnownHosts();
      if (content === undefined) {
        if (!silent) toast.error(
          t("knownHosts.toast.scanUnavailable"),
          t("vault.nav.knownHosts"),
        );
        return;
      }
      if (!content) {
        if (!silent) toast.info(t("knownHosts.toast.scanNoFile"), t("vault.nav.knownHosts"));
        return;
      }

      const parsed = parseKnownHostsFile(content);
      if (parsed.length === 0) {
        if (!silent) toast.info(
          t("knownHosts.toast.scanNoEntries"),
          t("vault.nav.knownHosts"),
        );
        return;
      }

      const existingHostnames = new Set(
        knownHosts.map((h) => `${h.hostname}:${h.port}`),
      );
      const newHosts = parsed.filter(
        (h) => !existingHostnames.has(`${h.hostname}:${h.port}`),
      );

      if (newHosts.length > 0) {
        onImportFromFile(newHosts);
        if (!silent) toast.success(
          t("knownHosts.toast.scanImported", { count: newHosts.length }),
          t("vault.nav.knownHosts"),
        );
      } else {
        if (!silent) toast.info(t("knownHosts.toast.scanNoNew"), t("vault.nav.knownHosts"));
      }
    } catch (err) {
      logger.error("Failed to scan system known_hosts:", err);
      if (!silent) toast.error(
        err instanceof Error ? err.message : t("knownHosts.toast.scanFailed"),
        t("vault.nav.knownHosts"),
      );
    } finally {
      onRefresh();
      setIsScanning(false);
    }
  }, [knownHosts, onRefresh, onImportFromFile, readKnownHosts, t]);

  // Auto-scan on first mount (silent — don't show toasts for missing known_hosts)
  useEffect(() => {
    if (!hasScannedRef.current) {
      hasScannedRef.current = true;
      const timer = setTimeout(() => {
        handleScanSystem(true);
      }, 100);
      return () => clearTimeout(timer);
    }
  }, [handleScanSystem]);

  // Sort and filter hosts with deduplication by hostname
  const filteredHosts = useMemo(() => {
    // First, deduplicate by hostname (keep the most recent one)
    const hostnameMap = new Map<string, KnownHost>();
    for (const h of knownHosts) {
      const key = h.hostname;
      const existing = hostnameMap.get(key);
      if (!existing || h.discoveredAt > existing.discoveredAt) {
        hostnameMap.set(key, h);
      }
    }
    let result = Array.from(hostnameMap.values());

    // Filter by search
    if (deferredSearch.trim()) {
      const term = deferredSearch.toLowerCase();
      result = result.filter(
        (h) =>
          h.hostname.toLowerCase().includes(term) ||
          h.keyType.toLowerCase().includes(term),
      );
    }

    // Sort
    result = [...result].sort((a, b) => {
      switch (sortMode) {
        case "az":
          return a.hostname.localeCompare(b.hostname);
        case "za":
          return b.hostname.localeCompare(a.hostname);
        case "newest":
          return b.discoveredAt - a.discoveredAt;
        case "oldest":
          return a.discoveredAt - b.discoveredAt;
        case "manual":
          return 0;
        default:
          return 0;
      }
    });

    return sortMode === "manual" ? sortByVaultOrder(result) : result;
  }, [knownHosts, deferredSearch, sortMode]);

  // Limit rendered items for performance
  const displayedHosts = useMemo(() => {
    return filteredHosts.slice(0, RENDER_LIMIT);
  }, [filteredHosts]);

  const hasMore = filteredHosts.length > RENDER_LIMIT;

  const handleFileSelect = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      const file = e.target.files?.[0];
      if (!file) return;

      const reader = new FileReader();
      reader.onload = (event) => {
        const content = event.target?.result as string;
        const parsed = parseKnownHostsFile(content);

        // Filter out already existing hosts and directly import
        const existingHostnames = new Set(
          knownHosts.map((h) => `${h.hostname}:${h.port}`),
        );
        const newHosts = parsed.filter(
          (h) => !existingHostnames.has(`${h.hostname}:${h.port}`),
        );

        if (newHosts.length > 0) {
          onImportFromFile(newHosts);
        }
      };
      reader.readAsText(file);

      // Reset file input
      if (fileInputRef.current) {
        fileInputRef.current.value = "";
      }
    },
    [knownHosts, onImportFromFile],
  );

  // Memoize host lookup for performance
  const hostIdSet = useMemo(() => new Set(hosts.map((h) => h.id)), [hosts]);

  // Pre-compute converted status for all known hosts
  const convertedMap = useMemo(() => {
    const map = new Map<string, boolean>();
    for (const kh of knownHosts) {
      if (kh.convertedToHostId) {
        map.set(kh.id, hostIdSet.has(kh.convertedToHostId));
      } else {
        map.set(kh.id, false);
      }
    }
    return map;
  }, [knownHosts, hostIdSet]);

  // Memoized handlers to prevent re-renders
  const handleDelete = useCallback(
    (id: string) => {
      onDelete(id);
    },
    [onDelete],
  );

  const handleConvertToHost = useCallback(
    (knownHost: KnownHost) => {
      onConvertToHost(knownHost);
    },
    [onConvertToHost],
  );

  const openFilePicker = useCallback(() => fileInputRef.current?.click(), []);

  const knownHostReorder = useVaultItemReorder({
    containerRef: listRef,
    viewMode,
    dragType: "known-host-id",
    targetAttribute: "data-known-host-id",
    disabled: deferredSearch.trim().length > 0,
    onReorder: (sourceId, targetId, position) => {
      onReorder(reorderVaultItems(knownHosts, sourceId, targetId, position));
      setSortMode("manual");
    },
  });

  // Memoize the rendered list to prevent re-renders
  const renderedItems = useMemo(() => {
    return displayedHosts.map((knownHost) => (
      <HostItem
        key={knownHost.id}
        knownHost={knownHost}
        converted={convertedMap.get(knownHost.id) || false}
        viewMode={viewMode}
        reorderProps={knownHostReorder.getItemReorderProps(knownHost.id, `known:${knownHost.id}`)}
        onDelete={handleDelete}
        onConvertToHost={handleConvertToHost}
      />
    ));
  }, [
    displayedHosts,
    convertedMap,
    viewMode,
    handleDelete,
    handleConvertToHost,
    knownHostReorder,
  ]);

  return (
    <div className="h-full flex flex-col">
      <VaultPageHeader>
        <div className="flex-1 min-w-0 flex items-center gap-2">
          <VaultHeaderSearch
            placeholder={t("knownHosts.search.placeholder")}
            className="flex-1 max-w-xs"
            value={search}
            onChange={(e) => setSearch(e.target.value)}
          />
        </div>
        <div className="flex items-center gap-1">
          {/* View Mode Toggle */}
          <Dropdown>
            <DropdownTrigger asChild>
              <Button variant="ghost" size="icon" className={vaultHeaderIconButtonClass}>
                {viewMode === "grid" ? (
                  <LayoutGrid size={16} />
                ) : (
                  <ListIcon size={16} />
                )}
                <ChevronDown size={10} className="ml-0.5" />
              </Button>
            </DropdownTrigger>
            <DropdownContent className="w-32" align="end">
              <Button
                variant={viewMode === "grid" ? "secondary" : "ghost"}
                className="w-full justify-start gap-2 h-9"
                onClick={() => setViewMode("grid")}
              >
                <LayoutGrid size={14} /> {t("vault.view.grid")}
              </Button>
              <Button
                variant={viewMode === "list" ? "secondary" : "ghost"}
                className="w-full justify-start gap-2 h-9"
                onClick={() => setViewMode("list")}
              >
                <ListIcon size={14} /> {t("vault.view.list")}
              </Button>
            </DropdownContent>
          </Dropdown>

          {/* Sort Toggle */}
          <SortDropdown
            value={sortMode}
            onChange={setSortMode}
            modes={["manual", "az", "za", "newest", "oldest"]}
            className={vaultHeaderIconButtonClass}
          />
        </div>
        <div className="w-px h-5 bg-border/50" />
        <div className="flex items-center gap-2">
          <Button
            variant="secondary"
            className={vaultHeaderSecondaryButtonClass}
            onClick={() => handleScanSystem()}
            disabled={isScanning}
          >
            <RefreshCw
              size={14}
              className={cn("mr-2", isScanning && "animate-spin")}
            />
            {t("knownHosts.action.scanSystem")}
          </Button>
          <input
            ref={fileInputRef}
            type="file"
            accept=".txt,known_hosts"
            className="hidden"
            onChange={handleFileSelect}
          />
          <Button
            variant="secondary"
            className={vaultHeaderSecondaryButtonClass}
            onClick={openFilePicker}
          >
            <Import size={14} className="mr-2" />
            {t("knownHosts.action.importFile")}
          </Button>
        </div>
      </VaultPageHeader>

      {/* Content */}
      <ScrollArea className="flex-1">
        <div
          ref={listRef}
          className={cn(
            "p-4",
            viewMode === "grid"
              ? "grid grid-cols-2 sm:grid-cols-3 xl:grid-cols-4 gap-3"
              : "flex flex-col gap-0",
          )}
          onDragOverCapture={knownHostReorder.handleDragOverCapture}
          onDragOver={knownHostReorder.handleDragOver}
          onDropCapture={knownHostReorder.handleDropCapture}
          onDragEndCapture={knownHostReorder.handleDragEndCapture}
        >
          {displayedHosts.length === 0 ? (
            <div
              className={cn(
                "flex flex-col items-center justify-center py-16 text-muted-foreground",
                viewMode === "grid" && "col-span-full",
              )}
            >
              <div className="h-16 w-16 rounded-2xl bg-secondary/80 flex items-center justify-center mb-4">
                <Shield size={32} className="opacity-60" />
              </div>
              <h3 className="text-lg font-semibold text-foreground mb-2">
                {t("knownHosts.empty.title")}
              </h3>
              <p className="text-sm text-center max-w-sm mb-4">
                {t("knownHosts.empty.desc")}
              </p>
              <div className="flex gap-2">
                <Button
                  variant="secondary"
                  onClick={() => handleScanSystem()}
                  disabled={isScanning}
                >
                  <RefreshCw
                    size={14}
                    className={cn("mr-2", isScanning && "animate-spin")}
                  />
                  {t("knownHosts.action.scanSystem")}
                </Button>
                <Button variant="outline" onClick={openFilePicker}>
                  <FolderOpen size={14} className="mr-2" />
                  {t("knownHosts.action.browseFile")}
                </Button>
              </div>
            </div>
          ) : (
            <>
              {renderedItems}
              {hasMore && (
                <div
                  className={cn(
                    "text-center py-4 text-sm text-muted-foreground",
                    viewMode === "grid" && "col-span-full",
                  )}
                >
                  {t("knownHosts.results.showingLimited", {
                    shown: displayedHosts.length,
                    total: filteredHosts.length,
                  })}
                </div>
              )}
            </>
          )}
        </div>
      </ScrollArea>
    </div>
  );
};

// Custom comparison - only compare data props, not callbacks
const knownHostsManagerAreEqual = (
  prev: KnownHostsManagerProps,
  next: KnownHostsManagerProps,
): boolean => {
  return prev.knownHosts === next.knownHosts && prev.hosts === next.hosts;
};

export default memo(KnownHostsManager, knownHostsManagerAreEqual);
