import { invoke } from "@tauri-apps/api/core";
import type { ISearchOptions } from "@xterm/addon-search";
import { Terminal } from "@xterm/xterm";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { connectionStore } from "@/features/remote/services/remote-connection-store";
import { parseRemotePath } from "@/features/remote/utils/remote-path";
import { useSettingsStore } from "@/features/settings/store";
import { useZoomStore } from "@/features/window/stores/zoom-store";
import { useProjectStore } from "@/features/window/stores/project-store";
import { extractDroppedFilePaths } from "@/features/file-system/utils/file-system-dropped-paths";
import { primitiveConfirm } from "@/ui/primitive-dialog-service";
import {
  createTerminalAddons,
  injectLinkStyles,
  loadWebLinksAddon,
  removeLinkStyles,
  type TerminalAddons,
} from "../hooks/use-terminal-addons";
import { useTerminalConnection } from "../hooks/use-terminal-connection";
import { useTerminalTheme } from "../hooks/use-terminal-theme";
import { useTerminalStore } from "../stores/terminal-store";
import { formatDroppedPathsForTerminal } from "../utils/terminal-file-drop";
import { resolveTerminalFont } from "../utils/resolve-font";
import { TerminalSearch, type TerminalSearchOptions } from "./terminal-search";
import "@xterm/xterm/css/xterm.css";
import "../styles/terminal.css";

const MULTILINE_PASTE_LINE_THRESHOLD = 5;
const LARGE_PASTE_CHAR_THRESHOLD = 1000;

interface XtermTerminalProps {
  sessionId: string;
  isActive: boolean;
  isVisible?: boolean;
  onReady?: () => void;
  onTerminalRef?: (ref: { focus: () => void; showSearch: () => void; terminal: Terminal }) => void;
  onTerminalExit?: (sessionId: string) => void;
  initialCommand?: string;
  workingDirectory?: string;
  remoteConnectionId?: string;
}

export const XtermTerminal: React.FC<XtermTerminalProps> = ({
  sessionId,
  isActive,
  isVisible = true,
  onReady,
  onTerminalRef,
  onTerminalExit,
  initialCommand,
  workingDirectory,
  remoteConnectionId,
}) => {
  const terminalContainerRef = useRef<HTMLDivElement>(null);
  const xtermRef = useRef<Terminal | null>(null);
  const addonsRef = useRef<TerminalAddons | null>(null);
  const [isInitialized, setIsInitialized] = useState(false);
  const [isSearchVisible, setIsSearchVisible] = useState(false);
  const [searchResults, setSearchResults] = useState({ current: 0, total: 0 });
  const isInitializingRef = useRef(false);

  const { updateSession, getSession } = useTerminalStore();
  const session = getSession(sessionId);
  const connectionId = session?.connectionId;
  const hadExistingConnectionOnMountRef = useRef(Boolean(session?.connectionId));

  const {
    theme: terminalThemeId,
    terminalFontFamily,
    terminalFontSize,
    terminalLineHeight,
    terminalLetterSpacing,
    terminalScrollback,
    terminalCursorStyle,
    terminalCursorBlink,
    terminalCursorWidth,
  } = useSettingsStore((state) => state.settings);
  const zoomLevel = useZoomStore.use.terminalZoomLevel();
  const { rootFolderPath } = useProjectStore();
  const { getTerminalTheme } = useTerminalTheme();
  const effectiveTerminalFontSize = Math.round(terminalFontSize * zoomLevel * 10) / 10;
  const effectiveTerminalLetterSpacing = terminalLetterSpacing * zoomLevel;
  const effectiveTerminalCursorWidth = Math.max(1, Math.round(terminalCursorWidth * zoomLevel));

  const fitTerminal = useCallback((attempts = 1) => {
    let attempt = 0;
    let rafId: number | null = null;

    const runFit = () => {
      const container = terminalContainerRef.current;
      const addons = addonsRef.current;
      if (!container || !addons) return;

      const rect = container.getBoundingClientRect();
      const isContainerVisible = container.offsetParent !== null;
      if (rect.width <= 0 || rect.height <= 0 || !isContainerVisible) {
        if (attempt < attempts - 1) {
          attempt += 1;
          rafId = requestAnimationFrame(runFit);
        }
        return;
      }

      addons.fitAddon.fit();

      if (attempt < attempts - 1) {
        attempt += 1;
        rafId = requestAnimationFrame(runFit);
      } else {
        // Final pass: force a renderer refresh so the canvas/WebGL backend
        // repaints after a size change. Without this, splitting a pane (or any
        // layout change that resizes the container) leaves the buffer in memory
        // but unpainted until the window regains focus.
        const term = xtermRef.current;
        if (term) term.refresh(0, term.rows - 1);
      }
    };

    rafId = requestAnimationFrame(runFit);

    return () => {
      if (rafId !== null) cancelAnimationFrame(rafId);
    };
  }, []);

  const { currentConnectionIdRef, writeBuffered } = useTerminalConnection({
    connectionId,
    getTerminalTheme,
    initialCommand,
    isInitialized,
    onTerminalExit,
    remoteConnectionId,
    reuseExistingConnection: hadExistingConnectionOnMountRef.current,
    sessionId,
    terminal: xtermRef.current,
    updateSession,
  });

  const handleTerminalFileDrop = useCallback(
    (event: React.DragEvent<HTMLDivElement>) => {
      const paths = extractDroppedFilePaths(event.dataTransfer);
      const text = formatDroppedPathsForTerminal(paths);
      if (!text) return;

      event.preventDefault();
      event.stopPropagation();
      writeBuffered(text);
      requestAnimationFrame(() => xtermRef.current?.focus());
    },
    [writeBuffered],
  );

  const handleTerminalDragOver = useCallback((event: React.DragEvent<HTMLDivElement>) => {
    if (!Array.from(event.dataTransfer.types).includes("Files")) return;
    event.preventDefault();
    event.stopPropagation();
    event.dataTransfer.dropEffect = "copy";
  }, []);

  const initializeTerminal = useCallback(async () => {
    const container = terminalContainerRef.current;
    if (!container || isInitialized || isInitializingRef.current) return;

    const rect = container.getBoundingClientRect();
    const isContainerVisible = container.offsetParent !== null;
    if (rect.width <= 0 || rect.height <= 0 || !isContainerVisible) return;

    isInitializingRef.current = true;
    const resolved = await resolveTerminalFont(terminalFontFamily, effectiveTerminalFontSize);
    await new Promise((resolve) => setTimeout(resolve, 100));

    if (!terminalContainerRef.current) {
      isInitializingRef.current = false;
      return;
    }

    try {
      const terminal = new Terminal({
        fontFamily: resolved.fontFamily,
        fontSize: effectiveTerminalFontSize,
        lineHeight: terminalLineHeight,
        letterSpacing: effectiveTerminalLetterSpacing,
        cursorBlink: terminalCursorBlink,
        cursorStyle: terminalCursorStyle,
        cursorWidth: effectiveTerminalCursorWidth,
        allowProposedApi: true,
        theme: getTerminalTheme(),
        scrollback: terminalScrollback,
        convertEol: false,
        macOptionIsMeta: true,
        rightClickSelectsWord: false,
      });

      const addons = createTerminalAddons(terminal, {
        skipWebGL: resolved.skipWebGL,
      });

      terminal.open(terminalContainerRef.current);
      terminal.attachCustomKeyEventHandler((event) => {
        if (event.ctrlKey && !event.metaKey) return true;
        if (
          event.metaKey &&
          ["Backspace", "k", "a", "e", "f", "ArrowLeft", "ArrowRight"].includes(event.key)
        ) {
          return true;
        }
        return !event.metaKey;
      });

      if (terminal.textarea) {
        terminal.textarea.spellcheck = false;
        terminal.textarea.addEventListener("beforeinput", (event) => {
          if (event.inputType === "insertReplacementText" || event.inputType === "insertFromDrop") {
            const text = event.dataTransfer?.getData("text/plain") ?? event.data;
            if (!text || !currentConnectionIdRef.current) return;

            event.preventDefault();
            writeBuffered(text);
          }
        });

        terminal.textarea.addEventListener(
          "paste",
          (event) => {
            const text = event.clipboardData?.getData("text/plain");
            if (!text || !currentConnectionIdRef.current) return;

            const normalizedText = text.replace(/\r\n/g, "\n");
            const lineCount = normalizedText.split("\n").length;
            const requiresConfirmation =
              lineCount >= MULTILINE_PASTE_LINE_THRESHOLD ||
              normalizedText.length >= LARGE_PASTE_CHAR_THRESHOLD;

            if (requiresConfirmation) {
              event.preventDefault();
              event.stopImmediatePropagation();
              void primitiveConfirm(
                `Paste ${lineCount} lines into the terminal? This may execute multiple commands.`,
                { title: "Paste Into Terminal", confirmLabel: "Paste" },
              ).then((confirmed) => {
                if (confirmed) writeBuffered(normalizedText);
              });
              return;
            }

            event.preventDefault();
            event.stopImmediatePropagation();
            writeBuffered(normalizedText);
          },
          true,
        );
      }

      loadWebLinksAddon(terminal);
      terminal.unicode.activeVersion = "11";
      injectLinkStyles(sessionId, terminalContainerRef.current.id || `terminal-${sessionId}`);

      xtermRef.current = terminal;
      addonsRef.current = addons;

      // Fit synchronously after open so terminal.rows/cols reflect the actual container size
      // before we create the PTY with those dimensions
      addons.fitAddon.fit();

      const existingSession = getSession(sessionId);

      // If the session already has a live PTY connection (e.g., component
      // remounted after a pane split or tab move), reuse the existing
      // connection instead of killing the running process.
      let activeConnectionId: string;
      if (existingSession?.connectionId) {
        activeConnectionId = existingSession.connectionId;
      } else {
        const targetDirectory =
          workingDirectory || existingSession?.currentDirectory || rootFolderPath;
        const remoteInfo = targetDirectory ? parseRemotePath(targetDirectory) : null;
        const effectiveRemoteConnectionId = remoteConnectionId || remoteInfo?.connectionId;

        activeConnectionId = effectiveRemoteConnectionId
          ? await (async () => {
              const connection = await connectionStore.getConnection(effectiveRemoteConnectionId);
              if (!connection) {
                throw new Error("Remote terminal connection not found.");
              }

              return invoke<string>("create_remote_terminal", {
                host: connection.host,
                port: connection.port,
                username: connection.username,
                password: connection.password || null,
                keyPath: connection.keyPath || null,
                workingDirectory: remoteInfo?.remotePath || "/",
                rows: terminal.rows,
                cols: terminal.cols,
              });
            })()
          : await invoke<string>("create_terminal", {
              config: {
                working_directory: targetDirectory || undefined,
                shell: existingSession?.shell || undefined,
                rows: terminal.rows,
                cols: terminal.cols,
              },
            });

        updateSession(sessionId, {
          connectionId: activeConnectionId,
          currentDirectory: targetDirectory,
          remoteConnectionId: effectiveRemoteConnectionId,
        });
      }

      // No snapshot replay: xterm is portaled and never remounts mid-session,
      // so the live PTY redrawing via SIGWINCH is the source of truth.

      setIsInitialized(true);
      isInitializingRef.current = false;

      // Re-fit after connection is established so onResize can notify the PTY
      fitTerminal(3);

      window.dispatchEvent(
        new CustomEvent("terminal-ready", {
          detail: { terminalId: sessionId, connectionId: activeConnectionId },
        }),
      );

      onTerminalRef?.({
        focus: () => terminal.focus(),
        showSearch: () => setIsSearchVisible(true),
        terminal,
      });
      onReady?.();
    } catch (error) {
      console.error("Failed to initialize terminal:", error);
      isInitializingRef.current = false;
    }
  }, [
    currentConnectionIdRef,
    fitTerminal,
    getSession,
    getTerminalTheme,
    isInitialized,
    onReady,
    onTerminalRef,
    rootFolderPath,
    remoteConnectionId,
    sessionId,
    terminalCursorBlink,
    terminalCursorStyle,
    terminalCursorWidth,
    terminalFontFamily,
    effectiveTerminalCursorWidth,
    effectiveTerminalFontSize,
    effectiveTerminalLetterSpacing,
    terminalLineHeight,
    terminalScrollback,
    updateSession,
    workingDirectory,
    writeBuffered,
  ]);

  useEffect(() => {
    if (!xtermRef.current) return;
    xtermRef.current.options.theme = getTerminalTheme();
    const timer = setTimeout(() => {
      xtermRef.current?.refresh(0, xtermRef.current.rows - 1);
      fitTerminal(4);
    }, 10);
    return () => clearTimeout(timer);
  }, [terminalThemeId, getTerminalTheme, fitTerminal]);

  useEffect(() => {
    if (!xtermRef.current || !addonsRef.current) return;

    let cancelled = false;

    const applyFontChange = async () => {
      const resolved = await resolveTerminalFont(terminalFontFamily, effectiveTerminalFontSize);
      if (cancelled || !xtermRef.current || !addonsRef.current) return;

      if (resolved.skipWebGL) {
        addonsRef.current.webglAddon?.dispose();
      }

      xtermRef.current.options.fontFamily = resolved.fontFamily;
      xtermRef.current.options.fontSize = effectiveTerminalFontSize;
      xtermRef.current.options.lineHeight = terminalLineHeight;
      xtermRef.current.options.letterSpacing = effectiveTerminalLetterSpacing;
      xtermRef.current.options.scrollback = terminalScrollback;
      xtermRef.current.options.cursorBlink = terminalCursorBlink;
      xtermRef.current.options.cursorStyle = terminalCursorStyle;
      xtermRef.current.options.cursorWidth = effectiveTerminalCursorWidth;

      fitTerminal(4);
      xtermRef.current.refresh(0, xtermRef.current.rows - 1);
    };

    void applyFontChange();

    return () => {
      cancelled = true;
    };
  }, [
    terminalFontFamily,
    effectiveTerminalCursorWidth,
    effectiveTerminalFontSize,
    effectiveTerminalLetterSpacing,
    terminalLineHeight,
    terminalScrollback,
    terminalCursorBlink,
    terminalCursorStyle,
    fitTerminal,
  ]);

  useEffect(() => {
    if (!isVisible) return;

    let mounted = true;
    const initTimer = setTimeout(() => {
      if (mounted && !isInitialized && !isInitializingRef.current) {
        void initializeTerminal();
      }
    }, 200);

    return () => {
      mounted = false;
      clearTimeout(initTimer);
      removeLinkStyles(sessionId);
    };
  }, [initializeTerminal, isInitialized, isVisible, sessionId]);

  useEffect(() => {
    if (isInitialized || !isVisible || !terminalContainerRef.current) return;

    let rafId: number | null = null;
    const container = terminalContainerRef.current;

    const attemptInitialize = () => {
      if (isInitialized || isInitializingRef.current) return;

      const rect = container.getBoundingClientRect();
      const isContainerVisible = container.offsetParent !== null;
      if (rect.width <= 0 || rect.height <= 0 || !isContainerVisible) {
        rafId = requestAnimationFrame(attemptInitialize);
        return;
      }

      void initializeTerminal();
    };

    rafId = requestAnimationFrame(attemptInitialize);

    return () => {
      if (rafId !== null) cancelAnimationFrame(rafId);
    };
  }, [initializeTerminal, isInitialized, isVisible]);

  // Dispose only the xterm UI on unmount. The PTY process is owned by
  // the buffer store and killed in closeBufferForce when the user actually
  // closes the tab — NOT here. This prevents pane splits, tab moves, and
  // other layout changes from killing running terminal processes.
  useEffect(() => {
    return () => {
      if (xtermRef.current) {
        xtermRef.current.dispose();
        xtermRef.current = null;
        addonsRef.current = null;
      }
    };
  }, []);

  // XtermTerminal stays mounted while slots move between panes. When a new
  // slot owner provides a fresh ref callback, hand the live terminal handle to
  // it even though initialization does not re-run.
  useEffect(() => {
    const terminal = xtermRef.current;
    if (!isInitialized || !terminal || !onTerminalRef) return;

    onTerminalRef({
      focus: () => terminal.focus(),
      showSearch: () => setIsSearchVisible(true),
      terminal,
    });
  }, [isInitialized, onTerminalRef]);

  // Listen for portal-target changes from TerminalHost; force a fit + repaint
  // so PTY/xterm dims match the new slot before any TUI relies on them.
  useEffect(() => {
    if (!isInitialized) return;
    const handler = (event: Event) => {
      const detail = (event as CustomEvent<{ sessionId: string }>).detail;
      if (!detail || detail.sessionId !== sessionId) return;
      fitTerminal(4);
      const term = xtermRef.current;
      if (term) term.refresh(0, term.rows - 1);
    };
    window.addEventListener("athas-terminal-refit", handler);
    return () => window.removeEventListener("athas-terminal-refit", handler);
  }, [fitTerminal, isInitialized, sessionId]);

  useEffect(() => {
    if (!addonsRef.current || !terminalContainerRef.current || !isInitialized) return;

    let rafId: number | null = null;
    const resizeObserver = new ResizeObserver(() => {
      if (rafId) cancelAnimationFrame(rafId);
      rafId = requestAnimationFrame(() => {
        const container = terminalContainerRef.current;
        if (!addonsRef.current || !container) return;

        const rect = container.getBoundingClientRect();
        if (rect.width > 0 && rect.height > 0) {
          fitTerminal(3);
        }
      });
    });

    resizeObserver.observe(terminalContainerRef.current);
    const cleanupFit = fitTerminal(12);

    return () => {
      resizeObserver.disconnect();
      if (rafId) cancelAnimationFrame(rafId);
      cleanupFit?.();
    };
  }, [fitTerminal, isInitialized]);

  useEffect(() => {
    if (!isActive || !isVisible || !xtermRef.current || !isInitialized) return;

    let cancelled = false;

    // Fit the terminal first to recalculate dimensions after display:none → display:flex
    const cleanupFit = fitTerminal(6);

    // Focus with verified retry — wait for layout to fully settle after tab switch
    const ensureFocus = (attempt: number) => {
      if (cancelled || !xtermRef.current || attempt >= 8) return;

      xtermRef.current.focus();

      // Verify the textarea actually received focus
      requestAnimationFrame(() => {
        if (cancelled || !xtermRef.current) return;
        const textarea = xtermRef.current.textarea;
        if (textarea && document.activeElement !== textarea) {
          ensureFocus(attempt + 1);
        }
      });
    };

    // Wait 2 frames for DOM layout to settle after display change, then focus
    requestAnimationFrame(() => {
      requestAnimationFrame(() => {
        if (!cancelled) ensureFocus(0);
      });
    });

    return () => {
      cancelled = true;
      cleanupFit?.();
    };
  }, [isActive, isInitialized, isVisible, fitTerminal]);

  useEffect(() => {
    if (!isInitialized || !addonsRef.current) return;

    const disposable = addonsRef.current.searchAddon.onDidChangeResults(
      ({ resultIndex, resultCount }) => {
        setSearchResults({
          current: resultCount > 0 && resultIndex >= 0 ? resultIndex + 1 : 0,
          total: resultCount,
        });
      },
    );

    return () => disposable.dispose();
  }, [isInitialized]);

  const handleZoom = useCallback(
    (delta: number) => {
      const newSize = Math.min(Math.max(terminalFontSize + delta, 8), 32);
      useSettingsStore.getState().updateSetting("terminalFontSize", newSize);
      if (xtermRef.current) {
        xtermRef.current.options.fontSize = newSize;
        fitTerminal(4);
      }
    },
    [fitTerminal, terminalFontSize],
  );

  const handleZoomReset = useCallback(() => {
    useSettingsStore.getState().updateSetting("terminalFontSize", 14);
    if (xtermRef.current) {
      xtermRef.current.options.fontSize = 14;
      fitTerminal(4);
    }
  }, [fitTerminal]);

  const getSearchOptions = useCallback((options: TerminalSearchOptions): ISearchOptions => {
    const rootStyles = getComputedStyle(document.documentElement);
    const selected = rootStyles.getPropertyValue("--color-selected").trim() || "#3b82f6";
    const accent = rootStyles.getPropertyValue("--color-accent").trim() || "#60a5fa";
    const border = rootStyles.getPropertyValue("--color-border").trim() || "#4b5563";

    return {
      caseSensitive: options.caseSensitive,
      wholeWord: options.wholeWord,
      regex: options.regex,
      decorations: {
        matchBackground: selected,
        matchBorder: border,
        matchOverviewRuler: selected,
        activeMatchBackground: accent,
        activeMatchBorder: border,
        activeMatchColorOverviewRuler: accent,
      },
    };
  }, []);

  const clearSearch = useCallback(() => {
    addonsRef.current?.searchAddon.clearDecorations();
    xtermRef.current?.clearSelection();
    setSearchResults({ current: 0, total: 0 });
  }, []);

  useEffect(() => {
    if (!isActive) return;

    const handleKeyDown = (event: KeyboardEvent) => {
      const isTerminalFocused =
        terminalContainerRef.current?.contains(event.target as Node) ||
        terminalContainerRef.current?.contains(document.activeElement);
      const key = event.key.toLowerCase();

      if (
        (event.ctrlKey || event.metaKey) &&
        key === "f" &&
        (isTerminalFocused || isSearchVisible)
      ) {
        event.preventDefault();
        event.stopPropagation();
        setIsSearchVisible(true);
      }

      if (event.key === "Escape" && isSearchVisible) {
        event.preventDefault();
        setIsSearchVisible(false);
        clearSearch();
        xtermRef.current?.focus();
      }

      if (isTerminalFocused && (event.ctrlKey || event.metaKey)) {
        if (event.key === "+" || event.key === "=") {
          event.preventDefault();
          handleZoom(2);
        } else if (event.key === "-") {
          event.preventDefault();
          handleZoom(-2);
        } else if (event.key === "0") {
          event.preventDefault();
          handleZoomReset();
        }
      }
    };

    window.addEventListener("keydown", handleKeyDown, true);
    return () => window.removeEventListener("keydown", handleKeyDown, true);
  }, [clearSearch, handleZoom, handleZoomReset, isActive, isSearchVisible]);

  const handleSearch = useCallback(
    (term: string, options: TerminalSearchOptions) => {
      if (!term || !addonsRef.current) {
        clearSearch();
        return;
      }

      const found = addonsRef.current.searchAddon.findNext(term, {
        ...getSearchOptions(options),
        incremental: true,
      });

      if (!found) {
        setSearchResults({ current: 0, total: 0 });
      }
    },
    [clearSearch, getSearchOptions],
  );

  const handleSearchNext = useCallback(
    (term: string, options: TerminalSearchOptions) => {
      if (!term || !addonsRef.current) return;
      addonsRef.current.searchAddon.findNext(term, getSearchOptions(options));
    },
    [getSearchOptions],
  );

  const handleSearchPrevious = useCallback(
    (term: string, options: TerminalSearchOptions) => {
      if (!term || !addonsRef.current) return;
      addonsRef.current.searchAddon.findPrevious(term, getSearchOptions(options));
    },
    [getSearchOptions],
  );

  const handleSearchClose = useCallback(() => {
    setIsSearchVisible(false);
    clearSearch();
    xtermRef.current?.focus();
  }, [clearSearch]);

  React.useImperativeHandle(
    getSession(sessionId)?.ref,
    () => ({
      terminal: xtermRef.current,
      searchAddon: addonsRef.current?.searchAddon,
      focus: () => xtermRef.current?.focus(),
      showSearch: () => setIsSearchVisible(true),
      blur: () => xtermRef.current?.blur(),
      clear: () => xtermRef.current?.clear(),
      selectAll: () => xtermRef.current?.selectAll(),
      clearSelection: () => xtermRef.current?.clearSelection(),
      getSelection: () => xtermRef.current?.getSelection() || "",
      paste: (text: string) => xtermRef.current?.paste(text),
      scrollToTop: () => xtermRef.current?.scrollToTop(),
      scrollToBottom: () => xtermRef.current?.scrollToBottom(),
      findNext: (term: string) => addonsRef.current?.searchAddon.findNext(term),
      findPrevious: (term: string) => addonsRef.current?.searchAddon.findPrevious(term),
      serialize: () => (xtermRef.current ? addonsRef.current?.serializeAddon.serialize() : ""),
      resize: () => fitTerminal(4),
    }),
    [fitTerminal, getSession, isInitialized, sessionId],
  );

  return (
    <div className="relative flex h-full w-full flex-col overflow-hidden bg-primary-bg">
      <TerminalSearch
        isVisible={isSearchVisible}
        onSearch={handleSearch}
        onNext={handleSearchNext}
        onPrevious={handleSearchPrevious}
        onClose={handleSearchClose}
        currentMatch={searchResults.current}
        totalMatches={searchResults.total}
      />
      <div className="flex min-h-0 flex-1 flex-col pl-[16px]">
        <div
          ref={terminalContainerRef}
          id={`terminal-${sessionId}`}
          data-terminal-drop-target
          data-terminal-session-id={sessionId}
          className={`xterm-container flex h-full min-h-0 flex-1 text-text ${!isActive ? "opacity-60" : ""}`}
          onDragOver={handleTerminalDragOver}
          onDrop={handleTerminalFileDrop}
          onMouseDown={() => {
            requestAnimationFrame(() => xtermRef.current?.focus());
          }}
        />
      </div>
    </div>
  );
};
