import { createContext, createMemo, createSignal, onMount, useContext } from "solid-js"
import type { Accessor, ParentComponent } from "solid-js"
import { storage, type OwnerBucket } from "../lib/storage"
import {
  ensureInstanceConfigLoaded,
  getInstanceConfig,
  updateInstanceConfig as updateInstanceData,
} from "./instance-config"
import { getLogger } from "../lib/logger"

const log = getLogger("actions")

type DeepReadonly<T> = T extends (...args: any[]) => unknown
  ? T
  : T extends Array<infer U>
    ? ReadonlyArray<DeepReadonly<U>>
    : T extends object
      ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
      : T

export interface ModelPreference {
  providerId: string
  modelId: string
}

export type DiffViewMode = "split" | "unified"
export type ExpansionPreference = "expanded" | "collapsed"
export type ToolInputsVisibilityPreference = "hidden" | "collapsed" | "expanded"
export type ListeningMode = "local" | "all"

export interface UiSettings {
  showThinkingBlocks: boolean
  showKeyboardShortcutHints: boolean
  thinkingBlocksExpansion: ExpansionPreference
  showTimelineTools: boolean
  promptSubmitOnEnter: boolean
  locale?: string
  diffViewMode: DiffViewMode
  toolOutputExpansion: ExpansionPreference
  diagnosticsExpansion: ExpansionPreference
  toolInputsVisibility: ToolInputsVisibilityPreference
  showUsageMetrics: boolean
  autoCleanupBlankSessions: boolean

  // OS notifications
  osNotificationsEnabled: boolean
  osNotificationsAllowWhenVisible: boolean
  notifyOnNeedsInput: boolean
  notifyOnIdle: boolean
}

// Backwards-compatible alias for older imports.
export type Preferences = UiSettings

export interface OpenCodeBinary {
  path: string
  version?: string
  lastUsed: number
  label?: string
}

export interface RecentFolder {
  path: string
  lastAccessed: number
}

export type ThemePreference = "light" | "dark" | "system"

interface UiConfigBucket {
  theme?: ThemePreference
  settings?: Partial<UiSettings>
}

interface ServerConfigBucket {
  listeningMode?: ListeningMode
  environmentVariables?: Record<string, string>
  opencodeBinary?: string
}

interface UiStateBucket {
  recentFolders?: RecentFolder[]
  opencodeBinaries?: OpenCodeBinary[]
  models?: {
    recents?: ModelPreference[]
    favorites?: ModelPreference[]
    thinkingSelections?: Record<string, string>
  }
}

interface NormalizedUiState {
  recentFolders: RecentFolder[]
  opencodeBinaries: OpenCodeBinary[]
  models: {
    recents: ModelPreference[]
    favorites: ModelPreference[]
    thinkingSelections: Record<string, string>
  }
}

const MAX_RECENT_FOLDERS = 20
const MAX_RECENT_MODELS = 5
const MAX_FAVORITE_MODELS = 50

const defaultUiSettings: UiSettings = {
  showThinkingBlocks: false,
  showKeyboardShortcutHints: true,
  thinkingBlocksExpansion: "expanded",
  showTimelineTools: true,
  promptSubmitOnEnter: false,
  diffViewMode: "split",
  toolOutputExpansion: "expanded",
  diagnosticsExpansion: "expanded",
  toolInputsVisibility: "collapsed",
  showUsageMetrics: true,
  autoCleanupBlankSessions: true,

  osNotificationsEnabled: false,
  osNotificationsAllowWhenVisible: false,
  notifyOnNeedsInput: true,
  notifyOnIdle: true,
}

function normalizeUiSettings(input?: Partial<UiSettings> | null): UiSettings {
  const sanitized = input ?? {}
  return {
    showThinkingBlocks: sanitized.showThinkingBlocks ?? defaultUiSettings.showThinkingBlocks,
    showKeyboardShortcutHints:
      sanitized.showKeyboardShortcutHints ?? defaultUiSettings.showKeyboardShortcutHints,
    thinkingBlocksExpansion: sanitized.thinkingBlocksExpansion ?? defaultUiSettings.thinkingBlocksExpansion,
    showTimelineTools: sanitized.showTimelineTools ?? defaultUiSettings.showTimelineTools,
    promptSubmitOnEnter: sanitized.promptSubmitOnEnter ?? defaultUiSettings.promptSubmitOnEnter,
    locale: sanitized.locale ?? defaultUiSettings.locale,
    diffViewMode: sanitized.diffViewMode ?? defaultUiSettings.diffViewMode,
    toolOutputExpansion: sanitized.toolOutputExpansion ?? defaultUiSettings.toolOutputExpansion,
    diagnosticsExpansion: sanitized.diagnosticsExpansion ?? defaultUiSettings.diagnosticsExpansion,
    toolInputsVisibility:
      sanitized.toolInputsVisibility === "hidden" || sanitized.toolInputsVisibility === "collapsed" || sanitized.toolInputsVisibility === "expanded"
        ? sanitized.toolInputsVisibility
        : defaultUiSettings.toolInputsVisibility,
    showUsageMetrics: sanitized.showUsageMetrics ?? defaultUiSettings.showUsageMetrics,
    autoCleanupBlankSessions: sanitized.autoCleanupBlankSessions ?? defaultUiSettings.autoCleanupBlankSessions,
    osNotificationsEnabled: sanitized.osNotificationsEnabled ?? defaultUiSettings.osNotificationsEnabled,
    osNotificationsAllowWhenVisible:
      sanitized.osNotificationsAllowWhenVisible ?? defaultUiSettings.osNotificationsAllowWhenVisible,
    notifyOnNeedsInput: sanitized.notifyOnNeedsInput ?? defaultUiSettings.notifyOnNeedsInput,
    notifyOnIdle: sanitized.notifyOnIdle ?? defaultUiSettings.notifyOnIdle,
  }
}

function normalizeRecord(value: unknown): Record<string, string> {
  if (!value || typeof value !== "object" || Array.isArray(value)) return {}
  const out: Record<string, string> = {}
  for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
    if (typeof v === "string") out[k] = v
  }
  return out
}

function cloneArray<T>(value: unknown, mapper: (item: any) => T | null): T[] {
  if (!Array.isArray(value)) return []
  const out: T[] = []
  for (const item of value) {
    const mapped = mapper(item)
    if (mapped) out.push(mapped)
  }
  return out
}

function normalizeUiState(input?: UiStateBucket | null): NormalizedUiState {
  const source = input ?? {}
  return {
    recentFolders: cloneArray<RecentFolder>(source.recentFolders, (f) => {
      if (!f || typeof f !== "object") return null
      const p = (f as any).path
      const lastAccessed = (f as any).lastAccessed
      if (typeof p !== "string") return null
      const ts = typeof lastAccessed === "number" ? lastAccessed : Date.now()
      return { path: p, lastAccessed: ts }
    }),
    opencodeBinaries: cloneArray<OpenCodeBinary>(source.opencodeBinaries, (b) => {
      if (!b || typeof b !== "object") return null
      const p = (b as any).path
      if (typeof p !== "string") return null
      const lastUsed = typeof (b as any).lastUsed === "number" ? (b as any).lastUsed : Date.now()
      const version = typeof (b as any).version === "string" ? (b as any).version : undefined
      const label = typeof (b as any).label === "string" ? (b as any).label : undefined
      return { path: p, version, label, lastUsed }
    }),
    models: {
      recents: cloneArray<ModelPreference>((source.models as any)?.recents, (m) => {
        if (!m || typeof m !== "object") return null
        const providerId = (m as any).providerId
        const modelId = (m as any).modelId
        if (typeof providerId !== "string" || typeof modelId !== "string") return null
        return { providerId, modelId }
      }),
      favorites: cloneArray<ModelPreference>((source.models as any)?.favorites, (m) => {
        if (!m || typeof m !== "object") return null
        const providerId = (m as any).providerId
        const modelId = (m as any).modelId
        if (typeof providerId !== "string" || typeof modelId !== "string") return null
        return { providerId, modelId }
      }),
      thinkingSelections: normalizeRecord((source.models as any)?.thinkingSelections),
    },
  }
}

function normalizeServerConfig(input?: ServerConfigBucket | null): Required<Pick<ServerConfigBucket, "listeningMode" | "environmentVariables" | "opencodeBinary">> {
  const source = input ?? {}
  const listeningMode = source.listeningMode === "all" ? "all" : "local"
  const opencodeBinary = typeof source.opencodeBinary === "string" && source.opencodeBinary.trim() ? source.opencodeBinary : "opencode"
  const environmentVariables = normalizeRecord(source.environmentVariables)
  return { listeningMode, opencodeBinary, environmentVariables }
}

function getModelKey(model: { providerId: string; modelId: string }): string {
  return `${model.providerId}/${model.modelId}`
}

function buildRecentFolderList(folderPath: string, source: RecentFolder[]): RecentFolder[] {
  const folders = source.filter((f) => f.path !== folderPath)
  folders.unshift({ path: folderPath, lastAccessed: Date.now() })
  return folders.slice(0, MAX_RECENT_FOLDERS)
}

function buildBinaryList(binaryPath: string, version: string | undefined, source: OpenCodeBinary[]): OpenCodeBinary[] {
  const timestamp = Date.now()
  const existing = source.find((b) => b.path === binaryPath)
  if (existing) {
    const updatedEntry: OpenCodeBinary = { ...existing, lastUsed: timestamp, version: version ?? existing.version }
    const remaining = source.filter((b) => b.path !== binaryPath)
    return [updatedEntry, ...remaining]
  }
  const nextEntry: OpenCodeBinary = version
    ? { path: binaryPath, version, lastUsed: timestamp }
    : { path: binaryPath, lastUsed: timestamp }
  return [nextEntry, ...source].slice(0, 10)
}

const [uiConfigBucket, setUiConfigBucket] = createSignal<UiConfigBucket>({})
const [serverConfigBucket, setServerConfigBucket] = createSignal<ServerConfigBucket>({})
const [uiStateBucket, setUiStateBucket] = createSignal<UiStateBucket>({})
const [isLoaded, setIsLoaded] = createSignal(false)

const uiSettings = createMemo<UiSettings>(() => normalizeUiSettings(uiConfigBucket().settings))
const themePreference = createMemo<ThemePreference>(() => uiConfigBucket().theme ?? "system")
const serverSettings = createMemo(() => normalizeServerConfig(serverConfigBucket()))
const uiState = createMemo(() => normalizeUiState(uiStateBucket()))

const preferences = uiSettings
const recentFolders = createMemo<RecentFolder[]>(() => uiState().recentFolders)
const opencodeBinaries = createMemo<OpenCodeBinary[]>(() => uiState().opencodeBinaries)

let loadPromise: Promise<void> | null = null

async function ensureLoaded(): Promise<void> {
  if (isLoaded()) return
  if (!loadPromise) {
    loadPromise = Promise.all([
      storage.loadConfigOwner("ui"),
      storage.loadConfigOwner("server"),
      storage.loadStateOwner("ui"),
    ])
      .then(([uiCfg, srvCfg, uiSt]) => {
        setUiConfigBucket(uiCfg as any)
        setServerConfigBucket(srvCfg as any)
        setUiStateBucket(uiSt as any)
        setIsLoaded(true)
      })
      .catch((error) => {
        log.error("Failed to load settings", error)
        setUiConfigBucket({})
        setServerConfigBucket({})
        setUiStateBucket({})
        setIsLoaded(true)
      })
      .finally(() => {
        loadPromise = null
      })
  }
  await loadPromise
}

async function patchConfigOwner(owner: string, patch: unknown) {
  await ensureLoaded()
  const updated = await storage.patchConfigOwner(owner, patch)
  if (owner === "ui") setUiConfigBucket(updated as any)
  if (owner === "server") setServerConfigBucket(updated as any)
}

async function patchStateOwner(owner: string, patch: unknown) {
  await ensureLoaded()
  const updated = await storage.patchStateOwner(owner, patch)
  if (owner === "ui") setUiStateBucket(updated as any)
}

function updateUiSettings(updates: Partial<UiSettings>) {
  const current = uiConfigBucket()
  const nextSettings = normalizeUiSettings({ ...(current.settings ?? {}), ...updates })
  const patch = { settings: nextSettings }
  void patchConfigOwner("ui", patch).catch((error) => log.error("Failed to patch ui settings", error))
}

function updatePreferences(updates: Partial<UiSettings>): void {
  updateUiSettings(updates)
}

function setThemePreference(preference: ThemePreference): void {
  if (themePreference() === preference) return
  void patchConfigOwner("ui", { theme: preference }).catch((error) => log.error("Failed to set theme", error))
}

 async function setListeningMode(mode: ListeningMode): Promise<void> {
   if (serverSettings().listeningMode === mode) return
   await patchConfigOwner("server", { listeningMode: mode })
 }

function updateEnvironmentVariables(envVars: Record<string, string>): void {
  void patchConfigOwner("server", { environmentVariables: envVars }).catch((error) =>
    log.error("Failed to update environment variables", error),
  )
}

function addEnvironmentVariable(key: string, value: string): void {
  const current = serverSettings().environmentVariables
  updateEnvironmentVariables({ ...current, [key]: value })
}

function removeEnvironmentVariable(key: string): void {
  const current = serverSettings().environmentVariables
  const { [key]: removed, ...rest } = current
  updateEnvironmentVariables(rest)
}

function updateLastUsedBinary(path: string): void {
  const target = path && path.trim().length > 0 ? path : "opencode"
  void patchConfigOwner("server", { opencodeBinary: target }).catch((error) => log.error("Failed to set default binary", error))

  // also bump lastUsed in state ui.opencodeBinaries
  const nextList = buildBinaryList(target, undefined, opencodeBinaries())
  void patchStateOwner("ui", { opencodeBinaries: nextList }).catch((error) => log.error("Failed to update binary list", error))
}

function addOpenCodeBinary(path: string, version?: string): void {
  const nextList = buildBinaryList(path, version, opencodeBinaries())
  void patchStateOwner("ui", { opencodeBinaries: nextList }).catch((error) => log.error("Failed to add binary", error))
}

function removeOpenCodeBinary(path: string): void {
  const nextList = opencodeBinaries().filter((b) => b.path !== path)
  void patchStateOwner("ui", { opencodeBinaries: nextList }).catch((error) => log.error("Failed to remove binary", error))

  if (serverSettings().opencodeBinary === path) {
    void patchConfigOwner("server", { opencodeBinary: "opencode" }).catch((error) =>
      log.error("Failed to reset default binary", error),
    )
  }
}

function addRecentFolder(folderPath: string): void {
  const next = buildRecentFolderList(folderPath, recentFolders())
  void patchStateOwner("ui", { recentFolders: next }).catch((error) => log.error("Failed to add recent folder", error))
}

function removeRecentFolder(folderPath: string): void {
  const next = recentFolders().filter((f) => f.path !== folderPath)
  void patchStateOwner("ui", { recentFolders: next }).catch((error) => log.error("Failed to remove recent folder", error))
}

function recordWorkspaceLaunch(folderPath: string, binaryPath?: string): void {
  const targetBinary = binaryPath && binaryPath.trim().length > 0 ? binaryPath : serverSettings().opencodeBinary
  const nextFolders = buildRecentFolderList(folderPath, recentFolders())
  const nextBinaries = buildBinaryList(targetBinary, undefined, opencodeBinaries())

  void patchStateOwner("ui", { recentFolders: nextFolders, opencodeBinaries: nextBinaries }).catch((error) =>
    log.error("Failed to update ui state on launch", error),
  )
  void patchConfigOwner("server", { opencodeBinary: targetBinary }).catch((error) =>
    log.error("Failed to persist selected binary", error),
  )
}

function addRecentModelPreference(model: ModelPreference): void {
  if (!model.providerId || !model.modelId) return
  const recents = uiState().models.recents
  const filtered = recents.filter((item) => item.providerId !== model.providerId || item.modelId !== model.modelId)
  const updated = [model, ...filtered].slice(0, MAX_RECENT_MODELS)
  void patchStateOwner("ui", { models: { recents: updated } }).catch((error) => log.error("Failed to update model recents", error))
}

function isFavoriteModelPreference(model: ModelPreference): boolean {
  if (!model.providerId || !model.modelId) return false
  return uiState().models.favorites.some((item) => item.providerId === model.providerId && item.modelId === model.modelId)
}

function toggleFavoriteModelPreference(model: ModelPreference): void {
  if (!model.providerId || !model.modelId) return
  const favorites = uiState().models.favorites
  const exists = favorites.some((item) => item.providerId === model.providerId && item.modelId === model.modelId)

  const updated = exists
    ? favorites.filter((item) => item.providerId !== model.providerId || item.modelId !== model.modelId)
    : [model, ...favorites.filter((item) => item.providerId !== model.providerId || item.modelId !== model.modelId)].slice(
        0,
        MAX_FAVORITE_MODELS,
      )

  void patchStateOwner("ui", { models: { favorites: updated } }).catch((error) => log.error("Failed to update model favorites", error))
}

function getModelThinkingSelection(model: { providerId: string; modelId: string }): string | undefined {
  if (!model.providerId || !model.modelId) return undefined
  return uiState().models.thinkingSelections[getModelKey(model)]
}

function setModelThinkingSelection(model: { providerId: string; modelId: string }, value: string | undefined): void {
  if (!model.providerId || !model.modelId) return
  const key = getModelKey(model)
  const current = uiState().models.thinkingSelections[key]
  if (current === value) return

  const selections = { ...uiState().models.thinkingSelections }
  if (!value) {
    delete selections[key]
  } else {
    selections[key] = value
  }
  void patchStateOwner("ui", { models: { thinkingSelections: selections } }).catch((error) =>
    log.error("Failed to update thinking selection", error),
  )
}

function setDiffViewMode(mode: DiffViewMode): void {
  if (preferences().diffViewMode === mode) return
  updateUiSettings({ diffViewMode: mode })
}

function setToolOutputExpansion(mode: ExpansionPreference): void {
  if (preferences().toolOutputExpansion === mode) return
  updateUiSettings({ toolOutputExpansion: mode })
}

function setDiagnosticsExpansion(mode: ExpansionPreference): void {
  if (preferences().diagnosticsExpansion === mode) return
  updateUiSettings({ diagnosticsExpansion: mode })
}

function setToolInputsVisibility(mode: ToolInputsVisibilityPreference): void {
  if (preferences().toolInputsVisibility === mode) return
  updateUiSettings({ toolInputsVisibility: mode })
}

function setThinkingBlocksExpansion(mode: ExpansionPreference): void {
  if (preferences().thinkingBlocksExpansion === mode) return
  updateUiSettings({ thinkingBlocksExpansion: mode })
}

function toggleShowThinkingBlocks(): void {
  updateUiSettings({ showThinkingBlocks: !preferences().showThinkingBlocks })
}

function toggleKeyboardShortcutHints(): void {
  updatePreferences({ showKeyboardShortcutHints: !preferences().showKeyboardShortcutHints })
}

function toggleShowTimelineTools(): void {
  updateUiSettings({ showTimelineTools: !preferences().showTimelineTools })
}

function toggleUsageMetrics(): void {
  updateUiSettings({ showUsageMetrics: !preferences().showUsageMetrics })
}

function togglePromptSubmitOnEnter(): void {
  updateUiSettings({ promptSubmitOnEnter: !preferences().promptSubmitOnEnter })
}

function toggleAutoCleanupBlankSessions(): void {
  const nextValue = !preferences().autoCleanupBlankSessions
  log.info("toggle auto cleanup", { value: nextValue })
  updateUiSettings({ autoCleanupBlankSessions: nextValue })
}

async function setAgentModelPreference(instanceId: string, agent: string, model: ModelPreference): Promise<void> {
  if (!instanceId || !agent || !model.providerId || !model.modelId) return
  await ensureInstanceConfigLoaded(instanceId)
  await updateInstanceData(instanceId, (draft) => {
    const selections = { ...(draft.agentModelSelections ?? {}) }
    const existing = selections[agent]
    if (existing && existing.providerId === model.providerId && existing.modelId === model.modelId) {
      return
    }
    selections[agent] = model
    draft.agentModelSelections = selections
  })
}

async function getAgentModelPreference(instanceId: string, agent: string): Promise<ModelPreference | undefined> {
  if (!instanceId || !agent) return undefined
  await ensureInstanceConfigLoaded(instanceId)
  const selections = getInstanceConfig(instanceId).agentModelSelections ?? {}
  return selections[agent]
}

void ensureLoaded().catch((error: unknown) => {
  log.error("Failed to initialize settings", error)
})

interface ConfigContextValue {
  isLoaded: Accessor<boolean>
  preferences: typeof preferences
  updatePreferences: typeof updatePreferences
  themePreference: typeof themePreference
  setThemePreference: typeof setThemePreference

  // server-owned stable config
  serverSettings: typeof serverSettings
  setListeningMode: typeof setListeningMode
  updateEnvironmentVariables: typeof updateEnvironmentVariables
  addEnvironmentVariable: typeof addEnvironmentVariable
  removeEnvironmentVariable: typeof removeEnvironmentVariable
  updateLastUsedBinary: typeof updateLastUsedBinary

  // ui-owned state
  recentFolders: typeof recentFolders
  opencodeBinaries: typeof opencodeBinaries
  uiState: typeof uiState
  addRecentFolder: typeof addRecentFolder
  removeRecentFolder: typeof removeRecentFolder
  addOpenCodeBinary: typeof addOpenCodeBinary
  removeOpenCodeBinary: typeof removeOpenCodeBinary
  recordWorkspaceLaunch: typeof recordWorkspaceLaunch
  addRecentModelPreference: typeof addRecentModelPreference
  isFavoriteModelPreference: typeof isFavoriteModelPreference
  toggleFavoriteModelPreference: typeof toggleFavoriteModelPreference
  getModelThinkingSelection: typeof getModelThinkingSelection
  setModelThinkingSelection: typeof setModelThinkingSelection

  // ui settings helpers
  toggleShowThinkingBlocks: typeof toggleShowThinkingBlocks
  toggleKeyboardShortcutHints: typeof toggleKeyboardShortcutHints
  toggleShowTimelineTools: typeof toggleShowTimelineTools
  toggleUsageMetrics: typeof toggleUsageMetrics
  toggleAutoCleanupBlankSessions: typeof toggleAutoCleanupBlankSessions
  togglePromptSubmitOnEnter: typeof togglePromptSubmitOnEnter
  setDiffViewMode: typeof setDiffViewMode
  setToolOutputExpansion: typeof setToolOutputExpansion
  setDiagnosticsExpansion: typeof setDiagnosticsExpansion
  setThinkingBlocksExpansion: typeof setThinkingBlocksExpansion
  setToolInputsVisibility: typeof setToolInputsVisibility

  // instance scoped
  setAgentModelPreference: typeof setAgentModelPreference
  getAgentModelPreference: typeof getAgentModelPreference
}

const ConfigContext = createContext<ConfigContextValue>()

const configContextValue: ConfigContextValue = {
  isLoaded,
  preferences,
  updatePreferences,
  themePreference,
  setThemePreference,
  serverSettings,
  setListeningMode,
  updateEnvironmentVariables,
  addEnvironmentVariable,
  removeEnvironmentVariable,
  updateLastUsedBinary,
  recentFolders,
  opencodeBinaries,
  uiState,
  addRecentFolder,
  removeRecentFolder,
  addOpenCodeBinary,
  removeOpenCodeBinary,
  recordWorkspaceLaunch,
  addRecentModelPreference,
  isFavoriteModelPreference,
  toggleFavoriteModelPreference,
  getModelThinkingSelection,
  setModelThinkingSelection,
  toggleShowThinkingBlocks,
  toggleKeyboardShortcutHints,
  toggleShowTimelineTools,
  toggleUsageMetrics,
  toggleAutoCleanupBlankSessions,
  togglePromptSubmitOnEnter,
  setDiffViewMode,
  setToolOutputExpansion,
  setDiagnosticsExpansion,
  setThinkingBlocksExpansion,
  setToolInputsVisibility,
  setAgentModelPreference,
  getAgentModelPreference,
}

export const ConfigProvider: ParentComponent = (props) => {
  onMount(() => {
    ensureLoaded().catch((error: unknown) => {
      log.error("Failed to initialize settings", error)
    })

    const unsubUi = storage.onConfigOwnerChanged("ui", (bucket) => {
      setUiConfigBucket(bucket as any)
      setIsLoaded(true)
    })
    const unsubServer = storage.onConfigOwnerChanged("server", (bucket) => {
      setServerConfigBucket(bucket as any)
      setIsLoaded(true)
    })
    const unsubStateUi = storage.onStateOwnerChanged("ui", (bucket) => {
      setUiStateBucket(bucket as any)
      setIsLoaded(true)
    })

    return () => {
      unsubUi()
      unsubServer()
      unsubStateUi()
    }
  })

  return <ConfigContext.Provider value={configContextValue}>{props.children}</ConfigContext.Provider>
}

export function useConfig(): ConfigContextValue {
  const context = useContext(ConfigContext)
  if (!context) {
    throw new Error("useConfig must be used within ConfigProvider")
  }
  return context
}

export {
  preferences,
  uiState,
  serverSettings,
  recentFolders,
  opencodeBinaries,
  themePreference,
  setThemePreference,
  updatePreferences,
  setListeningMode,
  updateEnvironmentVariables,
  addEnvironmentVariable,
  removeEnvironmentVariable,
  updateLastUsedBinary,
  addRecentFolder,
  removeRecentFolder,
  addOpenCodeBinary,
  removeOpenCodeBinary,
  recordWorkspaceLaunch,
  addRecentModelPreference,
  isFavoriteModelPreference,
  toggleFavoriteModelPreference,
  getModelThinkingSelection,
  setModelThinkingSelection,
  toggleShowThinkingBlocks,
  toggleKeyboardShortcutHints,
  toggleShowTimelineTools,
  toggleUsageMetrics,
  toggleAutoCleanupBlankSessions,
  togglePromptSubmitOnEnter,
  setDiffViewMode,
  setToolOutputExpansion,
  setDiagnosticsExpansion,
  setThinkingBlocksExpansion,
  setAgentModelPreference,
  getAgentModelPreference,
}
