import {
  Fragment,
  useEffect,
  useRef,
  useState,
  useCallback,
  useMemo,
  memo,
  type KeyboardEvent
} from 'react'
import {
  Plus,
  X,
  ChevronLeft,
  ChevronRight,
  FileCode,
  FileText,
  GitCompareArrows,
  Loader2,
  AlertCircle,
  Check,
  TerminalSquare
} from 'lucide-react'
import { useShallow } from 'zustand/react/shallow'
import { useSessionStore } from '@/stores/useSessionStore'
import {
  useFileViewerStore,
  type FileViewerTab,
  type DiffTab,
  type ContextTab
} from '@/stores/useFileViewerStore'
import { useWorktreeStore } from '@/stores/useWorktreeStore'
import { useProjectStore } from '@/stores/useProjectStore'
import { useConnectionStore } from '@/stores/useConnectionStore'
import { useSettingsStore } from '@/stores/useSettingsStore'
import { useWorktreeStatusStore } from '@/stores/useWorktreeStatusStore'
import { useSessionRuntimeStore } from '@/stores/useSessionRuntimeStore'
import { useLayoutStore } from '@/stores/useLayoutStore'
import { useVimModeStore } from '@/stores/useVimModeStore'
import { useHintStore } from '@/stores/useHintStore'
import { cn, parseColorQuad } from '@/lib/utils'
import { toast } from '@/lib/toast'
import { assignSessionHints } from '@/lib/hint-utils'
import { HintBadge } from '@/components/ui/HintBadge'
import { useI18n } from '@/i18n/useI18n'
import { NewSessionDialog } from './NewSessionDialog'
import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuSeparator,
  ContextMenuShortcut,
  ContextMenuSub,
  ContextMenuSubContent,
  ContextMenuSubTrigger,
  ContextMenuTrigger
} from '@/components/ui/context-menu'
import { SessionStatusIndicator } from '@/components/layout/SessionStatusIndicator'

/**
 * Session color palette (Catppuccin-inspired).
 * Each entry is a ConnectionColorQuad: [inactiveBg, activeBg, inactiveText, activeText].
 * Stored in DB as JSON string to match connection color format.
 */
const SESSION_COLOR_PALETTE: Array<{ label: string; value: string; swatch: string }> = [
  {
    label: 'Lavender',
    swatch: '#b4befe',
    value: JSON.stringify(['#b4befe40', '#b4befe', '#1e1e2e', '#1e1e2e'])
  },
  {
    label: 'Peach',
    swatch: '#fab387',
    value: JSON.stringify(['#fab38740', '#fab387', '#1e1e2e', '#1e1e2e'])
  },
  {
    label: 'Green',
    swatch: '#a6e3a1',
    value: JSON.stringify(['#a6e3a140', '#a6e3a1', '#1e1e2e', '#1e1e2e'])
  },
  {
    label: 'Pink',
    swatch: '#f5c2e7',
    value: JSON.stringify(['#f5c2e740', '#f5c2e7', '#1e1e2e', '#1e1e2e'])
  },
  {
    label: 'Teal',
    swatch: '#94e2d5',
    value: JSON.stringify(['#94e2d540', '#94e2d5', '#1e1e2e', '#1e1e2e'])
  }
]

interface SessionTabProps {
  sessionId: string
  name: string
  isActive: boolean
  agentSdk: 'opencode' | 'claude-code' | 'codex' | 'terminal'
  color: string | null
  onClick: () => void
  onClose: (e: React.MouseEvent) => void
  onMiddleClick: (e: React.MouseEvent) => void
  onRename: (newName: string) => void
  onSetColor: (color: string | null) => void
  onDragStart: (e: React.DragEvent) => void
  onDragOver: (e: React.DragEvent) => void
  onDrop: (e: React.DragEvent) => void
  onDragEnd: () => void
  isDragging: boolean
  isDragOver: boolean
  worktreeId: string
  onCloseOthers: () => void
  onCloseToRight: () => void
  hintCode?: string
}

const SessionTab = memo(function SessionTab({
  sessionId,
  name,
  isActive,
  agentSdk,
  color,
  onClick,
  onClose,
  onMiddleClick,
  onRename,
  onSetColor,
  onDragStart,
  onDragOver,
  onDrop,
  onDragEnd,
  isDragging,
  isDragOver,
  worktreeId: _worktreeId,
  onCloseOthers,
  onCloseToRight,
  hintCode
}: SessionTabProps): React.JSX.Element {
  const { t } = useI18n()
  const [isEditing, setIsEditing] = useState(false)
  const [editName, setEditName] = useState(name)
  const inputRef = useRef<HTMLInputElement>(null)

  const vimMode = useVimModeStore((s) => s.mode)
  const vimModeEnabled = useSettingsStore((s) => s.vimModeEnabled)
  const hintMode = useHintStore((s) => s.mode)
  const hintPendingChar = useHintStore((s) => s.pendingChar)
  const hintActionMode = useHintStore((s) => s.actionMode)

  const sessionStatus = useWorktreeStatusStore(
    (state) => state.sessionStatuses[sessionId]?.status ?? null
  )

  // Focus and select input text when entering edit mode
  useEffect(() => {
    if (isEditing && inputRef.current) {
      inputRef.current.focus()
      inputRef.current.select()
    }
  }, [isEditing])

  const handleDoubleClick = (e: React.MouseEvent) => {
    e.stopPropagation()
    setEditName(name)
    setIsEditing(true)
  }

  const handleSave = () => {
    const trimmed = editName.trim()
    if (trimmed && trimmed !== name) {
      onRename(trimmed)
    }
    setIsEditing(false)
  }

  const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
    if (e.key === 'Enter') {
      e.preventDefault()
      handleSave()
    } else if (e.key === 'Escape') {
      e.preventDefault()
      setEditName(name)
      setIsEditing(false)
    }
  }

  // Resolve background/text when a custom color is set
  const colorStyle = useMemo(() => {
    if (!color) return undefined
    const [inactiveBg, activeBg, inactiveText, activeText] = parseColorQuad(color)
    return {
      backgroundColor: isActive ? activeBg : inactiveBg,
      color: isActive ? activeText : inactiveText
    }
  }, [color, isActive])

  return (
    <ContextMenu>
      <ContextMenuTrigger asChild>
        <div
          data-testid={`session-tab-${sessionId}`}
          draggable={!isEditing}
          onDragStart={onDragStart}
          onDragOver={onDragOver}
          onDrop={onDrop}
          onDragEnd={onDragEnd}
          onClick={isEditing ? undefined : onClick}
          onDoubleClick={handleDoubleClick}
          onMouseDown={(e) => {
            // Middle click to close
            if (e.button === 1) {
              onMiddleClick(e)
            }
          }}
          className={cn(
            'group relative flex items-center gap-1 px-3 py-1 text-sm cursor-pointer select-none',
            'rounded-md transition-colors min-w-[100px] max-w-[200px] my-0.5 mx-0.5',
            !color && (
              isActive
                ? 'bg-background text-foreground shadow-sm'
                : 'text-muted-foreground hover:bg-background/50 hover:text-foreground'
            ),
            color && isActive && 'shadow-sm',
            isDragging && 'opacity-50',
            isDragOver && 'bg-accent/50'
          )}
          style={colorStyle}
        >
          {agentSdk === 'terminal' ? (
            <TerminalSquare
              className="h-3 w-3 text-emerald-500 flex-shrink-0"
              data-testid={`tab-terminal-${sessionId}`}
            />
          ) : (
            <>
              {(sessionStatus === 'working' || sessionStatus === 'planning') && (
                <Loader2
                  className={cn(
                    'h-3 w-3 animate-spin flex-shrink-0',
                    sessionStatus === 'planning' ? 'text-blue-400' : 'text-blue-500'
                  )}
                  data-testid={`tab-spinner-${sessionId}`}
                />
              )}
              {(sessionStatus === 'answering' || sessionStatus === 'permission') && (
                <AlertCircle
                  className="h-3 w-3 text-amber-500 flex-shrink-0"
                  data-testid={`tab-${sessionStatus === 'permission' ? 'permission' : 'answering'}-${sessionId}`}
                />
              )}
              {sessionStatus === 'completed' && (
                <Check
                  className="h-3 w-3 text-green-500 flex-shrink-0"
                  data-testid={`tab-completed-${sessionId}`}
                />
              )}
              {sessionStatus === 'unread' && !isActive && (
                <span
                  className="w-2 h-2 rounded-full bg-primary flex-shrink-0"
                  data-testid={`tab-unread-${sessionId}`}
                />
              )}
            </>
          )}
          {isEditing ? (
            <input
              ref={inputRef}
              type="text"
              value={editName}
              onChange={(e) => setEditName(e.target.value)}
              onBlur={handleSave}
              onKeyDown={handleKeyDown}
              className="flex-1 min-w-0 bg-transparent border border-primary/50 rounded px-1 py-0 text-sm outline-none"
              data-testid={`rename-input-${sessionId}`}
            />
          ) : (
            <span className="truncate flex-1">{name || t('sessionTabs.common.untitled')}</span>
          )}
          {hintCode && vimModeEnabled && vimMode === 'normal' && (
            <HintBadge
              code={hintCode}
              mode={hintMode}
              pendingChar={hintPendingChar}
              actionMode={hintActionMode}
            />
          )}
          <button
            onClick={onClose}
            className={cn(
              'p-0.5 rounded hover:bg-accent transition-opacity',
              isActive ? 'opacity-100' : 'opacity-0 group-hover:opacity-100'
            )}
            data-testid={`close-tab-${sessionId}`}
          >
            <X className="h-3 w-3" />
          </button>
        </div>
      </ContextMenuTrigger>
      <ContextMenuContent>
        <ContextMenuItem
          onSelect={() => {
            setEditName(name)
            setIsEditing(true)
          }}
        >
          {t('sessionTabs.menu.rename')}
        </ContextMenuItem>
        <ContextMenuSeparator />
        <ContextMenuItem onSelect={(e) => onClose(e as unknown as React.MouseEvent)}>
          {t('sessionTabs.menu.close')}
          <ContextMenuShortcut>&#8984;W</ContextMenuShortcut>
        </ContextMenuItem>
        <ContextMenuItem onSelect={onCloseOthers}>
          {t('sessionTabs.menu.closeOthers')}
        </ContextMenuItem>
        <ContextMenuItem onSelect={onCloseToRight}>
          {t('sessionTabs.menu.closeToRight')}
        </ContextMenuItem>
        <ContextMenuSeparator />
        <ContextMenuSub>
          <ContextMenuSubTrigger>
            <span className="flex items-center gap-2">
              {color ? (
                <span
                  className="h-3 w-3 rounded-full border border-border/40"
                  style={{ backgroundColor: parseColorQuad(color)[1] }}
                />
              ) : (
                <span className="h-3 w-3 rounded-full border border-border/40 bg-transparent" />
              )}
              {t('sessionTabs.menu.setColor')}
            </span>
          </ContextMenuSubTrigger>
          <ContextMenuSubContent>
            {SESSION_COLOR_PALETTE.map((c) => (
              <ContextMenuItem key={c.label} onSelect={() => onSetColor(c.value)}>
                <span className="flex items-center gap-2">
                  <span
                    className="h-3 w-3 rounded-full"
                    style={{ backgroundColor: c.swatch }}
                  />
                  {c.label}
                </span>
              </ContextMenuItem>
            ))}
            <ContextMenuSeparator />
            <ContextMenuItem onSelect={() => onSetColor(null)}>
              <span className="flex items-center gap-2 text-muted-foreground">
                <span className="h-3 w-3 rounded-full border border-border/40" />
                {t('sessionTabs.menu.clearColor')}
              </span>
            </ContextMenuItem>
          </ContextMenuSubContent>
        </ContextMenuSub>
      </ContextMenuContent>
    </ContextMenu>
  )
})

interface FileTabProps {
  filePath: string
  name: string
  isActive: boolean
  onClick: () => void
  onClose: (e: React.MouseEvent) => void
  onCloseOthers: () => void
  onCloseToRight: () => void
  relativePath: string
}

function FileTab({
  filePath,
  name,
  isActive,
  onClick,
  onClose,
  onCloseOthers,
  onCloseToRight,
  relativePath
}: FileTabProps): React.JSX.Element {
  const { t } = useI18n()
  const isDirty = useFileViewerStore((s) => s.dirtyFiles.has(filePath))

  return (
    <ContextMenu>
      <ContextMenuTrigger asChild>
        <div
          data-testid={`file-tab-${name}`}
          onClick={onClick}
          onMouseDown={(e) => {
            if (e.button === 1) {
              e.preventDefault()
              onClose(e)
            }
          }}
          className={cn(
            'group relative flex items-center gap-1.5 px-3 py-1 text-sm cursor-pointer select-none',
            'rounded-md transition-colors min-w-[100px] max-w-[200px] my-0.5 mx-0.5',
            isActive
              ? 'bg-background text-foreground shadow-sm'
              : 'text-muted-foreground hover:bg-background/50 hover:text-foreground'
          )}
          title={filePath}
        >
          <FileCode className="h-3.5 w-3.5 flex-shrink-0 text-blue-400" />
          <span className="truncate flex-1">{name}</span>
          {isDirty ? (
            <>
              <span
                className="w-2 h-2 rounded-full bg-current group-hover:hidden"
                data-testid={`dirty-indicator-${name}`}
              />
              <button
                onClick={onClose}
                className="hidden group-hover:block p-0.5 rounded hover:bg-accent"
                data-testid={`close-file-tab-${name}`}
              >
                <X className="h-3 w-3" />
              </button>
            </>
          ) : (
            <button
              onClick={onClose}
              className={cn(
                'p-0.5 rounded hover:bg-accent transition-opacity',
                isActive ? 'opacity-100' : 'opacity-0 group-hover:opacity-100'
              )}
              data-testid={`close-file-tab-${name}`}
            >
              <X className="h-3 w-3" />
            </button>
          )}
        </div>
      </ContextMenuTrigger>
      <ContextMenuContent>
        <ContextMenuItem onSelect={(e) => onClose(e as unknown as React.MouseEvent)}>
          {t('sessionTabs.menu.close')}
          <ContextMenuShortcut>&#8984;W</ContextMenuShortcut>
        </ContextMenuItem>
        <ContextMenuItem onSelect={onCloseOthers}>
          {t('sessionTabs.menu.closeOthers')}
        </ContextMenuItem>
        <ContextMenuItem onSelect={onCloseToRight}>
          {t('sessionTabs.menu.closeToRight')}
        </ContextMenuItem>
        <ContextMenuSeparator />
        <ContextMenuItem
          onSelect={() => copyToClipboard(relativePath, t('sessionTabs.toasts.copied'))}
        >
          {t('sessionTabs.menu.copyRelativePath')}
        </ContextMenuItem>
        <ContextMenuItem onSelect={() => copyToClipboard(filePath, t('sessionTabs.toasts.copied'))}>
          {t('sessionTabs.menu.copyAbsolutePath')}
        </ContextMenuItem>
      </ContextMenuContent>
    </ContextMenu>
  )
}

function copyToClipboard(text: string, successMessage: string) {
  navigator.clipboard.writeText(text)
  toast.success(successMessage)
}

interface DiffTabItemProps {
  tabKey: string
  tab: DiffTab
  isActive: boolean
  onActivate: () => void
  onClose: (e: React.MouseEvent) => void
  onCloseOthers: () => void
  onCloseToRight: () => void
}

function DiffTabItem({
  tabKey,
  tab,
  isActive,
  onActivate,
  onClose,
  onCloseOthers,
  onCloseToRight
}: DiffTabItemProps): React.JSX.Element {
  const { t } = useI18n()
  const absolutePath = `${tab.worktreePath}/${tab.filePath}`

  return (
    <ContextMenu>
      <ContextMenuTrigger asChild>
        <div
          data-testid={`diff-tab-${tab.fileName}`}
          onClick={onActivate}
          onMouseDown={(e) => {
            if (e.button === 1) {
              e.preventDefault()
              onClose(e)
            }
          }}
          className={cn(
            'group relative flex items-center gap-1.5 px-3 py-1 text-sm cursor-pointer select-none',
            'rounded-md transition-colors min-w-[100px] max-w-[200px] my-0.5 mx-0.5',
            isActive
              ? 'bg-background text-foreground shadow-sm'
              : 'text-muted-foreground hover:bg-background/50 hover:text-foreground'
          )}
          title={`${tab.filePath} (${tab.staged ? t('sessionTabs.common.staged') : t('sessionTabs.common.unstaged')})`}
        >
          <GitCompareArrows className="h-3.5 w-3.5 flex-shrink-0 text-orange-400" />
          <span className="truncate flex-1">{tab.fileName}</span>
          {tab.staged && <span className="text-[10px] text-green-500 font-medium shrink-0">S</span>}
          <button
            onClick={onClose}
            className={cn(
              'p-0.5 rounded hover:bg-accent transition-opacity',
              isActive ? 'opacity-100' : 'opacity-0 group-hover:opacity-100'
            )}
            data-testid={`close-diff-tab-${tabKey}`}
          >
            <X className="h-3 w-3" />
          </button>
        </div>
      </ContextMenuTrigger>
      <ContextMenuContent>
        <ContextMenuItem onSelect={(e) => onClose(e as unknown as React.MouseEvent)}>
          {t('sessionTabs.menu.close')}
          <ContextMenuShortcut>&#8984;W</ContextMenuShortcut>
        </ContextMenuItem>
        <ContextMenuItem onSelect={onCloseOthers}>
          {t('sessionTabs.menu.closeOthers')}
        </ContextMenuItem>
        <ContextMenuItem onSelect={onCloseToRight}>
          {t('sessionTabs.menu.closeToRight')}
        </ContextMenuItem>
        <ContextMenuSeparator />
        <ContextMenuItem
          onSelect={() => copyToClipboard(tab.filePath, t('sessionTabs.toasts.copied'))}
        >
          {t('sessionTabs.menu.copyRelativePath')}
        </ContextMenuItem>
        <ContextMenuItem
          onSelect={() => copyToClipboard(absolutePath, t('sessionTabs.toasts.copied'))}
        >
          {t('sessionTabs.menu.copyAbsolutePath')}
        </ContextMenuItem>
      </ContextMenuContent>
    </ContextMenu>
  )
}

// Sticky connection session tab — simplified, non-closable, non-draggable
interface ConnectionSessionTabProps {
  sessionId: string
  name: string
  isActive: boolean
  onClick: () => void
  connectionColor: string | null
  connectionName: string
}

const ConnectionSessionTab = memo(function ConnectionSessionTab({
  sessionId,
  name,
  isActive,
  onClick,
  connectionColor,
  connectionName
}: ConnectionSessionTabProps): React.JSX.Element {
  const { t } = useI18n()
  const sessionStatus = useWorktreeStatusStore(
    (state) => state.sessionStatuses[sessionId]?.status ?? null
  )

  const [inactiveBg, activeBg, inactiveText, activeText] = parseColorQuad(connectionColor)

  return (
    <div
      data-testid={`connection-session-tab-${sessionId}`}
      role="tab"
      tabIndex={0}
      onClick={onClick}
      onKeyDown={(e) => {
        if (e.key === 'Enter' || e.key === ' ') onClick()
      }}
      title={`${connectionName} — ${name || t('sessionTabs.common.untitled')}`}
      className={cn(
        'group relative flex items-center gap-1.5 px-3 py-1 text-sm cursor-pointer select-none',
        'rounded-md transition-colors min-w-[100px] max-w-[200px] my-0.5 mx-0.5'
      )}
      style={{
        backgroundColor: isActive ? activeBg : inactiveBg,
        color: isActive ? activeText : inactiveText
      }}
    >
      {/* Status indicators */}
      {(sessionStatus === 'working' || sessionStatus === 'planning') && (
        <Loader2
          className="h-3 w-3 animate-spin flex-shrink-0"
          style={{ color: isActive ? activeText : undefined }}
        />
      )}
      {(sessionStatus === 'answering' || sessionStatus === 'permission') && (
        <AlertCircle
          className="h-3 w-3 flex-shrink-0"
          style={{ color: isActive ? activeText : undefined }}
        />
      )}
      {sessionStatus === 'completed' && (
        <Check
          className="h-3 w-3 flex-shrink-0"
          style={{ color: isActive ? activeText : undefined }}
        />
      )}
      {sessionStatus === 'unread' && !isActive && (
        <span className="w-2 h-2 rounded-full bg-blue-500 flex-shrink-0" />
      )}

      <span className="truncate flex-1">{name || t('sessionTabs.common.untitled')}</span>
    </div>
  )
})

export function SessionTabs(): React.JSX.Element | null {
  const { t } = useI18n()
  const tabsContainerRef = useRef<HTMLDivElement>(null)
  const [showLeftArrow, setShowLeftArrow] = useState(false)
  const [showRightArrow, setShowRightArrow] = useState(false)
  const [draggedTabId, setDraggedTabId] = useState<string | null>(null)
  const [dragOverTabId, setDragOverTabId] = useState<string | null>(null)
  const [newSessionDialogOpen, setNewSessionDialogOpen] = useState(false)

  // Individual selectors — only re-render when the subscribed field changes
  const activeWorktreeId = useSessionStore((s) => s.activeWorktreeId)
  const activeSessionId = useSessionStore((s) => s.activeSessionId)
  const inlineConnectionSessionId = useSessionStore((s) => s.inlineConnectionSessionId)

  // Collection fields via useShallow — re-render only when references change
  const { sessionsByWorktree, tabOrderByWorktree, sessionsByConnection, tabOrderByConnection } =
    useSessionStore(
      useShallow((s) => ({
        sessionsByWorktree: s.sessionsByWorktree,
        tabOrderByWorktree: s.tabOrderByWorktree,
        sessionsByConnection: s.sessionsByConnection,
        tabOrderByConnection: s.tabOrderByConnection
      }))
    )

  // Method selectors — stable references, never cause re-renders
  const loadSessions = useSessionStore((s) => s.loadSessions)
  const createSession = useSessionStore((s) => s.createSession)
  const closeSession = useSessionStore((s) => s.closeSession)
  const setActiveSession = useSessionStore((s) => s.setActiveSession)
  const reorderTabs = useSessionStore((s) => s.reorderTabs)
  const updateSessionName = useSessionStore((s) => s.updateSessionName)
  const updateSessionColor = useSessionStore((s) => s.updateSessionColor)
  const closeOtherSessions = useSessionStore((s) => s.closeOtherSessions)
  const closeSessionsToRight = useSessionStore((s) => s.closeSessionsToRight)
  const loadConnectionSessions = useSessionStore((s) => s.loadConnectionSessions)
  const createConnectionSession = useSessionStore((s) => s.createConnectionSession)
  const setActiveConnectionSession = useSessionStore((s) => s.setActiveConnectionSession)
  const reorderConnectionTabs = useSessionStore((s) => s.reorderConnectionTabs)
  const closeOtherConnectionSessions = useSessionStore((s) => s.closeOtherConnectionSessions)
  const closeConnectionSessionsToRight = useSessionStore((s) => s.closeConnectionSessionsToRight)
  const setInlineConnectionSession = useSessionStore((s) => s.setInlineConnectionSession)
  const clearInlineConnectionSession = useSessionStore((s) => s.clearInlineConnectionSession)
  const loadConnectionSessionsBackground = useSessionStore(
    (s) => s.loadConnectionSessionsBackground
  )

  const {
    openFiles,
    activeFilePath,
    setActiveFile,
    closeOtherFiles,
    closeFilesToRight,
    requestCloseFile
  } = useFileViewerStore()

  const { selectedWorktreeId } = useWorktreeStore()
  const { projects } = useProjectStore()
  const selectedConnectionId = useConnectionStore((state) => state.selectedConnectionId)
  const connections = useConnectionStore((state) => state.connections)
  const pushGhosttySuppression = useLayoutStore((state) => state.pushGhosttySuppression)
  const popGhosttySuppression = useLayoutStore((state) => state.popGhosttySuppression)

  // Determine whether we are in connection mode or worktree mode
  const isConnectionMode = !!selectedConnectionId && !selectedWorktreeId

  // Get the worktree and project info for the selected worktree
  const selectedWorktree = useWorktreeStore((state) => {
    if (!selectedWorktreeId) return null
    for (const worktrees of state.worktreesByProject.values()) {
      const found = worktrees.find((w) => w.id === selectedWorktreeId)
      if (found) return found
    }
    return null
  })

  const project = selectedWorktree
    ? projects.find((p) => p.id === selectedWorktree.project_id)
    : null

  // Sync active worktree with selected worktree (worktree mode only)
  useEffect(() => {
    if (isConnectionMode) return
    if (selectedWorktreeId !== activeWorktreeId) {
      useSessionStore.getState().setActiveWorktree(selectedWorktreeId)
    }
  }, [selectedWorktreeId, activeWorktreeId, isConnectionMode])

  // Sync active connection with selected connection (connection mode only)
  useEffect(() => {
    if (!isConnectionMode || !selectedConnectionId) return
    useSessionStore.getState().setActiveConnection(selectedConnectionId)
  }, [selectedConnectionId, isConnectionMode])

  // Load sessions when worktree changes, then auto-start if the worktree has 0 sessions.
  // Auto-start runs as a direct follow-up to loadSessions (not a separate effect) to
  // eliminate race conditions between the two async operations.
  const autoStartSession = useSettingsStore((state) => state.autoStartSession)
  const availableAgentSdks = useSettingsStore((state) => state.availableAgentSdks)
  const autoStartedRef = useRef<string | null>(null)

  useEffect(() => {
    if (isConnectionMode) return
    if (!selectedWorktreeId || !project) return

    let cancelled = false

    void (async () => {
      await loadSessions(selectedWorktreeId, project.id)
      if (cancelled) return

      // After sessions are loaded, check if auto-start is needed
      if (!autoStartSession) return
      if (autoStartedRef.current === selectedWorktreeId) return

      const sessions = useSessionStore.getState().sessionsByWorktree.get(selectedWorktreeId) || []
      if (sessions.length > 0) return

      autoStartedRef.current = selectedWorktreeId
      await createSession(selectedWorktreeId, project.id)
    })()

    return () => {
      cancelled = true
    }
  }, [selectedWorktreeId, project, loadSessions, autoStartSession, createSession, isConnectionMode])

  // Load sessions when connection changes (connection mode)
  useEffect(() => {
    if (!isConnectionMode || !selectedConnectionId) return

    let cancelled = false

    void (async () => {
      await loadConnectionSessions(selectedConnectionId)
      if (cancelled) return

      // Auto-start a session if auto-start is enabled and no sessions exist
      if (!autoStartSession) return
      if (autoStartedRef.current === selectedConnectionId) return

      const sessions =
        useSessionStore.getState().sessionsByConnection.get(selectedConnectionId) || []
      if (sessions.length > 0) return

      autoStartedRef.current = selectedConnectionId
      await createConnectionSession(selectedConnectionId)
    })()

    return () => {
      cancelled = true
    }
  }, [
    selectedConnectionId,
    isConnectionMode,
    loadConnectionSessions,
    autoStartSession,
    createConnectionSession
  ])

  // Connections that include the currently selected worktree (for sticky tabs)
  const connectionsForWorktree = useMemo(() => {
    if (!selectedWorktreeId || isConnectionMode) return []
    return connections.filter((c) => c.members.some((m) => m.worktree_id === selectedWorktreeId))
  }, [connections, selectedWorktreeId, isConnectionMode])

  // Track which connection IDs have already been background-loaded for the current worktree.
  const loadedConnectionsRef = useRef<Set<string>>(new Set())

  // Reset loaded set when the selected worktree changes
  useEffect(() => {
    loadedConnectionsRef.current.clear()
  }, [selectedWorktreeId])

  // Background-load sessions for all connections the selected worktree belongs to.
  // This is non-blocking and does not enter connection mode.
  useEffect(() => {
    if (isConnectionMode || !selectedWorktreeId || connectionsForWorktree.length === 0) return
    for (const connection of connectionsForWorktree) {
      if (!loadedConnectionsRef.current.has(connection.id)) {
        loadedConnectionsRef.current.add(connection.id)
        loadConnectionSessionsBackground(connection.id)
      }
    }
  }, [
    selectedWorktreeId,
    connectionsForWorktree,
    isConnectionMode,
    loadConnectionSessionsBackground
  ])

  // Check for tab overflow and update arrow visibility
  const checkOverflow = useCallback(() => {
    const container = tabsContainerRef.current
    if (!container) return

    setShowLeftArrow(container.scrollLeft > 0)
    setShowRightArrow(container.scrollLeft < container.scrollWidth - container.clientWidth - 1)
  }, [])

  useEffect(() => {
    checkOverflow()
    const container = tabsContainerRef.current
    if (!container) return

    container.addEventListener('scroll', checkOverflow)
    window.addEventListener('resize', checkOverflow)
    return () => {
      container.removeEventListener('scroll', checkOverflow)
      window.removeEventListener('resize', checkOverflow)
    }
  }, [
    checkOverflow,
    sessionsByWorktree,
    tabOrderByWorktree,
    sessionsByConnection,
    tabOrderByConnection,
    openFiles
  ])

  // Safety: never leave Ghostty overlays suppressed if this component unmounts.
  useEffect(() => {
    return () => {
      popGhosttySuppression('session-tabs-context')
    }
  }, [popGhosttySuppression])

  // Scroll functions
  const scrollLeft = () => {
    const container = tabsContainerRef.current
    if (container) {
      container.scrollBy({ left: -150, behavior: 'smooth' })
    }
  }

  const scrollRight = () => {
    const container = tabsContainerRef.current
    if (container) {
      container.scrollBy({ left: 150, behavior: 'smooth' })
    }
  }

  // Handle creating a new session — opens the dialog so the user can pick
  // name + provider + model. The right-click menu still bypasses the dialog
  // for power users.
  const handleCreateSession = () => {
    if (isConnectionMode && selectedConnectionId) {
      setNewSessionDialogOpen(true)
      return
    }
    if (!selectedWorktreeId || !project) return
    setNewSessionDialogOpen(true)
  }

  // Handle creating a new session with a specific agent SDK (from context menu)
  const handleCreateSessionWithSdk = async (
    sdk: 'opencode' | 'claude-code' | 'codex' | 'terminal'
  ) => {
    if (isConnectionMode && selectedConnectionId) {
      const result = await createConnectionSession(selectedConnectionId, sdk)
      if (!result.success) {
        toast.error(result.error || t('sessionTabs.errors.createSession'))
      }
      return
    }

    if (!selectedWorktreeId || !project) return

    const result = await createSession(selectedWorktreeId, project.id, sdk)
    if (!result.success) {
      toast.error(result.error || t('sessionTabs.errors.createSession'))
    }
  }

  // Handle closing a session
  const handleCloseSession = async (e: React.MouseEvent, sessionId: string) => {
    e.stopPropagation()
    const result = await closeSession(sessionId)
    if (!result.success) {
      toast.error(result.error || t('sessionTabs.errors.closeSession'))
    } else {
      // P1-4 CR fix: clean up runtime store to prevent memory leak
      useSessionRuntimeStore.getState().clearSession(sessionId)
    }
  }

  // Handle renaming a session tab
  const handleRenameSession = async (sessionId: string, newName: string) => {
    const success = await updateSessionName(sessionId, newName)
    if (!success) {
      toast.error(t('sessionTabs.errors.renameSession'))
    }
  }

  const handleSetSessionColor = async (sessionId: string, color: string | null) => {
    const success = await updateSessionColor(sessionId, color)
    if (!success) {
      toast.error(t('sessionTabs.errors.setColor'))
    }
  }

  // Handle clicking a session tab - deactivate file tab and clear unread status
  const handleSessionTabClick = (sessionId: string) => {
    setActiveFile(null)
    clearInlineConnectionSession()
    if (isConnectionMode) {
      setActiveConnectionSession(sessionId)
    } else {
      setActiveSession(sessionId)
    }
    useWorktreeStatusStore.getState().clearSessionStatus(sessionId)
  }

  // Handle clicking a sticky connection session tab (inline viewing in worktree mode)
  const handleConnectionSessionTabClick = (sessionId: string) => {
    setActiveFile(null)
    setInlineConnectionSession(sessionId)
    useWorktreeStatusStore.getState().clearSessionStatus(sessionId)
  }

  // Handle clicking a file tab - keep session but activate file view
  const handleFileTabClick = (filePath: string) => {
    setActiveFile(filePath)
  }

  // Handle closing a file tab
  const handleCloseFileTab = (e: React.MouseEvent, filePath: string) => {
    e.stopPropagation()
    requestCloseFile(filePath)
  }

  // Handle clicking a diff tab - restore activeDiff for the viewer
  const handleDiffTabClick = (tabKey: string) => {
    useFileViewerStore.getState().activateDiffTab(tabKey)
  }

  // Handle closing a diff tab
  const handleCloseDiffTab = (e: React.MouseEvent, tabKey: string) => {
    e.stopPropagation()
    useFileViewerStore.getState().closeDiffTab(tabKey)
  }

  // Drag and drop handlers
  const handleDragStart = (e: React.DragEvent, sessionId: string) => {
    setDraggedTabId(sessionId)
    e.dataTransfer.effectAllowed = 'move'
    e.dataTransfer.setData('text/plain', sessionId)
  }

  const handleDragOver = (e: React.DragEvent, sessionId: string) => {
    e.preventDefault()
    e.dataTransfer.dropEffect = 'move'
    if (draggedTabId && draggedTabId !== sessionId) {
      setDragOverTabId(sessionId)
    }
  }

  const handleDrop = (e: React.DragEvent, targetSessionId: string) => {
    e.preventDefault()
    if (!draggedTabId || draggedTabId === targetSessionId) {
      return
    }

    if (isConnectionMode && selectedConnectionId) {
      const tabOrder = tabOrderByConnection.get(selectedConnectionId) || []
      const fromIndex = tabOrder.indexOf(draggedTabId)
      const toIndex = tabOrder.indexOf(targetSessionId)

      if (fromIndex !== -1 && toIndex !== -1) {
        reorderConnectionTabs(selectedConnectionId, fromIndex, toIndex)
      }
    } else if (selectedWorktreeId) {
      const tabOrder = tabOrderByWorktree.get(selectedWorktreeId) || []
      const fromIndex = tabOrder.indexOf(draggedTabId)
      const toIndex = tabOrder.indexOf(targetSessionId)

      if (fromIndex !== -1 && toIndex !== -1) {
        reorderTabs(selectedWorktreeId, fromIndex, toIndex)
      }
    }

    setDraggedTabId(null)
    setDragOverTabId(null)
  }

  const handleDragEnd = () => {
    setDraggedTabId(null)
    setDragOverTabId(null)
  }

  // Resolve scope ID for the active context (may be undefined)
  const scopeId = isConnectionMode ? selectedConnectionId : selectedWorktreeId

  // Get sessions in tab order (memoized for stable reference)
  const orderedSessions = useMemo(() => {
    const sessions = scopeId
      ? isConnectionMode
        ? sessionsByConnection.get(scopeId) || []
        : sessionsByWorktree.get(scopeId) || []
      : []
    const tabOrder = scopeId
      ? isConnectionMode
        ? tabOrderByConnection.get(scopeId) || []
        : tabOrderByWorktree.get(scopeId) || []
      : []
    return tabOrder
      .map((id) => sessions.find((s) => s.id === id))
      .filter((s): s is NonNullable<typeof s> => s !== undefined)
  }, [
    scopeId,
    isConnectionMode,
    sessionsByConnection,
    sessionsByWorktree,
    tabOrderByConnection,
    tabOrderByWorktree
  ])

  // Vim/hint state for session tab hints
  const vimMode = useVimModeStore((s) => s.mode)
  const vimModeEnabled = useSettingsStore((s) => s.vimModeEnabled)
  const setSessionHints = useHintStore((s) => s.setSessionHints)
  const clearSessionHints = useHintStore((s) => s.clearSessionHints)

  const sessionHints = useMemo(
    () => assignSessionHints(orderedSessions.map((s) => s.id)),
    [orderedSessions]
  )

  useEffect(() => {
    if (vimModeEnabled && vimMode === 'normal') {
      setSessionHints(sessionHints.sessionHintMap, sessionHints.sessionHintTargetMap)
    } else {
      clearSessionHints()
    }
    return () => {
      clearSessionHints()
    }
  }, [vimModeEnabled, vimMode, sessionHints, setSessionHints, clearSessionHints])

  // Don't render if nothing is selected
  if (!selectedWorktreeId && !selectedConnectionId) {
    return null
  }

  // After early return, scopeId is guaranteed to be defined
  const resolvedScopeId = scopeId!

  // Get file tabs for the current worktree (only regular file tabs, not diff tabs)
  const fileTabs = Array.from(openFiles.values()).filter(
    (f): f is FileViewerTab => f.type === 'file' && f.worktreeId === selectedWorktreeId
  )

  // Get diff tabs from openFiles
  const diffTabs = Array.from(openFiles.entries()).filter(
    (entry): entry is [string, DiffTab] => entry[1].type === 'diff'
  )

  // Get context tabs from openFiles
  const contextTabs = Array.from(openFiles.entries()).filter(
    (entry): entry is [string, ContextTab] => entry[1].type === 'context'
  )

  // Determine if a file/diff tab is the active one
  const isFileTabActive = activeFilePath !== null

  return (
    <div
      className="flex items-center border-b border-border/40 bg-muted/30 px-1"
      data-testid="session-tabs"
    >
      {/* New session button - on the left */}
      {/* Right-click shows provider menu with session type options */}
      <ContextMenu
        onOpenChange={(open) => {
          if (open) pushGhosttySuppression('session-tabs-context')
          else popGhosttySuppression('session-tabs-context')
        }}
      >
        <ContextMenuTrigger asChild>
          <button
            onClick={handleCreateSession}
            className="p-1.5 rounded-md hover:bg-background/50 transition-colors shrink-0"
            data-testid="create-session"
            title={t('sessionTabs.actions.createSession')}
          >
            <Plus className="h-4 w-4" />
          </button>
        </ContextMenuTrigger>
        <ContextMenuContent>
          {availableAgentSdks?.opencode && (
            <ContextMenuItem onSelect={() => handleCreateSessionWithSdk('opencode')}>
              {t('sessionTabs.actions.newOpenCode')}
            </ContextMenuItem>
          )}
          {availableAgentSdks?.claude && (
            <ContextMenuItem onSelect={() => handleCreateSessionWithSdk('claude-code')}>
              {t('sessionTabs.actions.newClaudeCode')}
            </ContextMenuItem>
          )}
          {availableAgentSdks?.codex && (
            <ContextMenuItem onSelect={() => handleCreateSessionWithSdk('codex')}>
              {t('sessionTabs.actions.newCodex')}
            </ContextMenuItem>
          )}
          {(availableAgentSdks?.opencode ||
            availableAgentSdks?.claude ||
            availableAgentSdks?.codex) && <ContextMenuSeparator />}
          <ContextMenuItem onSelect={() => handleCreateSessionWithSdk('terminal')}>
            <TerminalSquare className="h-4 w-4 mr-2 text-emerald-500" />
            {t('sessionTabs.actions.newTerminal')}
          </ContextMenuItem>
        </ContextMenuContent>
      </ContextMenu>

      <NewSessionDialog
        open={newSessionDialogOpen}
        onOpenChange={setNewSessionDialogOpen}
        worktreeId={isConnectionMode ? null : selectedWorktreeId}
        projectId={isConnectionMode ? null : project?.id ?? null}
        connectionId={isConnectionMode ? selectedConnectionId : null}
        defaultIndex={
          isConnectionMode && selectedConnectionId
            ? (sessionsByConnection.get(selectedConnectionId)?.length ?? 0) + 1
            : selectedWorktreeId
              ? (sessionsByWorktree.get(selectedWorktreeId)?.length ?? 0) + 1
              : 1
        }
      />

      {/* Left scroll arrow */}
      {showLeftArrow && (
        <button
          onClick={scrollLeft}
          className="p-1 hover:bg-accent transition-colors shrink-0"
          data-testid="scroll-left"
        >
          <ChevronLeft className="h-4 w-4" />
        </button>
      )}

      {/* Tabs container */}
      <div
        ref={tabsContainerRef}
        className="flex-1 flex overflow-x-auto scrollbar-hide"
        style={{ scrollbarWidth: 'none', msOverflowStyle: 'none' }}
        data-testid="session-tabs-scroll-container"
      >
        {orderedSessions.length === 0 &&
        fileTabs.length === 0 &&
        diffTabs.length === 0 &&
        contextTabs.length === 0 &&
        !(
          !isConnectionMode &&
          connectionsForWorktree.some((c) => (sessionsByConnection.get(c.id) || []).length > 0)
        ) ? (
          <div
            className="flex items-center px-3 py-1.5 text-sm text-muted-foreground"
            data-testid="no-sessions"
          >
            {t('sessionTabs.empty.noSessions')}
          </div>
        ) : (
          <>
            {/* Sticky connection session tabs (worktree mode only) */}
            {!isConnectionMode &&
              connectionsForWorktree.map((connection) => {
                const connectionSessions = sessionsByConnection.get(connection.id) || []
                const connectionTabOrder = tabOrderByConnection.get(connection.id) || []
                const orderedConnectionSessions = connectionTabOrder
                  .map((id) => connectionSessions.find((s) => s.id === id))
                  .filter((s): s is NonNullable<typeof s> => s !== undefined)

                if (orderedConnectionSessions.length === 0) return null

                return (
                  <Fragment key={connection.id}>
                    {/* Thin visual separator before each connection group */}
                    <div className="w-px bg-border/60 self-stretch my-1" aria-hidden="true" />
                    {orderedConnectionSessions.map((session) => (
                      <ConnectionSessionTab
                        key={session.id}
                        sessionId={session.id}
                        name={session.name || t('sessionTabs.common.untitled')}
                        isActive={session.id === inlineConnectionSessionId && !isFileTabActive}
                        onClick={() => handleConnectionSessionTabClick(session.id)}
                        connectionColor={connection.color}
                        connectionName={connection.name}
                      />
                    ))}
                  </Fragment>
                )
              })}

            {/* Session tabs */}
            {orderedSessions.map((session) => (
              <SessionTab
                key={session.id}
                sessionId={session.id}
                name={session.name || t('sessionTabs.common.untitled')}
                agentSdk={session.agent_sdk}
                color={session.color ?? null}
                isActive={
                  session.id === activeSessionId && !isFileTabActive && !inlineConnectionSessionId
                }
                onClick={() => handleSessionTabClick(session.id)}
                onClose={(e) => handleCloseSession(e, session.id)}
                onMiddleClick={(e) => handleCloseSession(e, session.id)}
                onRename={(newName) => handleRenameSession(session.id, newName)}
                onSetColor={(color) => handleSetSessionColor(session.id, color)}
                onDragStart={(e) => handleDragStart(e, session.id)}
                onDragOver={(e) => handleDragOver(e, session.id)}
                onDrop={(e) => handleDrop(e, session.id)}
                onDragEnd={handleDragEnd}
                isDragging={draggedTabId === session.id}
                isDragOver={dragOverTabId === session.id}
                worktreeId={resolvedScopeId}
                onCloseOthers={() =>
                  isConnectionMode
                    ? closeOtherConnectionSessions(resolvedScopeId, session.id)
                    : closeOtherSessions(resolvedScopeId, session.id)
                }
                onCloseToRight={() =>
                  isConnectionMode
                    ? closeConnectionSessionsToRight(resolvedScopeId, session.id)
                    : closeSessionsToRight(resolvedScopeId, session.id)
                }
                hintCode={sessionHints.sessionHintMap.get(session.id)}
              />
            ))}
            {/* File viewer tabs */}
            {fileTabs.map((file) => {
              const worktreePath = selectedWorktree?.path || ''
              const relativePath =
                worktreePath && file.path.startsWith(worktreePath)
                  ? file.path.slice(worktreePath.length + 1)
                  : file.name
              return (
                <FileTab
                  key={file.path}
                  filePath={file.path}
                  name={file.name}
                  isActive={isFileTabActive && activeFilePath === file.path}
                  onClick={() => handleFileTabClick(file.path)}
                  onClose={(e) => handleCloseFileTab(e, file.path)}
                  onCloseOthers={() => closeOtherFiles(file.path)}
                  onCloseToRight={() => closeFilesToRight(file.path)}
                  relativePath={relativePath}
                />
              )
            })}
            {/* Diff viewer tabs */}
            {diffTabs.map(([key, tab]) => (
              <DiffTabItem
                key={key}
                tabKey={key}
                tab={tab}
                isActive={isFileTabActive && activeFilePath === key}
                onActivate={() => handleDiffTabClick(key)}
                onClose={(e) => handleCloseDiffTab(e, key)}
                onCloseOthers={() => closeOtherFiles(key)}
                onCloseToRight={() => closeFilesToRight(key)}
              />
            ))}
            {/* Context editor tabs */}
            {contextTabs.map(([key, tab]) => (
              <div
                key={key}
                data-testid={`context-tab-${tab.worktreeId}`}
                onClick={() => {
                  useFileViewerStore.getState().activateContextEditor(tab.worktreeId)
                }}
                onMouseDown={(e) => {
                  if (e.button === 1) {
                    e.preventDefault()
                    useFileViewerStore.getState().closeContextEditor()
                  }
                }}
                className={cn(
                  'group relative flex items-center gap-1.5 px-3 py-1 text-sm cursor-pointer select-none',
                  'rounded-md transition-colors min-w-[100px] max-w-[200px] my-0.5 mx-0.5',
                  isFileTabActive && activeFilePath === key
                    ? 'bg-background text-foreground shadow-sm'
                    : 'text-muted-foreground hover:bg-background/50 hover:text-foreground'
                )}
                title={t('sessionTabs.context.title')}
              >
                <FileText className="h-3.5 w-3.5 flex-shrink-0 text-emerald-400" />
                <span className="truncate flex-1">{t('sessionTabs.context.label')}</span>
                <button
                  onClick={(e) => {
                    e.stopPropagation()
                    useFileViewerStore.getState().closeContextEditor()
                  }}
                  className={cn(
                    'p-0.5 rounded hover:bg-accent transition-opacity',
                    isFileTabActive && activeFilePath === key
                      ? 'opacity-100'
                      : 'opacity-0 group-hover:opacity-100'
                  )}
                >
                  <X className="h-3 w-3" />
                </button>
              </div>
            ))}
          </>
        )}
      </div>

      {/* Right scroll arrow */}
      {showRightArrow && (
        <button
          onClick={scrollRight}
          className="p-1 hover:bg-accent transition-colors shrink-0"
          data-testid="scroll-right"
        >
          <ChevronRight className="h-4 w-4" />
        </button>
      )}

      {/* Global session status indicator (Phase 6) */}
      <SessionStatusIndicator
        onJumpToSession={(sid) => handleSessionTabClick(sid)}
      />
    </div>
  )
}
