import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'
import type { SelectedModel } from './useSettingsStore'
import { useSettingsStore } from './useSettingsStore'
import { useGitStore } from './useGitStore'
import { useWorktreeStore } from './useWorktreeStore'
import { removeSessionViewState } from '@/lib/session-view-registry'
import { translate } from '@/i18n/useI18n'
import { DEFAULT_LOCALE } from '@/i18n/messages'

function t(key: string, params?: Record<string, string | number | boolean>): string {
  const locale = useSettingsStore.getState().locale ?? DEFAULT_LOCALE
  return translate(locale, key, params)
}

// Session mode type
export type SessionMode = 'build' | 'plan'

// Pending plan approval state (from ExitPlanMode blocking tool)
export interface PendingPlan {
  requestId: string
  planContent: string
  toolUseID: string
}

export interface PendingPromptOptions {
  mode?: SessionMode
  goalMode?: boolean
  successCriteria?: string
}

export interface PendingInitialMessage {
  message: string
  options?: PendingPromptOptions
}

// Session type matching the database schema
interface Session {
  id: string
  worktree_id: string | null
  project_id: string
  connection_id: string | null
  name: string | null
  status: 'active' | 'completed' | 'error' | 'archived'
  opencode_session_id: string | null
  agent_sdk: 'opencode' | 'claude-code' | 'codex' | 'terminal'
  mode: SessionMode
  model_provider_id: string | null
  model_id: string | null
  model_variant: string | null
  first_message_at: number | null
  created_at: string
  updated_at: string
  completed_at: string | null
}

// Options for creating a session from the new session dialog (or quick paths)
export interface CreateSessionOptions {
  name?: string
  agentSdk?: 'opencode' | 'claude-code' | 'codex' | 'terminal'
  model?: SelectedModel | null
  initialMode?: SessionMode
}

interface SessionState {
  // Data - keyed by worktree ID
  sessionsByWorktree: Map<string, Session[]>
  // Tab order - keyed by worktree ID, array of session IDs
  tabOrderByWorktree: Map<string, string[]>
  // Mode per session - keyed by session ID
  modeBySession: Map<string, SessionMode>
  // Pending initial messages - keyed by session ID (e.g., code review prompts)
  pendingMessages: Map<string, string>
  pendingMessageOptions: Map<string, PendingPromptOptions>
  // Pending plan approvals - keyed by session ID (from ExitPlanMode blocking tool)
  pendingPlans: Map<string, PendingPlan>
  // Pending follow-up messages - keyed by session ID, ordered queue of messages to auto-send
  pendingFollowUpMessages: Map<string, string[]>
  isLoading: boolean
  error: string | null

  // UI State
  activeSessionId: string | null
  activeWorktreeId: string | null
  // Persisted: last active session per worktree
  activeSessionByWorktree: Record<string, string>

  // Connection session state
  sessionsByConnection: Map<string, Session[]>
  tabOrderByConnection: Map<string, string[]>
  activeSessionByConnection: Record<string, string> // persisted
  activeConnectionId: string | null

  // Inline connection session viewing (in worktree mode)
  // When set, MainPane renders this connection session instead of the active worktree session.
  // Sidebar selection remains on the worktree.
  inlineConnectionSessionId: string | null

  // Transient signal: terminal session IDs that were closed since last acknowledgement.
  // MainPane subscribes to this to prune mountedTerminalSessionIds.
  closedTerminalSessionIds: Set<string>

  // Actions
  acknowledgeClosedTerminals: (ids: Set<string>) => void
  loadSessions: (worktreeId: string, projectId: string) => Promise<void>
  createSession: (
    worktreeId: string,
    projectId: string,
    agentSdkOverride?: 'opencode' | 'claude-code' | 'codex' | 'terminal',
    initialMode?: SessionMode,
    options?: { name?: string; model?: SelectedModel | null }
  ) => Promise<{ success: boolean; session?: Session; error?: string }>
  updateSessionAgent: (
    sessionId: string,
    update: {
      agentSdk?: 'opencode' | 'claude-code' | 'codex' | 'terminal'
      model?: SelectedModel | null
    }
  ) => Promise<{ success: boolean; error?: string }>
  closeSession: (sessionId: string) => Promise<{ success: boolean; error?: string }>
  reopenSession: (
    sessionId: string,
    worktreeId: string
  ) => Promise<{ success: boolean; error?: string }>
  setActiveSession: (sessionId: string | null) => void
  setActiveWorktree: (worktreeId: string | null) => void
  updateSessionName: (sessionId: string, name: string) => Promise<boolean>
  updateSessionColor: (sessionId: string, color: string | null) => Promise<boolean>
  reorderTabs: (worktreeId: string, fromIndex: number, toIndex: number) => void
  getSessionsForWorktree: (worktreeId: string) => Session[]
  getTabOrderForWorktree: (worktreeId: string) => string[]
  getSessionMode: (sessionId: string) => SessionMode
  toggleSessionMode: (sessionId: string) => Promise<void>
  setSessionMode: (sessionId: string, mode: SessionMode) => Promise<void>
  setSessionModel: (
    sessionId: string,
    model: SelectedModel,
    options?: { skipGlobalUpdate?: boolean }
  ) => Promise<void>
  setOpenCodeSessionId: (sessionId: string, opencodeSessionId: string | null) => void
  /**
   * Optimistically stamp `first_message_at` on a session so the UI locks the
   * provider/model selectors immediately (the canonical write happens in main
   * via createSessionMessage / upsertSessionActivity).
   */
  markSessionFirstMessage: (sessionId: string) => void
  setPendingMessage: (sessionId: string, message: string, options?: PendingPromptOptions) => void
  dequeuePendingMessage: (sessionId: string) => string | null
  dequeuePendingMessageWithOptions: (sessionId: string) => PendingInitialMessage | null
  requeuePendingMessage: (
    sessionId: string,
    message: string,
    options?: PendingPromptOptions
  ) => void
  consumePendingMessage: (sessionId: string) => string | null
  setPendingFollowUpMessages: (sessionId: string, messages: string[]) => void
  dequeueFollowUpMessage: (sessionId: string) => string | null
  requeueFollowUpMessageFront: (sessionId: string, message: string) => void
  consumeFollowUpMessage: (sessionId: string) => string | null
  closeOtherSessions: (worktreeId: string, keepSessionId: string) => Promise<void>
  closeSessionsToRight: (worktreeId: string, fromSessionId: string) => Promise<void>
  // Plan approval
  setPendingPlan: (sessionId: string, plan: PendingPlan) => void
  clearPendingPlan: (sessionId: string) => void
  getPendingPlan: (sessionId: string) => PendingPlan | null

  // Inline connection session actions
  setInlineConnectionSession: (sessionId: string | null) => void
  clearInlineConnectionSession: () => void
  loadConnectionSessionsBackground: (connectionId: string) => Promise<void>

  // Connection session actions
  loadConnectionSessions: (connectionId: string) => Promise<void>
  createConnectionSession: (
    connectionId: string,
    agentSdkOverride?: 'opencode' | 'claude-code' | 'codex' | 'terminal',
    initialMode?: SessionMode,
    options?: { name?: string; model?: SelectedModel | null }
  ) => Promise<{ success: boolean; session?: Session; error?: string }>
  setActiveConnectionSession: (sessionId: string | null) => void
  setActiveConnection: (connectionId: string | null) => void
  getSessionsForConnection: (connectionId: string) => Session[]
  getTabOrderForConnection: (connectionId: string) => string[]
  reorderConnectionTabs: (connectionId: string, fromIndex: number, toIndex: number) => void
  closeOtherConnectionSessions: (connectionId: string, keepSessionId: string) => Promise<void>
  closeConnectionSessionsToRight: (connectionId: string, fromSessionId: string) => Promise<void>
}

// Helper: find a session across both worktree and connection maps
function findSessionScope(
  state: SessionState,
  sessionId: string
): { type: 'worktree'; scopeId: string } | { type: 'connection'; scopeId: string } | null {
  for (const [worktreeId, sessions] of state.sessionsByWorktree.entries()) {
    if (sessions.some((s) => s.id === sessionId)) {
      return { type: 'worktree', scopeId: worktreeId }
    }
  }
  for (const [connectionId, sessions] of state.sessionsByConnection.entries()) {
    if (sessions.some((s) => s.id === sessionId)) {
      return { type: 'connection', scopeId: connectionId }
    }
  }
  return null
}

export const useSessionStore = create<SessionState>()(
  persist(
    (set, get) => ({
      // Initial state
      sessionsByWorktree: new Map(),
      tabOrderByWorktree: new Map(),
      modeBySession: new Map(),
      pendingMessages: new Map(),
      pendingMessageOptions: new Map(),
      pendingPlans: new Map(),
      pendingFollowUpMessages: new Map(),
      isLoading: false,
      error: null,
      activeSessionId: null,
      activeWorktreeId: null,
      activeSessionByWorktree: {},

      // Connection session state
      sessionsByConnection: new Map(),
      tabOrderByConnection: new Map(),
      activeSessionByConnection: {},
      activeConnectionId: null,
      inlineConnectionSessionId: null,
      closedTerminalSessionIds: new Set<string>(),

      acknowledgeClosedTerminals: (ids: Set<string>) => {
        set((state) => {
          const remaining = new Set(state.closedTerminalSessionIds)
          for (const id of ids) remaining.delete(id)
          return { closedTerminalSessionIds: remaining }
        })
      },

      // Load sessions for a worktree from database (only active sessions for tabs)
      loadSessions: async (worktreeId: string, _projectId: string) => {
        // Only show loading indicator when no sessions are cached yet.
        // When sessions already exist (e.g., after createSession populated them),
        // skip the indicator to avoid unmounting active SessionViews mid-init.
        const hasCached = get().sessionsByWorktree.has(worktreeId)
        set({ isLoading: !hasCached, error: null })
        try {
          // Only load active sessions - completed sessions appear in history only
          const sessions = await window.db.session.getActiveByWorktree(worktreeId)
          // Sort by updated_at descending (most recent first)
          const sortedSessions = sessions.sort(
            (a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()
          )

          set((state) => {
            const newSessionsMap = new Map(state.sessionsByWorktree)
            newSessionsMap.set(worktreeId, sortedSessions)

            // Initialize tab order if not exists - use session IDs in sorted order
            const newTabOrderMap = new Map(state.tabOrderByWorktree)
            if (!newTabOrderMap.has(worktreeId)) {
              newTabOrderMap.set(
                worktreeId,
                sortedSessions.map((s) => s.id)
              )
            } else {
              // Sync tab order with actual sessions (remove deleted, add new)
              const existingOrder = newTabOrderMap.get(worktreeId)!
              const sessionIds = new Set(sortedSessions.map((s) => s.id))
              const validOrder = existingOrder.filter((id) => sessionIds.has(id))
              const newIds = sortedSessions
                .map((s) => s.id)
                .filter((id) => !validOrder.includes(id))
              newTabOrderMap.set(worktreeId, [...validOrder, ...newIds])
            }

            // Populate mode map from loaded sessions
            const newModeMap = new Map(state.modeBySession)
            for (const session of sortedSessions) {
              if (!newModeMap.has(session.id)) {
                newModeMap.set(session.id, session.mode || 'build')
              }
            }

            // Set active session if none selected and sessions exist
            let activeSessionId = state.activeSessionId
            if (
              state.activeWorktreeId === worktreeId &&
              !activeSessionId &&
              sortedSessions.length > 0
            ) {
              // Try to restore persisted active session
              const persistedSessionId = state.activeSessionByWorktree[worktreeId]
              const sessionExists =
                persistedSessionId && sortedSessions.some((s) => s.id === persistedSessionId)

              if (sessionExists) {
                activeSessionId = persistedSessionId
              } else {
                const tabOrder = newTabOrderMap.get(worktreeId)!
                activeSessionId = tabOrder[0] || sortedSessions[0].id
              }
            }

            return {
              sessionsByWorktree: newSessionsMap,
              tabOrderByWorktree: newTabOrderMap,
              modeBySession: newModeMap,
              isLoading: false,
              activeSessionId
            }
          })
        } catch (error) {
          set({
            error: error instanceof Error ? error.message : 'Failed to load sessions',
            isLoading: false
          })
        }
      },

      // Create a new session
      createSession: async (
        worktreeId: string,
        projectId: string,
        agentSdkOverride?: 'opencode' | 'claude-code' | 'codex' | 'terminal',
        initialMode?: SessionMode,
        options?: { name?: string; model?: SelectedModel | null }
      ) => {
        try {
          // Resolve default agent SDK: explicit override → worktree.last_agent_sdk → settings default
          const { useSettingsStore } = await import('./useSettingsStore')
          let worktreeLastAgentSdk:
            | 'opencode'
            | 'claude-code'
            | 'codex'
            | 'terminal'
            | null
            | undefined
          const worktreesByProject = useWorktreeStore.getState().worktreesByProject
          for (const worktrees of worktreesByProject.values()) {
            const w = worktrees.find((w) => w.id === worktreeId)
            if (w) {
              worktreeLastAgentSdk = (w as { last_agent_sdk?: typeof worktreeLastAgentSdk })
                .last_agent_sdk
              break
            }
          }
          const defaultAgentSdk =
            agentSdkOverride ??
            worktreeLastAgentSdk ??
            useSettingsStore.getState().defaultAgentSdk ??
            'opencode'

          const isTerminal = defaultAgentSdk === 'terminal'

          // Terminal sessions skip model resolution entirely
          let defaultModel: { providerID: string; modelID: string; variant?: string } | null = null

          if (options?.model) {
            defaultModel = {
              providerID: options.model.providerID,
              modelID: options.model.modelID,
              variant: options.model.variant
            }
          } else if (!isTerminal) {
            const { resolveModelForSdk } = await import('./useSettingsStore')
            const configuredDefaultSdk = useSettingsStore.getState().defaultAgentSdk ?? 'opencode'

            // Priority 1: mode-specific default (only when session SDK matches the
            // configured default — mode defaults are set in that SDK's context)
            if (defaultAgentSdk === configuredDefaultSdk) {
              const modeModel = useSettingsStore.getState().getModelForMode(initialMode ?? 'build')
              if (modeModel) {
                defaultModel = modeModel
              }
            }

            // Priority 2: per-provider default → (legacy) global default
            if (!defaultModel) {
              defaultModel = resolveModelForSdk(defaultAgentSdk)
            }

            // Legacy worktree fallback only when per-provider feature not yet active
            if (!defaultModel) {
              const settingsState = useSettingsStore.getState()
              const hasPerProviderDefaults =
                Object.keys(settingsState.selectedModelByProvider).length > 0
              if (!hasPerProviderDefaults) {
                const worktree = useWorktreeStore.getState().worktreesByProject
                let worktreeRecord:
                  | {
                      last_model_provider_id: string | null
                      last_model_id: string | null
                      last_model_variant: string | null
                    }
                  | undefined
                for (const worktrees of worktree.values()) {
                  worktreeRecord = worktrees.find((w) => w.id === worktreeId)
                  if (worktreeRecord) break
                }
                if (worktreeRecord?.last_model_id) {
                  defaultModel = {
                    providerID: worktreeRecord.last_model_provider_id!,
                    modelID: worktreeRecord.last_model_id,
                    variant: worktreeRecord.last_model_variant ?? undefined
                  }
                }
              }
            }
          }

          const existingSessions = get().sessionsByWorktree.get(worktreeId) || []
          const sessionNumber = existingSessions.length + 1

          const sessionName =
            options?.name?.trim() ||
            (isTerminal ? `Terminal ${sessionNumber}` : `Session ${sessionNumber}`)

          const session = await window.db.session.create({
            worktree_id: worktreeId,
            project_id: projectId,
            name: sessionName,
            agent_sdk: defaultAgentSdk,
            ...(defaultModel
              ? {
                  model_provider_id: defaultModel.providerID,
                  model_id: defaultModel.modelID,
                  model_variant: defaultModel.variant ?? null
                }
              : {})
          })

          // Persist last_agent_sdk on the worktree so the next session defaults to it.
          // Fire-and-forget; not critical to the create flow.
          if (worktreeLastAgentSdk !== defaultAgentSdk) {
            window.db.worktree
              .update(worktreeId, { last_agent_sdk: defaultAgentSdk })
              .then(() => {
                useWorktreeStore.getState().updateWorktreeLastAgentSdk(worktreeId, defaultAgentSdk)
              })
              .catch(() => {
                /* non-critical */
              })
          }

          // Clear file viewer so the new session takes focus in MainPane
          const { useFileViewerStore } = await import('./useFileViewerStore')
          useFileViewerStore.getState().setActiveFile(null)
          useFileViewerStore.getState().clearActiveDiff()

          set((state) => {
            const newSessionsMap = new Map(state.sessionsByWorktree)
            const existingSessions = newSessionsMap.get(worktreeId) || []
            newSessionsMap.set(worktreeId, [session, ...existingSessions])

            // Add to tab order at the end
            const newTabOrderMap = new Map(state.tabOrderByWorktree)
            const existingOrder = newTabOrderMap.get(worktreeId) || []
            newTabOrderMap.set(worktreeId, [...existingOrder, session.id])

            // Initialize mode for new session
            const newModeMap = new Map(state.modeBySession)
            newModeMap.set(session.id, session.mode || 'build')

            return {
              sessionsByWorktree: newSessionsMap,
              tabOrderByWorktree: newTabOrderMap,
              modeBySession: newModeMap,
              activeSessionId: session.id,
              activeSessionByWorktree: {
                ...state.activeSessionByWorktree,
                [worktreeId]: session.id
              }
            }
          })

          return { success: true, session }
        } catch (error) {
          return {
            success: false,
            error: error instanceof Error ? error.message : 'Failed to create session'
          }
        }
      },

      // Close a session tab (removes from tab view, keeps in database for history)
      // Scope-agnostic: works for both worktree and connection sessions
      closeSession: async (sessionId: string) => {
        try {
          // Check if this is a terminal session before removing from state
          let isTerminalSession = false
          for (const sessions of get().sessionsByWorktree.values()) {
            const found = sessions.find((s) => s.id === sessionId)
            if (found) {
              isTerminalSession = found.agent_sdk === 'terminal'
              break
            }
          }
          if (!isTerminalSession) {
            for (const sessions of get().sessionsByConnection.values()) {
              const found = sessions.find((s) => s.id === sessionId)
              if (found) {
                isTerminalSession = found.agent_sdk === 'terminal'
                break
              }
            }
          }

          // Mark session as completed instead of deleting
          // This preserves it in session history
          await window.db.session.update(sessionId, {
            status: 'completed',
            completed_at: new Date().toISOString()
          })

          // Destroy PTY for terminal sessions
          if (isTerminalSession) {
            try {
              await window.terminalOps.destroy(sessionId)
            } catch {
              // Best-effort cleanup — PTY may already be gone
            }
          }

          set((state) => {
            const newWorktreeSessionsMap = new Map(state.sessionsByWorktree)
            const newWorktreeTabOrderMap = new Map(state.tabOrderByWorktree)
            const newConnectionSessionsMap = new Map(state.sessionsByConnection)
            const newConnectionTabOrderMap = new Map(state.tabOrderByConnection)
            let newActiveSessionId = state.activeSessionId

            // Check worktree sessions first
            let foundInWorktree = false
            for (const [worktreeId, sessions] of newWorktreeSessionsMap.entries()) {
              const filtered = sessions.filter((s) => s.id !== sessionId)
              if (filtered.length !== sessions.length) {
                foundInWorktree = true
                newWorktreeSessionsMap.set(worktreeId, filtered)

                // Update tab order
                const tabOrder = newWorktreeTabOrderMap.get(worktreeId) || []
                const sessionIndex = tabOrder.indexOf(sessionId)
                const newOrder = tabOrder.filter((id) => id !== sessionId)
                newWorktreeTabOrderMap.set(worktreeId, newOrder)

                // If closing the active session, select another one
                if (state.activeSessionId === sessionId) {
                  if (newOrder.length > 0) {
                    const newIndex = Math.min(sessionIndex, newOrder.length - 1)
                    newActiveSessionId = newOrder[newIndex]
                  } else {
                    newActiveSessionId = null
                  }
                }
                break
              }
            }

            // Check connection sessions if not found in worktree
            if (!foundInWorktree) {
              for (const [connectionId, sessions] of newConnectionSessionsMap.entries()) {
                const filtered = sessions.filter((s) => s.id !== sessionId)
                if (filtered.length !== sessions.length) {
                  newConnectionSessionsMap.set(connectionId, filtered)

                  // Update tab order
                  const tabOrder = newConnectionTabOrderMap.get(connectionId) || []
                  const sessionIndex = tabOrder.indexOf(sessionId)
                  const newOrder = tabOrder.filter((id) => id !== sessionId)
                  newConnectionTabOrderMap.set(connectionId, newOrder)

                  // If closing the active session, select another one
                  if (state.activeSessionId === sessionId) {
                    if (newOrder.length > 0) {
                      const newIndex = Math.min(sessionIndex, newOrder.length - 1)
                      newActiveSessionId = newOrder[newIndex]
                    } else {
                      newActiveSessionId = null
                    }
                  }
                  break
                }
              }
            }

            // Update persisted active session mappings
            const newActiveByWorktree = { ...state.activeSessionByWorktree }
            for (const [worktreeId] of newWorktreeSessionsMap.entries()) {
              if (newActiveByWorktree[worktreeId] === sessionId) {
                if (newActiveSessionId) {
                  newActiveByWorktree[worktreeId] = newActiveSessionId
                } else {
                  delete newActiveByWorktree[worktreeId]
                }
              }
            }

            const newActiveByConnection = { ...state.activeSessionByConnection }
            for (const [connectionId] of newConnectionSessionsMap.entries()) {
              if (newActiveByConnection[connectionId] === sessionId) {
                if (newActiveSessionId) {
                  newActiveByConnection[connectionId] = newActiveSessionId
                } else {
                  delete newActiveByConnection[connectionId]
                }
              }
            }

            const newClosedTerminals = isTerminalSession
              ? new Set([...state.closedTerminalSessionIds, sessionId])
              : state.closedTerminalSessionIds

            return {
              sessionsByWorktree: newWorktreeSessionsMap,
              tabOrderByWorktree: newWorktreeTabOrderMap,
              sessionsByConnection: newConnectionSessionsMap,
              tabOrderByConnection: newConnectionTabOrderMap,
              activeSessionId: newActiveSessionId,
              activeSessionByWorktree: newActiveByWorktree,
              activeSessionByConnection: newActiveByConnection,
              closedTerminalSessionIds: newClosedTerminals
            }
          })

          // If this session was a PR-creating session, cancel the PR flow
          const gitStore = useGitStore.getState()
          for (const [worktreeId, creation] of gitStore.prCreation.entries()) {
            if (creation.sessionId === sessionId && creation.creating) {
              gitStore.setPrCreation(worktreeId, null)
              break
            }
          }

          removeSessionViewState(sessionId)

          return { success: true }
        } catch (error) {
          return {
            success: false,
            error: error instanceof Error ? error.message : 'Failed to close session'
          }
        }
      },

      // Reopen a closed session (from history) - marks as active and adds to tabs
      reopenSession: async (sessionId: string, worktreeId: string) => {
        try {
          // Mark session as active again
          const updatedSession = await window.db.session.update(sessionId, {
            status: 'active',
            completed_at: null
          })

          if (!updatedSession) {
            return { success: false, error: 'Session not found' }
          }

          set((state) => {
            const newSessionsMap = new Map(state.sessionsByWorktree)
            const existingSessions = newSessionsMap.get(worktreeId) || []

            // Only add if not already in the list
            if (!existingSessions.some((s) => s.id === sessionId)) {
              newSessionsMap.set(worktreeId, [updatedSession, ...existingSessions])
            }

            // Add to tab order
            const newTabOrderMap = new Map(state.tabOrderByWorktree)
            const existingOrder = newTabOrderMap.get(worktreeId) || []
            if (!existingOrder.includes(sessionId)) {
              newTabOrderMap.set(worktreeId, [...existingOrder, sessionId])
            }

            return {
              sessionsByWorktree: newSessionsMap,
              tabOrderByWorktree: newTabOrderMap,
              activeSessionId: sessionId,
              activeWorktreeId: worktreeId
            }
          })

          return { success: true }
        } catch (error) {
          return {
            success: false,
            error: error instanceof Error ? error.message : 'Failed to reopen session'
          }
        }
      },

      // Set active session
      setActiveSession: (sessionId: string | null) => {
        // Clear file viewer so the session takes focus in MainPane
        if (sessionId) {
          import('./useFileViewerStore').then(({ useFileViewerStore }) => {
            useFileViewerStore.getState().setActiveFile(null)
            useFileViewerStore.getState().clearActiveDiff()
          })
        }

        const state = get()
        const worktreeId = state.activeWorktreeId
        const connectionId = state.activeConnectionId

        if (sessionId && worktreeId) {
          set((state) => ({
            activeSessionId: sessionId,
            activeSessionByWorktree: {
              ...state.activeSessionByWorktree,
              [worktreeId]: sessionId
            }
          }))
        } else if (sessionId && connectionId) {
          set((state) => ({
            activeSessionId: sessionId,
            activeSessionByConnection: {
              ...state.activeSessionByConnection,
              [connectionId]: sessionId
            }
          }))
        } else {
          set({ activeSessionId: sessionId })
        }
      },

      // Set active worktree and load its sessions
      setActiveWorktree: (worktreeId: string | null) => {
        const state = get()

        if (worktreeId === state.activeWorktreeId) return

        set({
          activeWorktreeId: worktreeId,
          activeConnectionId: null,
          inlineConnectionSessionId: null
        })

        if (worktreeId) {
          // Check if we already have sessions for this worktree
          const existingSessions = state.sessionsByWorktree.get(worktreeId)
          if (existingSessions) {
            // Try to restore persisted active session for this worktree
            const persistedSessionId = state.activeSessionByWorktree[worktreeId]
            const sessionExists =
              persistedSessionId && existingSessions.some((s) => s.id === persistedSessionId)

            if (sessionExists) {
              set({ activeSessionId: persistedSessionId })
            } else {
              // Fallback to first tab
              const tabOrder = state.tabOrderByWorktree.get(worktreeId) || []
              const activeId =
                tabOrder[0] || (existingSessions.length > 0 ? existingSessions[0].id : null)
              set({ activeSessionId: activeId })
            }
          } else {
            // Clear active session until sessions are loaded
            set({ activeSessionId: null })
          }
        } else {
          set({ activeSessionId: null })
        }
      },

      // Update session name (scope-agnostic)
      updateSessionName: async (sessionId: string, name: string) => {
        try {
          const updatedSession = await window.db.session.update(sessionId, { name })
          if (updatedSession) {
            const scope = findSessionScope(get(), sessionId)

            set((state) => {
              // Update in worktree sessions
              const newWorktreeSessionsMap = new Map(state.sessionsByWorktree)
              for (const [wtId, sessions] of newWorktreeSessionsMap.entries()) {
                const updated = sessions.map((s) => (s.id === sessionId ? { ...s, name } : s))
                if (updated.some((s, i) => s !== sessions[i])) {
                  newWorktreeSessionsMap.set(wtId, updated)
                }
              }

              // Update in connection sessions
              const newConnectionSessionsMap = new Map(state.sessionsByConnection)
              for (const [connId, sessions] of newConnectionSessionsMap.entries()) {
                const updated = sessions.map((s) => (s.id === sessionId ? { ...s, name } : s))
                if (updated.some((s, i) => s !== sessions[i])) {
                  newConnectionSessionsMap.set(connId, updated)
                }
              }

              return {
                sessionsByWorktree: newWorktreeSessionsMap,
                sessionsByConnection: newConnectionSessionsMap
              }
            })

            // Append non-default session titles to the worktree (updates store + DB)
            const isDefault = /^Session \d+$/.test(name)
            if (!isDefault && scope?.type === 'worktree') {
              useWorktreeStore.getState().appendSessionTitle(scope.scopeId, name)
            }

            return true
          }
          return false
        } catch {
          return false
        }
      },

      // Update session color (scope-agnostic)
      updateSessionColor: async (sessionId: string, color: string | null) => {
        try {
          const updatedSession = await window.db.session.update(sessionId, { color })
          if (!updatedSession) return false

          set((state) => {
            const newWorktreeSessionsMap = new Map(state.sessionsByWorktree)
            for (const [wtId, sessions] of newWorktreeSessionsMap.entries()) {
              const updated = sessions.map((s) => (s.id === sessionId ? { ...s, color } : s))
              if (updated.some((s, i) => s !== sessions[i])) {
                newWorktreeSessionsMap.set(wtId, updated)
              }
            }

            const newConnectionSessionsMap = new Map(state.sessionsByConnection)
            for (const [connId, sessions] of newConnectionSessionsMap.entries()) {
              const updated = sessions.map((s) => (s.id === sessionId ? { ...s, color } : s))
              if (updated.some((s, i) => s !== sessions[i])) {
                newConnectionSessionsMap.set(connId, updated)
              }
            }

            return {
              sessionsByWorktree: newWorktreeSessionsMap,
              sessionsByConnection: newConnectionSessionsMap
            }
          })

          return true
        } catch {
          return false
        }
      },

      // Update agent_sdk and/or model for a session. Locked once the session
      // has received its first activity (first_message_at is set).
      updateSessionAgent: async (
        sessionId: string,
        update: {
          agentSdk?: 'opencode' | 'claude-code' | 'codex' | 'terminal'
          model?: SelectedModel | null
        }
      ) => {
        // Look up the session across both scopes
        let target: Session | null = null
        let scope: { type: 'worktree'; scopeId: string } | { type: 'connection'; scopeId: string } | null =
          null
        for (const [worktreeId, sessions] of get().sessionsByWorktree.entries()) {
          const found = sessions.find((s) => s.id === sessionId)
          if (found) {
            target = found
            scope = { type: 'worktree', scopeId: worktreeId }
            break
          }
        }
        if (!target) {
          for (const [connectionId, sessions] of get().sessionsByConnection.entries()) {
            const found = sessions.find((s) => s.id === sessionId)
            if (found) {
              target = found
              scope = { type: 'connection', scopeId: connectionId }
              break
            }
          }
        }

        if (!target) {
          return { success: false, error: t('sessionStore.errors.sessionNotFound') }
        }
        // Lock applies to provider (agent_sdk) only — switching SDKs mid-session
        // would break the underlying runtime. Model changes within the same SDK
        // are allowed; only context-window behaviour shifts.
        const isProviderChange =
          update.agentSdk !== undefined && update.agentSdk !== target.agent_sdk
        if (isProviderChange && target.first_message_at != null) {
          return { success: false, error: t('sessionStore.errors.sessionLocked') }
        }

        const newAgentSdk = update.agentSdk ?? target.agent_sdk
        const isTerminal = newAgentSdk === 'terminal'

        // Resolve model: explicit override → per-SDK default → current session model (if SDK unchanged)
        let nextModel: SelectedModel | null = null
        if (update.model) {
          nextModel = update.model
        } else if (!isTerminal) {
          if (newAgentSdk === target.agent_sdk && target.model_id && target.model_provider_id) {
            nextModel = {
              providerID: target.model_provider_id,
              modelID: target.model_id,
              variant: target.model_variant ?? undefined
            }
          } else {
            const { resolveModelForSdk } = await import('./useSettingsStore')
            const resolved = resolveModelForSdk(newAgentSdk)
            if (resolved) {
              nextModel = {
                providerID: resolved.providerID,
                modelID: resolved.modelID,
                variant: resolved.variant ?? undefined
              }
            }
          }
        }

        try {
          await window.db.session.update(sessionId, {
            agent_sdk: newAgentSdk,
            model_provider_id: isTerminal ? null : nextModel?.providerID ?? null,
            model_id: isTerminal ? null : nextModel?.modelID ?? null,
            model_variant: isTerminal ? null : nextModel?.variant ?? null
          })
        } catch (error) {
          return {
            success: false,
            error: error instanceof Error ? error.message : 'Failed to update session agent'
          }
        }

        // Update in-memory state in the scope we found
        set((state) => {
          const updater = (s: Session): Session =>
            s.id === sessionId
              ? {
                  ...s,
                  agent_sdk: newAgentSdk,
                  model_provider_id: isTerminal ? null : nextModel?.providerID ?? null,
                  model_id: isTerminal ? null : nextModel?.modelID ?? null,
                  model_variant: isTerminal ? null : nextModel?.variant ?? null
                }
              : s

          if (scope?.type === 'worktree') {
            const newMap = new Map(state.sessionsByWorktree)
            const sessions = newMap.get(scope.scopeId) || []
            newMap.set(scope.scopeId, sessions.map(updater))
            return { sessionsByWorktree: newMap }
          }
          if (scope?.type === 'connection') {
            const newMap = new Map(state.sessionsByConnection)
            const sessions = newMap.get(scope.scopeId) || []
            newMap.set(scope.scopeId, sessions.map(updater))
            return { sessionsByConnection: newMap }
          }
          return {}
        })

        // Persist last_agent_sdk on the worktree (worktree-scoped sessions only)
        if (scope?.type === 'worktree' && newAgentSdk !== target.agent_sdk) {
          window.db.worktree
            .update(scope.scopeId, { last_agent_sdk: newAgentSdk })
            .then(() => {
              useWorktreeStore
                .getState()
                .updateWorktreeLastAgentSdk(scope!.scopeId as string, newAgentSdk)
            })
            .catch(() => {
              /* non-critical */
            })
        }

        // Push the new model to the agent backend (SDK-aware)
        if (!isTerminal && nextModel) {
          try {
            await window.agentOps.setModel({ ...nextModel, runtimeId: newAgentSdk })
          } catch (error) {
            console.error('Failed to push model to agent backend:', error)
          }
          // Remember this model per-SDK so future sessions inherit it
          try {
            const { useSettingsStore } = await import('./useSettingsStore')
            useSettingsStore
              .getState()
              .setSelectedModelForSdk(newAgentSdk, nextModel, { skipBackendPush: true })
          } catch {
            /* non-critical */
          }
        }

        return { success: true }
      },

      // Reorder tabs
      reorderTabs: (worktreeId: string, fromIndex: number, toIndex: number) => {
        set((state) => {
          const newTabOrderMap = new Map(state.tabOrderByWorktree)
          const order = [...(newTabOrderMap.get(worktreeId) || [])]

          if (
            fromIndex < 0 ||
            fromIndex >= order.length ||
            toIndex < 0 ||
            toIndex >= order.length
          ) {
            return state
          }

          // Remove from old position and insert at new position
          const [removed] = order.splice(fromIndex, 1)
          order.splice(toIndex, 0, removed)

          newTabOrderMap.set(worktreeId, order)
          return { tabOrderByWorktree: newTabOrderMap }
        })
      },

      // Get sessions for a worktree
      getSessionsForWorktree: (worktreeId: string) => {
        return get().sessionsByWorktree.get(worktreeId) || []
      },

      // Get tab order for a worktree
      getTabOrderForWorktree: (worktreeId: string) => {
        return get().tabOrderByWorktree.get(worktreeId) || []
      },

      // Get session mode (defaults to 'build')
      getSessionMode: (sessionId: string): SessionMode => {
        return get().modeBySession.get(sessionId) || 'build'
      },

      // Get session by ID from either worktree or connection sessions
      getSessionById: (sessionId: string): Session | null => {
        for (const sessions of get().sessionsByWorktree.values()) {
          const found = sessions.find((s) => s.id === sessionId)
          if (found) return found
        }
        for (const sessions of get().sessionsByConnection.values()) {
          const found = sessions.find((s) => s.id === sessionId)
          if (found) return found
        }
        return null
      },

      // Toggle session mode between build and plan
      toggleSessionMode: async (sessionId: string) => {
        const currentMode = get().modeBySession.get(sessionId) || 'build'
        const newMode: SessionMode = currentMode === 'build' ? 'plan' : 'build'

        // Update local state immediately
        set((state) => {
          const newModeMap = new Map(state.modeBySession)
          newModeMap.set(sessionId, newMode)
          return { modeBySession: newModeMap }
        })

        // Persist to database
        try {
          await window.db.session.update(sessionId, { mode: newMode })
        } catch (error) {
          console.error('Failed to persist session mode:', error)
        }

        // Auto-apply mode-specific model if configured
        await get().applyModeDefaultModel(sessionId, newMode)
      },

      // Set session mode explicitly (also applies mode-specific model default)
      setSessionMode: async (sessionId: string, mode: SessionMode) => {
        set((state) => {
          const newModeMap = new Map(state.modeBySession)
          newModeMap.set(sessionId, mode)
          return { modeBySession: newModeMap }
        })

        try {
          await window.db.session.update(sessionId, { mode })
        } catch (error) {
          console.error('Failed to persist session mode:', error)
        }

        // Apply mode-specific default model (same as toggleSessionMode)
        await get().applyModeDefaultModel(sessionId, mode)
      },

      // Set model for a specific session (per-session model selection, scope-agnostic)
      setSessionModel: async (
        sessionId: string,
        model: SelectedModel,
        options?: { skipGlobalUpdate?: boolean }
      ) => {
        // Update local state immediately (search both maps)
        set((state) => {
          const newWorktreeSessionsMap = new Map(state.sessionsByWorktree)
          for (const [worktreeId, sessions] of newWorktreeSessionsMap.entries()) {
            const updated = sessions.map((s) =>
              s.id === sessionId
                ? {
                    ...s,
                    model_provider_id: model.providerID,
                    model_id: model.modelID,
                    model_variant: model.variant ?? null
                  }
                : s
            )
            if (updated.some((s, i) => s !== sessions[i])) {
              newWorktreeSessionsMap.set(worktreeId, updated)
            }
          }

          const newConnectionSessionsMap = new Map(state.sessionsByConnection)
          for (const [connectionId, sessions] of newConnectionSessionsMap.entries()) {
            const updated = sessions.map((s) =>
              s.id === sessionId
                ? {
                    ...s,
                    model_provider_id: model.providerID,
                    model_id: model.modelID,
                    model_variant: model.variant ?? null
                  }
                : s
            )
            if (updated.some((s, i) => s !== sessions[i])) {
              newConnectionSessionsMap.set(connectionId, updated)
            }
          }

          return {
            sessionsByWorktree: newWorktreeSessionsMap,
            sessionsByConnection: newConnectionSessionsMap
          }
        })

        // Persist to database
        try {
          await window.db.session.update(sessionId, {
            model_provider_id: model.providerID,
            model_id: model.modelID,
            model_variant: model.variant ?? null
          })
        } catch (error) {
          console.error('Failed to persist session model:', error)
        }

        // Find the session's SDK to route correctly (search both scopes)
        let agentSdk: 'opencode' | 'claude-code' | 'codex' | 'terminal' = 'opencode'
        for (const sessions of get().sessionsByWorktree.values()) {
          const found = sessions.find((s) => s.id === sessionId)
          if (found?.agent_sdk) {
            agentSdk = found.agent_sdk
            break
          }
        }
        if (agentSdk === 'opencode') {
          for (const sessions of get().sessionsByConnection.values()) {
            const found = sessions.find((s) => s.id === sessionId)
            if (found?.agent_sdk) {
              agentSdk = found.agent_sdk
              break
            }
          }
        }

        // Push to agent backend (SDK-aware) — skip for terminal sessions
        try {
          if (agentSdk !== 'terminal') {
            await window.agentOps.setModel({ ...model, runtimeId: agentSdk })
          }
        } catch (error) {
          console.error('Failed to push model to agent backend:', error)
        }

        // Update per-provider last-used model so new worktrees inherit it
        // Skip when auto-applying mode defaults — those shouldn't rewrite global preferences
        if (!options?.skipGlobalUpdate) {
          try {
            const { useSettingsStore } = await import('./useSettingsStore')
            useSettingsStore
              .getState()
              .setSelectedModelForSdk(agentSdk, model, { skipBackendPush: true })
          } catch {
            /* non-critical */
          }
        }

        // Also persist as the worktree's last-used model (only for worktree sessions)
        const scope = findSessionScope(get(), sessionId)
        if (scope?.type === 'worktree') {
          try {
            await window.db.worktree.updateModel({
              worktreeId: scope.scopeId,
              modelProviderId: model.providerID,
              modelId: model.modelID,
              modelVariant: model.variant ?? null
            })
            useWorktreeStore.getState().updateWorktreeModel(scope.scopeId, model)
          } catch {
            /* non-critical */
          }
        }
      },

      // Apply mode-specific default model when toggling modes
      applyModeDefaultModel: async (sessionId: string, newMode: SessionMode) => {
        // Import settings store dynamically to avoid circular deps
        const { useSettingsStore, resolveModelForSdk } = await import('./useSettingsStore')

        const session = get().getSessionById(sessionId)
        const settings = useSettingsStore.getState()
        const sessionSdk = session?.agent_sdk ?? settings.defaultAgentSdk ?? 'opencode'
        if (sessionSdk === 'terminal') return

        const configuredDefaultSdk = settings.defaultAgentSdk ?? 'opencode'

        // Mode defaults are configured in the context of the default SDK.
        // Only apply them when the session SDK matches; otherwise fall back to per-SDK default.
        const modeDefault =
          sessionSdk === configuredDefaultSdk ? settings.getModelForMode(newMode) : null
        const newModeDefault = modeDefault ?? resolveModelForSdk(sessionSdk, settings)
        if (!newModeDefault) {
          // No defaults configured, keep current model
          return
        }

        // Apply the new mode's default model (without rewriting global preferences)
        await get().setSessionModel(sessionId, newModeDefault, { skipGlobalUpdate: true })
      },

      // Keep opencode_session_id in sync in-memory after connect/reconnect (scope-agnostic)
      setOpenCodeSessionId: (sessionId: string, opencodeSessionId: string | null) => {
        set((state) => {
          let updatedAny = false

          // Check worktree sessions
          const newWorktreeSessionsMap = new Map(state.sessionsByWorktree)
          for (const [worktreeId, sessions] of newWorktreeSessionsMap.entries()) {
            const updatedSessions = sessions.map((s) => {
              if (s.id !== sessionId) return s
              updatedAny = true
              return { ...s, opencode_session_id: opencodeSessionId }
            })
            if (updatedAny) {
              newWorktreeSessionsMap.set(worktreeId, updatedSessions)
              return { sessionsByWorktree: newWorktreeSessionsMap }
            }
          }

          // Check connection sessions
          const newConnectionSessionsMap = new Map(state.sessionsByConnection)
          for (const [connectionId, sessions] of newConnectionSessionsMap.entries()) {
            const updatedSessions = sessions.map((s) => {
              if (s.id !== sessionId) return s
              updatedAny = true
              return { ...s, opencode_session_id: opencodeSessionId }
            })
            if (updatedAny) {
              newConnectionSessionsMap.set(connectionId, updatedSessions)
              return { sessionsByConnection: newConnectionSessionsMap }
            }
          }

          return {}
        })
      },

      markSessionFirstMessage: (sessionId: string) => {
        const stamp = Date.now()
        set((state) => {
          const stampSession = (s: Session): Session =>
            s.id === sessionId && s.first_message_at == null
              ? { ...s, first_message_at: stamp }
              : s

          let touched = false
          const newWorktreeMap = new Map(state.sessionsByWorktree)
          for (const [worktreeId, sessions] of newWorktreeMap.entries()) {
            const updated = sessions.map(stampSession)
            if (updated.some((s, i) => s !== sessions[i])) {
              newWorktreeMap.set(worktreeId, updated)
              touched = true
            }
          }
          const newConnectionMap = new Map(state.sessionsByConnection)
          for (const [connectionId, sessions] of newConnectionMap.entries()) {
            const updated = sessions.map(stampSession)
            if (updated.some((s, i) => s !== sessions[i])) {
              newConnectionMap.set(connectionId, updated)
              touched = true
            }
          }
          if (!touched) return {}
          return {
            sessionsByWorktree: newWorktreeMap,
            sessionsByConnection: newConnectionMap
          }
        })
      },

      // Set a pending initial message for a session (e.g., code review prompt)
      setPendingMessage: (sessionId: string, message: string, options?: PendingPromptOptions) => {
        set((state) => {
          const newMap = new Map(state.pendingMessages)
          const newOptionsMap = new Map(state.pendingMessageOptions)
          newMap.set(sessionId, message)
          if (options) {
            newOptionsMap.set(sessionId, options)
          } else {
            newOptionsMap.delete(sessionId)
          }
          return { pendingMessages: newMap, pendingMessageOptions: newOptionsMap }
        })
      },

      // Dequeue (get and remove) a pending message for a session
      dequeuePendingMessage: (sessionId: string): string | null => {
        const message = get().pendingMessages.get(sessionId) || null
        if (message) {
          set((state) => {
            const newMap = new Map(state.pendingMessages)
            const newOptionsMap = new Map(state.pendingMessageOptions)
            newMap.delete(sessionId)
            newOptionsMap.delete(sessionId)
            return { pendingMessages: newMap, pendingMessageOptions: newOptionsMap }
          })
        }
        return message
      },

      // Dequeue the pending initial message together with launch-specific prompt options.
      dequeuePendingMessageWithOptions: (sessionId: string): PendingInitialMessage | null => {
        const message = get().pendingMessages.get(sessionId) || null
        if (!message) return null

        const options = get().pendingMessageOptions.get(sessionId)
        set((state) => {
          const newMap = new Map(state.pendingMessages)
          const newOptionsMap = new Map(state.pendingMessageOptions)
          newMap.delete(sessionId)
          newOptionsMap.delete(sessionId)
          return { pendingMessages: newMap, pendingMessageOptions: newOptionsMap }
        })

        return options ? { message, options } : { message }
      },

      // Restore a pending message for a session (used when auto-send fails)
      requeuePendingMessage: (
        sessionId: string,
        message: string,
        options?: PendingPromptOptions
      ) => {
        set((state) => {
          const newMap = new Map(state.pendingMessages)
          const newOptionsMap = new Map(state.pendingMessageOptions)
          newMap.set(sessionId, message)
          if (options) {
            newOptionsMap.set(sessionId, options)
          } else {
            newOptionsMap.delete(sessionId)
          }
          return { pendingMessages: newMap, pendingMessageOptions: newOptionsMap }
        })
      },

      // Consume (get and remove) a pending message for a session
      consumePendingMessage: (sessionId: string): string | null => {
        return get().dequeuePendingMessage(sessionId)
      },

      // Set follow-up messages queue for a session (ordered, auto-sent after each idle)
      setPendingFollowUpMessages: (sessionId: string, messages: string[]) => {
        set((state) => {
          const newMap = new Map(state.pendingFollowUpMessages)
          newMap.set(sessionId, [...messages])
          return { pendingFollowUpMessages: newMap }
        })
      },

      // Dequeue (pop first) a follow-up message for a session
      dequeueFollowUpMessage: (sessionId: string): string | null => {
        const messages = get().pendingFollowUpMessages.get(sessionId)
        if (!messages || messages.length === 0) {
          return null
        }
        const [first, ...rest] = messages
        set((state) => {
          const newMap = new Map(state.pendingFollowUpMessages)
          if (rest.length === 0) {
            newMap.delete(sessionId)
          } else {
            newMap.set(sessionId, rest)
          }
          return { pendingFollowUpMessages: newMap }
        })
        return first
      },

      // Requeue a follow-up message at the front (used on transient send failure)
      requeueFollowUpMessageFront: (sessionId: string, message: string) => {
        set((state) => {
          const existing = state.pendingFollowUpMessages.get(sessionId) || []
          const newMap = new Map(state.pendingFollowUpMessages)
          newMap.set(sessionId, [message, ...existing])
          return { pendingFollowUpMessages: newMap }
        })
      },

      // Consume (pop first) a follow-up message for a session
      consumeFollowUpMessage: (sessionId: string): string | null => {
        return get().dequeueFollowUpMessage(sessionId)
      },

      // Close all sessions except the kept one
      closeOtherSessions: async (worktreeId: string, keepSessionId: string) => {
        const tabOrder = [...(get().tabOrderByWorktree.get(worktreeId) || [])]
        for (const sessionId of tabOrder) {
          if (sessionId !== keepSessionId) {
            await get().closeSession(sessionId)
          }
        }
        // Ensure the kept session is active
        set({ activeSessionId: keepSessionId })
      },

      // Close all sessions to the right of the given one in tab order
      closeSessionsToRight: async (worktreeId: string, fromSessionId: string) => {
        const tabOrder = [...(get().tabOrderByWorktree.get(worktreeId) || [])]
        const index = tabOrder.indexOf(fromSessionId)
        if (index === -1) return
        const toClose = tabOrder.slice(index + 1)
        for (const sessionId of toClose) {
          const result = await get().closeSession(sessionId)
          if (result.success) {
            removeSessionViewState(sessionId)
          }
        }
      },

      // Plan approval state management
      setPendingPlan: (sessionId: string, plan: PendingPlan) => {
        set((state) => {
          const newMap = new Map(state.pendingPlans)
          newMap.set(sessionId, plan)
          return { pendingPlans: newMap }
        })
      },

      clearPendingPlan: (sessionId: string) => {
        set((state) => {
          const newMap = new Map(state.pendingPlans)
          newMap.delete(sessionId)
          return { pendingPlans: newMap }
        })
      },

      getPendingPlan: (sessionId: string): PendingPlan | null => {
        return get().pendingPlans.get(sessionId) ?? null
      },

      // ─── Inline connection session actions ─────────────────────────────

      setInlineConnectionSession: (sessionId: string | null) => {
        set({ inlineConnectionSessionId: sessionId })
      },

      clearInlineConnectionSession: () => {
        set({ inlineConnectionSessionId: null })
      },

      // Load sessions for a connection without entering connection mode.
      // Used by sticky tabs in worktree mode to pre-load connection sessions.
      loadConnectionSessionsBackground: async (connectionId: string) => {
        try {
          const sessions = await window.db.session.getActiveByConnection(connectionId)
          const sortedSessions = sessions.sort(
            (a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()
          )

          set((state) => {
            const newSessionsMap = new Map(state.sessionsByConnection)
            newSessionsMap.set(connectionId, sortedSessions)

            // Initialize or sync tab order
            const newTabOrderMap = new Map(state.tabOrderByConnection)
            if (!newTabOrderMap.has(connectionId)) {
              newTabOrderMap.set(
                connectionId,
                sortedSessions.map((s) => s.id)
              )
            } else {
              const existingOrder = newTabOrderMap.get(connectionId)!
              const sessionIds = new Set(sortedSessions.map((s) => s.id))
              const validOrder = existingOrder.filter((id) => sessionIds.has(id))
              const newIds = sortedSessions
                .map((s) => s.id)
                .filter((id) => !validOrder.includes(id))
              newTabOrderMap.set(connectionId, [...validOrder, ...newIds])
            }

            // Populate mode map
            const newModeMap = new Map(state.modeBySession)
            for (const session of sortedSessions) {
              if (!newModeMap.has(session.id)) {
                newModeMap.set(session.id, session.mode || 'build')
              }
            }

            return {
              sessionsByConnection: newSessionsMap,
              tabOrderByConnection: newTabOrderMap,
              modeBySession: newModeMap
              // NOTE: does NOT touch activeSessionId or activeConnectionId
            }
          })
        } catch {
          // Non-fatal: sticky tabs just won't show sessions until next load
        }
      },

      // ─── Connection session actions ──────────────────────────────────────

      // Load active sessions for a connection
      loadConnectionSessions: async (connectionId: string) => {
        set({ isLoading: true, error: null })
        try {
          const sessions = await window.db.session.getActiveByConnection(connectionId)
          const sortedSessions = sessions.sort(
            (a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()
          )

          set((state) => {
            const newSessionsMap = new Map(state.sessionsByConnection)
            newSessionsMap.set(connectionId, sortedSessions)

            // Initialize or sync tab order
            const newTabOrderMap = new Map(state.tabOrderByConnection)
            if (!newTabOrderMap.has(connectionId)) {
              newTabOrderMap.set(
                connectionId,
                sortedSessions.map((s) => s.id)
              )
            } else {
              const existingOrder = newTabOrderMap.get(connectionId)!
              const sessionIds = new Set(sortedSessions.map((s) => s.id))
              const validOrder = existingOrder.filter((id) => sessionIds.has(id))
              const newIds = sortedSessions
                .map((s) => s.id)
                .filter((id) => !validOrder.includes(id))
              newTabOrderMap.set(connectionId, [...validOrder, ...newIds])
            }

            // Populate mode map
            const newModeMap = new Map(state.modeBySession)
            for (const session of sortedSessions) {
              if (!newModeMap.has(session.id)) {
                newModeMap.set(session.id, session.mode || 'build')
              }
            }

            // Set active session if in connection context
            let activeSessionId = state.activeSessionId
            if (
              state.activeConnectionId === connectionId &&
              !activeSessionId &&
              sortedSessions.length > 0
            ) {
              const persistedSessionId = state.activeSessionByConnection[connectionId]
              const sessionExists =
                persistedSessionId && sortedSessions.some((s) => s.id === persistedSessionId)

              if (sessionExists) {
                activeSessionId = persistedSessionId
              } else {
                const tabOrder = newTabOrderMap.get(connectionId)!
                activeSessionId = tabOrder[0] || sortedSessions[0].id
              }
            }

            return {
              sessionsByConnection: newSessionsMap,
              tabOrderByConnection: newTabOrderMap,
              modeBySession: newModeMap,
              isLoading: false,
              activeSessionId
            }
          })
        } catch (error) {
          set({
            error: error instanceof Error ? error.message : 'Failed to load connection sessions',
            isLoading: false
          })
        }
      },

      // Create a session scoped to a connection
      createConnectionSession: async (
        connectionId: string,
        agentSdkOverride?: 'opencode' | 'claude-code' | 'codex' | 'terminal',
        initialMode?: SessionMode,
        options?: { name?: string; model?: SelectedModel | null }
      ) => {
        try {
          // Look up the connection to get the first member's project_id
          const result = await window.connectionOps.get(connectionId)
          if (!result.success || !result.connection || result.connection.members.length === 0) {
            return { success: false, error: result.error || 'Connection has no members' }
          }

          const projectId = result.connection.members[0].project_id

          // Determine default model and agent SDK from global settings
          let defaultModel: { providerID: string; modelID: string; variant?: string } | null = null
          let defaultAgentSdk: 'opencode' | 'claude-code' | 'codex' | 'terminal' = 'opencode'
          try {
            const { useSettingsStore } = await import('./useSettingsStore')
            defaultAgentSdk =
              agentSdkOverride ?? useSettingsStore.getState().defaultAgentSdk ?? 'opencode'
            if (options?.model) {
              defaultModel = {
                providerID: options.model.providerID,
                modelID: options.model.modelID,
                variant: options.model.variant
              }
            } else if (defaultAgentSdk !== 'terminal') {
              const configuredDefaultSdk = useSettingsStore.getState().defaultAgentSdk ?? 'opencode'

              // Priority 1: mode-specific default (only when session SDK matches the
              // configured default — mode defaults are set in that SDK's context)
              if (defaultAgentSdk === configuredDefaultSdk) {
                const modeModel = useSettingsStore
                  .getState()
                  .getModelForMode(initialMode ?? 'build')
                if (modeModel) {
                  defaultModel = modeModel
                }
              }

              // Priority 2: per-provider default → (legacy) global default
              if (!defaultModel) {
                const { resolveModelForSdk } = await import('./useSettingsStore')
                defaultModel = resolveModelForSdk(defaultAgentSdk)
              }
            }
          } catch {
            /* non-critical */
          }

          const isTerminal = defaultAgentSdk === 'terminal'
          const existingSessions = get().sessionsByConnection.get(connectionId) || []
          const sessionNumber = existingSessions.length + 1

          const sessionName =
            options?.name?.trim() ||
            (isTerminal ? `Terminal ${sessionNumber}` : `Session ${sessionNumber}`)

          const session = await window.db.session.create({
            worktree_id: null,
            project_id: projectId,
            connection_id: connectionId,
            name: sessionName,
            agent_sdk: defaultAgentSdk,
            ...(defaultModel
              ? {
                  model_provider_id: defaultModel.providerID,
                  model_id: defaultModel.modelID,
                  model_variant: defaultModel.variant ?? null
                }
              : {})
          })

          set((state) => {
            const newSessionsMap = new Map(state.sessionsByConnection)
            const existing = newSessionsMap.get(connectionId) || []
            newSessionsMap.set(connectionId, [session, ...existing])

            const newTabOrderMap = new Map(state.tabOrderByConnection)
            const existingOrder = newTabOrderMap.get(connectionId) || []
            newTabOrderMap.set(connectionId, [...existingOrder, session.id])

            const newModeMap = new Map(state.modeBySession)
            newModeMap.set(session.id, session.mode || 'build')

            return {
              sessionsByConnection: newSessionsMap,
              tabOrderByConnection: newTabOrderMap,
              modeBySession: newModeMap,
              activeSessionId: session.id,
              activeSessionByConnection: {
                ...state.activeSessionByConnection,
                [connectionId]: session.id
              }
            }
          })

          return { success: true, session }
        } catch (error) {
          return {
            success: false,
            error:
              error instanceof Error
                ? error.message
                : t('sessionStore.errors.createConnectionSession')
          }
        }
      },

      // Set active session in connection context
      setActiveConnectionSession: (sessionId: string | null) => {
        const connectionId = get().activeConnectionId
        if (sessionId && connectionId) {
          set((state) => ({
            activeSessionId: sessionId,
            activeSessionByConnection: {
              ...state.activeSessionByConnection,
              [connectionId]: sessionId
            }
          }))
        } else {
          set({ activeSessionId: sessionId })
        }
      },

      // Set active connection and restore its last active session
      setActiveConnection: (connectionId: string | null) => {
        const state = get()

        if (connectionId === state.activeConnectionId) return

        set({ activeConnectionId: connectionId, activeWorktreeId: null })

        if (connectionId) {
          const existingSessions = state.sessionsByConnection.get(connectionId)
          if (existingSessions) {
            const persistedSessionId = state.activeSessionByConnection[connectionId]
            const sessionExists =
              persistedSessionId && existingSessions.some((s) => s.id === persistedSessionId)

            if (sessionExists) {
              set({ activeSessionId: persistedSessionId })
            } else {
              const tabOrder = state.tabOrderByConnection.get(connectionId) || []
              const activeId =
                tabOrder[0] || (existingSessions.length > 0 ? existingSessions[0].id : null)
              set({ activeSessionId: activeId })
            }
          } else {
            set({ activeSessionId: null })
          }
        } else {
          set({ activeSessionId: null })
        }
      },

      // Get sessions for a connection
      getSessionsForConnection: (connectionId: string) => {
        return get().sessionsByConnection.get(connectionId) || []
      },

      // Get tab order for a connection
      getTabOrderForConnection: (connectionId: string) => {
        return get().tabOrderByConnection.get(connectionId) || []
      },

      // Reorder connection tabs
      reorderConnectionTabs: (connectionId: string, fromIndex: number, toIndex: number) => {
        set((state) => {
          const newTabOrderMap = new Map(state.tabOrderByConnection)
          const order = [...(newTabOrderMap.get(connectionId) || [])]

          if (
            fromIndex < 0 ||
            fromIndex >= order.length ||
            toIndex < 0 ||
            toIndex >= order.length
          ) {
            return state
          }

          const [removed] = order.splice(fromIndex, 1)
          order.splice(toIndex, 0, removed)

          newTabOrderMap.set(connectionId, order)
          return { tabOrderByConnection: newTabOrderMap }
        })
      },

      // Close all connection sessions except the kept one
      closeOtherConnectionSessions: async (connectionId: string, keepSessionId: string) => {
        const tabOrder = [...(get().tabOrderByConnection.get(connectionId) || [])]
        for (const sessionId of tabOrder) {
          if (sessionId !== keepSessionId) {
            await get().closeSession(sessionId)
          }
        }
        set({ activeSessionId: keepSessionId })
      },

      // Close connection sessions to the right of the given one
      closeConnectionSessionsToRight: async (connectionId: string, fromSessionId: string) => {
        const tabOrder = [...(get().tabOrderByConnection.get(connectionId) || [])]
        const index = tabOrder.indexOf(fromSessionId)
        if (index === -1) return
        const toClose = tabOrder.slice(index + 1)
        for (const sessionId of toClose) {
          await get().closeSession(sessionId)
        }
      }
    }),
    {
      name: 'hive-session-tabs',
      storage: createJSONStorage(() => localStorage),
      partialize: (state) => ({
        activeSessionByWorktree: state.activeSessionByWorktree,
        activeSessionByConnection: state.activeSessionByConnection
      })
    }
  )
)
