import type MarkdownIt from 'markdown-it'
import type { Ref, UnwrapNestedRefs } from 'vue'
import type { OARequest } from '../lib/codeSamples/request'
import type { OperationSlot, ParsedOperation } from '../types'
import vitesseDark from '@shikijs/themes/vitesse-dark'
import vitesseLight from '@shikijs/themes/vitesse-light'
import { useDark } from '@vueuse/core'
import { ref } from 'vue'
import { generateCodeSample } from '../lib/codeSamples/generateCodeSample'
import { deepUnref } from '../lib/utils/deepUnref'
import { locales } from '../locales'

function ensureNestedProperty<T, K extends keyof T>(obj: T, key: K): Exclude<NonNullable<T[K]>, false> {
  if (!obj[key]) {
    obj[key] = {} as T[K]
  }
  return obj[key] as Exclude<NonNullable<T[K]>, false>
}

function ensureRefProperty<T, K extends keyof T, V>(obj: T, key: K, propName: string, value: V): Ref<V> {
  const container = ensureNestedProperty(obj, key) as any
  if (!container[propName]) {
    container[propName] = ref(value)
  } else if (value !== undefined) {
    container[propName].value = value
  }
  return container[propName]
}

function ensureNestedRefProperty<V>(obj: any, path: string[], propName: string, value: V): Ref<V> {
  let current = obj
  for (const segment of path) {
    current = ensureNestedProperty(current, segment)
  }

  if (!current[propName]) {
    current[propName] = ref(value)
  } else if (value !== undefined) {
    current[propName].value = value
  }

  return current[propName]
}

export type BodyViewType = 'schema' | 'contentType' | 'autogenerated'
export type JsonRendererType = 'vue-json-pretty' | 'shiki' | string
export type ResponseCodeSelectorType = 'tabs' | 'select'
export type OperationColsType = 1 | 2
export type OperationBadges = 'deprecated' | 'operationId'
export type PlaygroundJsonEditorMode = 'text' | 'tree' | 'table'
export type Languages = 'es' | 'en' | 'ja' | 'pt-BR' | 'zh' | string
export type Messages = Record<Languages, Record<string, string>>

export interface ThemeConfig {
  highlighterTheme?: {
    light?: any
    dark?: any
  }
}

export interface PathConfig {
  showBaseURL?: Ref<boolean>
}

export interface RequestConfig {
  defaultView?: Ref<BodyViewType>
}

export interface JsonViewerConfig {
  deep?: Ref<number>
  renderer?: Ref<JsonRendererType>
}

export interface SchemaViewerConfig {
  deep?: Ref<number>
}

export interface HeadingLevels {
  h1?: number
  h2?: number
  h3?: number
  h4?: number
  h5?: number
  h6?: number
}

export interface ResponseConfig {
  responseCodeSelector?: Ref<ResponseCodeSelectorType>
  maxTabs?: Ref<number>
  body?: {
    defaultView?: Ref<BodyViewType>
  }
}

export type PlaygroundExampleBehavior = 'placeholder' | 'value' | 'ignore'

export interface PlaygroundConfig {
  jsonEditor?: {
    mode?: Ref<PlaygroundJsonEditorMode>
    mainMenuBar?: Ref<boolean>
    navigationBar?: Ref<boolean>
    statusBar?: Ref<boolean>
  }

  examples?: {
    behavior?: Ref<PlaygroundExampleBehavior>
    playgroundExampleBehavior?: Ref<PlaygroundExampleBehavior>
  }
}

export interface SecurityConfig {
  defaultScheme?: Ref<string | null>
}

export interface OperationConfig {
  badges?: Ref<OperationBadges[]>
  slots?: Ref<OperationSlot[]>
  /** @deprecated Use server.getServers instead */
  getServers?: GetServersFunction | null
  hiddenSlots?: Ref<OperationSlot[]>
  cols?: Ref<OperationColsType>
  defaultBaseUrl?: string
}

export interface AvailableLocale {
  code: string
  label: string
}

export interface I18nConfig {
  locale?: Ref<Languages>
  fallbackLocale?: Ref<Languages>
  messages?: Messages
  availableLocales?: AvailableLocale[]
}

export interface SpecConfig {
  groupByTags?: Ref<boolean>
  collapsePaths?: Ref<boolean>
  showPathsSummary?: Ref<boolean>
  avoidCirculars?: Ref<boolean>
  lazyRendering?: Ref<boolean>
  defaultTag?: string
  defaultTagDescription?: string
  wrapExamples?: boolean
  disableDownload?: Ref<boolean>
}

export interface ServerConfig {
  allowCustomServer?: boolean
  getServers?: GetServersFunction | null
}

export interface StorageConfig {
  prefix?: string
  persistAuth?: boolean
}

export interface OperationLinkConfig {
  linkPrefix?: string
  transformHref?: (href: string) => string
}

export interface MarkdownConfig {
  operationLink?: OperationLinkConfig | false
  externalLinksNewTab?: boolean
  config?: (md: MarkdownIt) => MarkdownIt | undefined
}

export interface UseThemeConfig {
  theme?: Partial<ThemeConfig>
  path?: Partial<PathConfig>
  requestBody?: Partial<RequestConfig>
  jsonViewer?: Partial<JsonViewerConfig>
  schemaViewer?: Partial<SchemaViewerConfig>
  headingLevels?: Partial<HeadingLevels>
  response?: Partial<ResponseConfig>
  playground?: Partial<PlaygroundConfig>
  security?: Partial<SecurityConfig>
  operation?: Partial<OperationConfig>
  i18n?: Partial<I18nConfig>
  spec?: Partial<SpecConfig>
  codeSamples?: Partial<CodeSamplesConfig>
  linksPrefixes?: Partial<LinksPrefixesConfig>
  server?: Partial<ServerConfig>
  storage?: Partial<StorageConfig>
  markdown?: Partial<MarkdownConfig>
}

export interface CodeSamplesConfig {
  defaultLang: string
  availableLanguages: LanguageConfig[]
  generator: GeneratorFunction
  defaultHeaders: Record<string, string>
}

export interface LinksPrefixesConfig {
  tags: string
  operations: string
}

export interface LanguageConfig {
  /** Unique identifier and Shiki highlighter language (e.g., 'curl', 'javascript', 'php') */
  lang: string
  /** Display name shown in the UI */
  label: string
  /** Target language for @scalar/snippetz code generation (e.g., 'js', 'shell', 'python') */
  target?: string
  /** HTTP client for the target language (e.g., 'fetch', 'axios', 'curl') */
  client?: string
  /** Icon identifier for vitepress-plugin-group-icons */
  icon?: string
  /** Shiki language for syntax highlighting (defaults to 'plain') */
  highlighter?: string
}

export type PartialUseThemeConfig = Partial<UnwrapNestedRefs<UseThemeConfig>>

type GeneratorFunction = (langConfig: LanguageConfig, request: OARequest) => Promise<string>

type GetServersFunction = ({ method, path, operation }: { method: string, path: string, operation: ParsedOperation }) => string[] | null

export const DEFAULT_OPERATION_SLOTS: OperationSlot[] = [
  'header',
  'path',
  'description',
  'security',
  'parameters',
  'request-body',
  'responses',
  'playground',
  'code-samples',
  'branding',
  'footer',
]

export const DEFAULT_BASE_URL = 'http://localhost'
export const DEFAULT_STORAGE_PREFIX = '--oa'
export const DEFAULT_STORAGE_PERSIST_AUTH = true

export const availableLanguages: LanguageConfig[] = [
  {
    lang: 'curl',
    label: 'cURL',
    target: 'shell',
    client: 'curl',
    highlighter: 'bash',
    icon: 'curl',
  },
  {
    lang: 'javascript',
    label: 'JavaScript',
    target: 'js',
    client: 'fetch',
    highlighter: 'javascript',
    icon: '.js',
  },
  {
    lang: 'php',
    label: 'PHP',
    target: 'php',
    client: 'curl',
    highlighter: 'php',
    icon: '.php',
  },
  {
    lang: 'python',
    label: 'Python',
    target: 'python',
    client: 'requests',
    highlighter: 'python',
    icon: '.py',
  },
]

const DEFAULT_OPERATIONS_PREFIX = '/operations/'

const defaultValues = {
  theme: {
    highlighterTheme: {
      light: vitesseLight,
      dark: vitesseDark,
    },
  },
  path: {
    showBaseURL: false,
  },
  requestBody: {
    defaultView: 'contentType' as BodyViewType,
  },
  jsonViewer: {
    deep: Number.POSITIVE_INFINITY,
    renderer: 'vue-json-pretty' as JsonRendererType,
  },
  schemaViewer: {
    deep: 1,
  },
  headingLevels: {
    h1: 1,
    h2: 2,
    h3: 3,
    h4: 4,
    h5: 5,
    h6: 6,
  },
  response: {
    responseCodeSelector: 'tabs' as ResponseCodeSelectorType,
    maxTabs: 5,
    body: {
      defaultView: 'contentType' as BodyViewType,
    },
  },
  playground: {
    jsonEditor: {
      mode: 'tree' as PlaygroundJsonEditorMode,
      mainMenuBar: false,
      navigationBar: false,
      statusBar: false,
    },
    examples: {
      behavior: 'value' as PlaygroundExampleBehavior,
      playgroundExampleBehavior: 'value' as PlaygroundExampleBehavior,
    },
  },
  security: {
    defaultScheme: null as string | null,
  },
  operation: {
    badges: ['deprecated'] as OperationBadges[],
    slots: DEFAULT_OPERATION_SLOTS,
    hiddenSlots: [] as OperationSlot[],
    cols: 2 as OperationColsType,
    defaultBaseUrl: DEFAULT_BASE_URL,
    getServers: null,
  },
  i18n: {
    locale: 'en' as Languages,
    fallbackLocale: 'en' as Languages,
    messages: locales,
    availableLocales: [
      {
        code: 'en',
        label: 'English',
      },
      {
        code: 'es',
        label: 'Español',
      },
      {
        code: 'ja',
        label: 'Japanese',
      },
      {
        code: 'pt-BR',
        label: 'Português (Brasil)',
      },
      {
        code: 'zh',
        label: '中文',
      },
    ],
  },
  spec: {
    groupByTags: true,
    collapsePaths: false,
    showPathsSummary: true,
    avoidCirculars: false,
    lazyRendering: false,
    defaultTag: 'Default',
    defaultTagDescription: '',
    wrapExamples: true,
    disableDownload: false,
  },
  codeSamples: {
    defaultLang: 'curl',
    availableLanguages,
    generator: (langConfig: LanguageConfig, request: OARequest) => generateCodeSample(langConfig, request),
    defaultHeaders: {},
  },
  linksPrefixes: {
    tags: '/tags/',
    operations: DEFAULT_OPERATIONS_PREFIX,
  },
  server: {
    allowCustomServer: false,
    getServers: null,
  },
  storage: {
    prefix: DEFAULT_STORAGE_PREFIX,
    persistAuth: DEFAULT_STORAGE_PERSIST_AUTH,
  },
  markdown: {
    operationLink: {
      linkPrefix: DEFAULT_OPERATIONS_PREFIX,
    },
    externalLinksNewTab: false,
    config: undefined,
  },
}

const themeConfig: UseThemeConfig = {
  theme: {
    highlighterTheme: {
      light: defaultValues.theme.highlighterTheme.light,
      dark: defaultValues.theme.highlighterTheme.dark,
    },
  },
  path: {
    showBaseURL: ref(defaultValues.path.showBaseURL),
  },
  requestBody: {
    defaultView: ref(defaultValues.requestBody.defaultView),
  },
  jsonViewer: {
    deep: ref(defaultValues.jsonViewer.deep),
    renderer: ref(defaultValues.jsonViewer.renderer),
  },
  schemaViewer: {
    deep: ref(defaultValues.schemaViewer.deep),
  },
  headingLevels: {
    ...defaultValues.headingLevels,
  },
  response: {
    responseCodeSelector: ref(defaultValues.response.responseCodeSelector),
    maxTabs: ref(defaultValues.response.maxTabs),
    body: {
      defaultView: ref(defaultValues.response.body.defaultView),
    },
  },
  playground: {
    jsonEditor: {
      mode: ref(defaultValues.playground.jsonEditor.mode),
      mainMenuBar: ref(defaultValues.playground.jsonEditor.mainMenuBar),
      navigationBar: ref(defaultValues.playground.jsonEditor.navigationBar),
      statusBar: ref(defaultValues.playground.jsonEditor.statusBar),
    },
    examples: {
      behavior: ref(defaultValues.playground.examples?.behavior ?? 'value'),
      playgroundExampleBehavior: ref(defaultValues.playground.examples?.playgroundExampleBehavior ?? 'value'),
    },
  },
  security: {
    defaultScheme: ref(defaultValues.security.defaultScheme),
  },
  operation: {
    badges: ref(defaultValues.operation.badges),
    slots: ref(defaultValues.operation.slots),
    hiddenSlots: ref(defaultValues.operation.hiddenSlots),
    cols: ref(defaultValues.operation.cols),
    defaultBaseUrl: defaultValues.operation.defaultBaseUrl,
    getServers: defaultValues.operation.getServers,
  },
  i18n: {
    locale: ref(defaultValues.i18n.locale),
    fallbackLocale: ref(defaultValues.i18n.fallbackLocale),
    messages: defaultValues.i18n.messages,
    availableLocales: defaultValues.i18n.availableLocales,
  },
  spec: {
    groupByTags: ref(defaultValues.spec.groupByTags),
    collapsePaths: ref(defaultValues.spec.collapsePaths),
    showPathsSummary: ref(defaultValues.spec.showPathsSummary),
    avoidCirculars: ref(defaultValues.spec.avoidCirculars),
    lazyRendering: ref(defaultValues.spec.lazyRendering),
    defaultTag: defaultValues.spec.defaultTag,
    defaultTagDescription: defaultValues.spec.defaultTagDescription,
    wrapExamples: defaultValues.spec.wrapExamples,
    disableDownload: ref(defaultValues.spec.disableDownload),
  },
  codeSamples: {
    defaultLang: defaultValues.codeSamples.defaultLang,
    availableLanguages: defaultValues.codeSamples.availableLanguages,
    generator: defaultValues.codeSamples.generator,
    defaultHeaders: defaultValues.codeSamples.defaultHeaders,
  },
  linksPrefixes: {
    tags: defaultValues.linksPrefixes.tags,
    operations: defaultValues.linksPrefixes.operations,
  },
  server: {
    allowCustomServer: defaultValues.server.allowCustomServer,
    getServers: defaultValues.server.getServers,
  },
  storage: {
    prefix: defaultValues.storage.prefix,
    persistAuth: defaultValues.storage.persistAuth,
  },
  markdown: {
    operationLink: {
      linkPrefix: defaultValues.markdown.operationLink.linkPrefix,
    },
    externalLinksNewTab: defaultValues.markdown.externalLinksNewTab,
    config: defaultValues.markdown.config,
  },
}

const isDark = useDark({
  storageKey: 'vitepress-theme-appearance',
})

export function useTheme(initialConfig: PartialUseThemeConfig = {}) {
  setConfig(initialConfig)

  function setConfig(config: PartialUseThemeConfig) {
    if (!config || !Object.keys(config).length) {
      return
    }

    // Theme and highlighter
    if (config.theme?.highlighterTheme) {
      setHighlighterTheme(config.theme.highlighterTheme)
    }

    // Path
    if (config.path?.showBaseURL !== undefined) {
      ensureRefProperty(themeConfig, 'path', 'showBaseURL', config.path.showBaseURL)
    }

    // Request body
    if (config.requestBody?.defaultView !== undefined) {
      ensureRefProperty(themeConfig, 'requestBody', 'defaultView', config.requestBody.defaultView)
    }

    // JSON viewer
    if (config.jsonViewer?.deep !== undefined) {
      ensureRefProperty(themeConfig, 'jsonViewer', 'deep', config.jsonViewer.deep)
    }
    if (config.jsonViewer?.renderer !== undefined) {
      ensureRefProperty(themeConfig, 'jsonViewer', 'renderer', config.jsonViewer.renderer)
    }

    // Schema viewer
    if (config.schemaViewer?.deep !== undefined) {
      ensureRefProperty(themeConfig, 'schemaViewer', 'deep', config.schemaViewer.deep)
    }

    // Heading levels
    if (config.headingLevels !== undefined) {
      setHeadingLevels(config.headingLevels)
    }

    // Response
    if (config.response?.responseCodeSelector !== undefined) {
      ensureRefProperty(themeConfig, 'response', 'responseCodeSelector', config.response.responseCodeSelector)
    }
    if (config.response?.maxTabs !== undefined) {
      ensureRefProperty(themeConfig, 'response', 'maxTabs', config.response.maxTabs)
    }
    if (config.response?.body?.defaultView !== undefined) {
      ensureNestedRefProperty(themeConfig, ['response', 'body'], 'defaultView', config.response.body.defaultView)
    }

    // Playground
    if (config.playground?.jsonEditor?.mode !== undefined) {
      ensureNestedRefProperty(themeConfig, ['playground', 'jsonEditor'], 'mode', config.playground.jsonEditor.mode)
    }
    if (config.playground?.jsonEditor?.mainMenuBar !== undefined) {
      ensureNestedRefProperty(themeConfig, ['playground', 'jsonEditor'], 'mainMenuBar', config.playground.jsonEditor.mainMenuBar)
    }
    if (config.playground?.jsonEditor?.navigationBar !== undefined) {
      ensureNestedRefProperty(themeConfig, ['playground', 'jsonEditor'], 'navigationBar', config.playground.jsonEditor.navigationBar)
    }
    if (config.playground?.jsonEditor?.statusBar !== undefined) {
      ensureNestedRefProperty(themeConfig, ['playground', 'jsonEditor'], 'statusBar', config.playground.jsonEditor.statusBar)
    }
    if (config.playground?.examples?.behavior !== undefined) {
      ensureNestedRefProperty(themeConfig, ['playground', 'examples'], 'behavior', config.playground.examples.behavior)
    }
    if (config.playground?.examples?.playgroundExampleBehavior !== undefined) {
      ensureNestedRefProperty(themeConfig, ['playground', 'examples'], 'playgroundExampleBehavior', config.playground.examples.playgroundExampleBehavior)
    }

    // Security
    if (config.security?.defaultScheme !== undefined) {
      ensureRefProperty(themeConfig, 'security', 'defaultScheme', config.security.defaultScheme)
    }

    // Operation
    if (config.operation?.badges !== undefined) {
      ensureRefProperty(themeConfig, 'operation', 'badges', config.operation.badges)
    }
    if (config.operation?.slots !== undefined) {
      ensureRefProperty(themeConfig, 'operation', 'slots', config.operation.slots)
    }
    if (config.operation?.hiddenSlots !== undefined) {
      ensureRefProperty(themeConfig, 'operation', 'hiddenSlots', config.operation.hiddenSlots)
    }
    if (config.operation?.cols !== undefined) {
      ensureRefProperty(themeConfig, 'operation', 'cols', config.operation.cols)
    }
    if (config.operation?.defaultBaseUrl !== undefined) {
      setOperationDefaultBaseUrl(config.operation.defaultBaseUrl)
    }
    if (config.operation?.getServers !== undefined) {
      setOperationServers(config.operation.getServers)
    }

    // i18n
    if (config.i18n !== undefined) {
      setI18nConfig(config.i18n)
    }

    // Spec
    if (config.spec !== undefined) {
      setSpecConfig(config.spec)
    }

    // Code samples
    if (config.codeSamples !== undefined) {
      setCodeSamplesConfig(config.codeSamples)
    }

    // Links prefixes
    if (config.linksPrefixes !== undefined) {
      setLinksPrefixesConfig(config.linksPrefixes)
    }

    // Server
    if (config.server !== undefined) {
      setServerConfig(config.server)
    }

    // Storage
    if (config.storage !== undefined) {
      setStorageConfig(config.storage)
    }

    // Markdown
    if (config.markdown !== undefined) {
      setMarkdownConfig(config.markdown)
    }
  }

  function reset() {
    setConfig(defaultValues)
  }

  function getState() {
    return deepUnref(themeConfig)
  }

  function getLocale(): Languages {
    return themeConfig?.i18n?.locale?.value || 'en'
  }

  /**
   * @deprecated Use `setI18nConfig({ locale: value })` instead.
   */
  function setLocale(value: Languages) {
    console.warn('`setLocale` is deprecated. Use `setI18nConfig({ locale: value })` instead.')
    // @ts-expect-error: This is a valid expression.
    themeConfig.i18n.locale.value = value
  }

  function getHighlighterTheme() {
    return themeConfig?.theme?.highlighterTheme
  }

  function setHighlighterTheme(value: ThemeConfig['highlighterTheme']) {
    // @ts-expect-error: This is a valid expression.
    themeConfig.theme.highlighterTheme = {
      ...themeConfig?.theme?.highlighterTheme,
      ...value,
    }
  }

  function getRequestBodyDefaultView(): BodyViewType | undefined {
    return themeConfig?.requestBody?.defaultView?.value
  }

  function setRequestBodyDefaultView(value: BodyViewType) {
    ensureRefProperty(themeConfig, 'requestBody', 'defaultView', value)
  }

  function getShowBaseURL(): boolean | undefined {
    return themeConfig?.path?.showBaseURL?.value
  }

  function setShowBaseURL(value: boolean) {
    ensureRefProperty(themeConfig, 'path', 'showBaseURL', value)
  }

  function getJsonViewerDeep(): number | undefined {
    return themeConfig?.jsonViewer?.deep?.value
  }

  function setJsonViewerDeep(value: number) {
    ensureRefProperty(themeConfig, 'jsonViewer', 'deep', value)
  }

  function getJsonViewerRenderer(): JsonRendererType {
    return themeConfig?.jsonViewer?.renderer?.value || 'vue-json-pretty'
  }

  function setJsonViewerRenderer(value: JsonRendererType) {
    ensureRefProperty(themeConfig, 'jsonViewer', 'renderer', value)
  }

  function getSchemaViewerDeep(): number | undefined {
    return themeConfig?.schemaViewer?.deep?.value
  }

  function setSchemaViewerDeep(value: number) {
    ensureRefProperty(themeConfig, 'schemaViewer', 'deep', value)
  }

  function getHeadingLevels() {
    return themeConfig.headingLevels
  }

  function getHeadingLevel(level: keyof HeadingLevels): `h${1 | 2 | 3 | 4 | 5 | 6}` {
    if (!themeConfig.headingLevels) {
      return `h${level}` as 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'
    }

    const headingLevel = themeConfig.headingLevels[level] as number

    if (headingLevel < 1 || headingLevel > 6) {
      throw new Error(`Heading level for ${level} must be between 1 and 6.`)
    }

    return `h${headingLevel}` as 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'
  }

  function setHeadingLevels(levels: Partial<UnwrapNestedRefs<HeadingLevels>>) {
    if (!themeConfig.headingLevels) {
      themeConfig.headingLevels = { }
    }

    for (const key of Object.keys(levels)) {
      const value = levels[key as keyof HeadingLevels] as number

      if (value < 1 || value > 6) {
        throw new Error(`Heading level for ${key} must be between 1 and 6.`)
      }
    }

    Object.assign(themeConfig.headingLevels, levels)
  }

  function getResponseCodeSelector(): ResponseCodeSelectorType | undefined {
    return themeConfig?.response?.responseCodeSelector?.value
  }

  function setResponseCodeSelector(value: ResponseCodeSelectorType) {
    ensureRefProperty(themeConfig, 'response', 'responseCodeSelector', value)
  }

  function getResponseCodeMaxTabs(): number | undefined {
    return themeConfig?.response?.maxTabs?.value
  }

  function setResponseCodeMaxTabs(value: number) {
    ensureRefProperty(themeConfig, 'response', 'maxTabs', value)
  }

  function getResponseBodyDefaultView(): BodyViewType | undefined {
    return themeConfig?.response?.body?.defaultView?.value
  }

  function setResponseBodyDefaultView(value: BodyViewType) {
    ensureNestedRefProperty(themeConfig, ['response', 'body'], 'defaultView', value)
  }

  function getPlaygroundJsonEditorMode(): PlaygroundJsonEditorMode | undefined {
    return themeConfig?.playground?.jsonEditor?.mode?.value
  }

  function setPlaygroundJsonEditorMode(value: PlaygroundJsonEditorMode) {
    ensureNestedRefProperty(themeConfig, ['playground', 'jsonEditor'], 'mode', value)
  }

  function getPlaygroundJsonEditorMainMenuBar(): boolean | undefined {
    return themeConfig?.playground?.jsonEditor?.mainMenuBar?.value
  }

  function setPlaygroundJsonEditorMainMenuBar(value: boolean) {
    ensureNestedRefProperty(themeConfig, ['playground', 'jsonEditor'], 'mainMenuBar', value)
  }

  function getPlaygroundJsonEditorNavigationBar(): boolean | undefined {
    return themeConfig?.playground?.jsonEditor?.navigationBar?.value
  }

  function setPlaygroundJsonEditorNavigationBar(value: boolean) {
    ensureNestedRefProperty(themeConfig, ['playground', 'jsonEditor'], 'navigationBar', value)
  }

  function getPlaygroundJsonEditorStatusBar(): boolean | undefined {
    return themeConfig?.playground?.jsonEditor?.statusBar?.value
  }

  function setPlaygroundJsonEditorStatusBar(value: boolean) {
    ensureNestedRefProperty(themeConfig, ['playground', 'jsonEditor'], 'statusBar', value)
  }

  function getPlaygroundExamplesBehavior(): PlaygroundExampleBehavior {
    return themeConfig?.playground?.examples?.behavior?.value ?? 'value'
  }

  function setPlaygroundExamplesBehavior(value: PlaygroundExampleBehavior) {
    ensureNestedRefProperty(themeConfig, ['playground', 'examples'], 'behavior', value)
  }

  function getPlaygroundXExampleBehavior(): PlaygroundExampleBehavior {
    return themeConfig?.playground?.examples?.playgroundExampleBehavior?.value ?? 'value'
  }

  function setPlaygroundXExampleBehavior(value: PlaygroundExampleBehavior) {
    ensureNestedRefProperty(themeConfig, ['playground', 'examples'], 'playgroundExampleBehavior', value)
  }

  function getSecurityDefaultScheme(): string | null | undefined {
    return themeConfig?.security?.defaultScheme?.value
  }

  function setSecurityDefaultScheme(value: string | null) {
    ensureRefProperty(themeConfig, 'security', 'defaultScheme', value)
  }

  function getOperationBadges(): OperationBadges[] {
    return [...(themeConfig?.operation?.badges?.value || [])]
  }

  function setOperationBadges(value: OperationBadges[]) {
    ensureRefProperty(themeConfig, 'operation', 'badges', value)
  }

  function getOperationSlots(): OperationSlot[] | undefined {
    return themeConfig?.operation?.slots?.value
  }

  function setOperationSlots(value: OperationSlot[]) {
    ensureRefProperty(themeConfig, 'operation', 'slots', value)
  }

  function getOperationHiddenSlots(): OperationSlot[] | undefined {
    return themeConfig?.operation?.hiddenSlots?.value
  }

  function setOperationHiddenSlots(value: OperationSlot[]) {
    ensureRefProperty(themeConfig, 'operation', 'hiddenSlots', value)
  }

  function getOperationCols(): OperationColsType | undefined {
    return themeConfig?.operation?.cols?.value
  }

  function setOperationCols(value: OperationColsType) {
    ensureRefProperty(themeConfig, 'operation', 'cols', value)
  }

  function getOperationDefaultBaseUrl(): string {
    return themeConfig?.operation?.defaultBaseUrl || DEFAULT_BASE_URL
  }

  function setOperationDefaultBaseUrl(value: string) {
    const operation = ensureNestedProperty(themeConfig, 'operation')
    operation.defaultBaseUrl = value
  }

  function getOperationServers(): GetServersFunction | null {
    if (themeConfig?.operation?.getServers) {
      console.warn('operation.getServers is deprecated. Use server.getServers instead.')
      return themeConfig.operation.getServers
    }

    return themeConfig?.server?.getServers || null
  }

  function setOperationServers(value: GetServersFunction | null) {
    const operation = ensureNestedProperty(themeConfig, 'operation')
    operation.getServers = value
  }

  function getI18nConfig(): I18nConfig {
    return themeConfig.i18n as I18nConfig
  }

  function setI18nConfig(config: Partial<UnwrapNestedRefs<I18nConfig>>) {
    const i18n = ensureNestedProperty(themeConfig, 'i18n')

    if (config.locale) {
      ensureRefProperty(themeConfig, 'i18n', 'locale', config.locale)
    }

    if (config.fallbackLocale) {
      ensureRefProperty(themeConfig, 'i18n', 'fallbackLocale', config.fallbackLocale)
    }

    if (config.messages) {
      i18n.messages = config.messages
    }

    if (config.availableLocales) {
      i18n.availableLocales = config.availableLocales
    }
  }

  function getSpecConfig() {
    return themeConfig.spec
  }

  function getWrapExamples() {
    return themeConfig?.spec?.wrapExamples
  }

  function getSpecDisableDownload(): boolean | undefined {
    return themeConfig?.spec?.disableDownload?.value
  }

  function setSpecConfig(config: Partial<UnwrapNestedRefs<SpecConfig>>) {
    const spec = ensureNestedProperty(themeConfig, 'spec')

    if (config.groupByTags !== undefined) {
      ensureRefProperty(themeConfig, 'spec', 'groupByTags', config.groupByTags)
    }

    if (config.collapsePaths !== undefined) {
      ensureRefProperty(themeConfig, 'spec', 'collapsePaths', config.collapsePaths)
    }

    if (config.showPathsSummary !== undefined) {
      ensureRefProperty(themeConfig, 'spec', 'showPathsSummary', config.showPathsSummary)
    }

    if (config.avoidCirculars !== undefined) {
      ensureRefProperty(themeConfig, 'spec', 'avoidCirculars', config.avoidCirculars)
    }

    if (config.lazyRendering !== undefined) {
      ensureRefProperty(themeConfig, 'spec', 'lazyRendering', config.lazyRendering)
    }

    if (config.defaultTag !== undefined) {
      spec.defaultTag = config.defaultTag
    }

    if (config.defaultTagDescription !== undefined) {
      spec.defaultTagDescription = config.defaultTagDescription
    }

    if (config.wrapExamples !== undefined) {
      spec.wrapExamples = config.wrapExamples
    }

    if (config.disableDownload !== undefined) {
      ensureRefProperty(themeConfig, 'spec', 'disableDownload', config.disableDownload)
    }
  }

  function getCodeSamplesDefaultLang() {
    const availableLanguages = getCodeSamplesAvailableLanguages()
    const codeSamples = ensureNestedProperty(themeConfig, 'codeSamples')
    const availableLangs = availableLanguages.map(l => l.lang)

    if (codeSamples.defaultLang && availableLangs.includes(codeSamples.defaultLang)) {
      return codeSamples.defaultLang
    }

    return availableLangs[0]
  }

  function getCodeSamplesAvailableLanguages(filter: Array<string> = []) {
    const codeSamples = ensureNestedProperty(themeConfig, 'codeSamples')
    if (filter.length) {
      return codeSamples.availableLanguages?.filter(lang => filter.includes(lang.lang)) || []
    }
    return codeSamples.availableLanguages || []
  }

  function getCodeSamplesGenerator() {
    const codeSamples = ensureNestedProperty(themeConfig, 'codeSamples')
    if (!codeSamples.generator) {
      codeSamples.generator = defaultValues.codeSamples.generator
    }
    return codeSamples.generator
  }

  function getCodeSamplesDefaultHeaders() {
    const codeSamples = ensureNestedProperty(themeConfig, 'codeSamples')
    if (!codeSamples.defaultHeaders) {
      codeSamples.defaultHeaders = defaultValues.codeSamples.defaultHeaders
    }
    return codeSamples.defaultHeaders
  }

  function setCodeSamplesConfig(config: Partial<UnwrapNestedRefs<CodeSamplesConfig>>) {
    const codeSamples = ensureNestedProperty(themeConfig, 'codeSamples')

    if (config.defaultLang) {
      codeSamples.defaultLang = config.defaultLang
    }

    if (config.availableLanguages) {
      setCodeSamplesAvailableLanguages(config.availableLanguages)
    }

    if (config.generator) {
      codeSamples.generator = config.generator
    }

    if (config.defaultHeaders) {
      codeSamples.defaultHeaders = config.defaultHeaders
    }
  }

  function setCodeSamplesAvailableLanguages(languages: LanguageConfig[]) {
    const codeSamples = ensureNestedProperty(themeConfig, 'codeSamples')
    codeSamples.availableLanguages = languages
  }

  function getLinksPrefixesConfig() {
    return themeConfig.linksPrefixes
  }

  function setLinksPrefixesConfig(config: Partial<UnwrapNestedRefs<LinksPrefixesConfig>>) {
    const linksPrefixes = ensureNestedProperty(themeConfig, 'linksPrefixes')

    if (config.tags) {
      linksPrefixes.tags = config.tags
    }

    if (config.operations) {
      linksPrefixes.operations = config.operations
    }
  }

  function getTagsLinkPrefix() {
    return themeConfig?.linksPrefixes?.tags
  }

  function getOperationsLinkPrefix() {
    return themeConfig?.linksPrefixes?.operations
  }

  function getServerConfig(): ServerConfig {
    return themeConfig.server as ServerConfig
  }

  function setServerConfig(config: Partial<UnwrapNestedRefs<ServerConfig>>) {
    const server = ensureNestedProperty(themeConfig, 'server')

    if (config.allowCustomServer !== undefined) {
      server.allowCustomServer = config.allowCustomServer
    }
    if (config.getServers !== undefined) {
      server.getServers = config.getServers
    }
  }

  function getServerAllowCustomServer(): boolean {
    return themeConfig.server?.allowCustomServer || false
  }

  function getMarkdownConfig(): MarkdownConfig {
    return themeConfig.markdown as MarkdownConfig
  }

  function getExternalLinksNewTab(): boolean {
    return themeConfig.markdown?.externalLinksNewTab ?? false
  }

  function setMarkdownConfig(config: Partial<UnwrapNestedRefs<MarkdownConfig>>) {
    const markdown = ensureNestedProperty(themeConfig, 'markdown')

    if (config.operationLink !== undefined) {
      if (config.operationLink === false) {
        markdown.operationLink = false
      } else {
        const operationLink = ensureNestedProperty(markdown, 'operationLink')

        if (config.operationLink.linkPrefix !== undefined) {
          operationLink.linkPrefix = config.operationLink.linkPrefix
        }

        if (config.operationLink.transformHref !== undefined) {
          operationLink.transformHref = config.operationLink.transformHref
        }
      }
    }

    if (config.externalLinksNewTab !== undefined) {
      markdown.externalLinksNewTab = config.externalLinksNewTab
    }

    if (config.config !== undefined) {
      markdown.config = config.config
    }
  }

  function getOperationLinkConfig(): OperationLinkConfig | false | undefined {
    return themeConfig.markdown?.operationLink
  }

  function getStoragePrefix(): string {
    return themeConfig?.storage?.prefix ?? DEFAULT_STORAGE_PREFIX
  }

  function setStorageConfig(config: Partial<StorageConfig>) {
    const storage = ensureNestedProperty(themeConfig, 'storage')
    if (config.prefix !== undefined) {
      storage.prefix = config.prefix
    }
    if (config.persistAuth !== undefined) {
      storage.persistAuth = config.persistAuth
    }
  }

  function getStoragePersistAuth(): boolean {
    return themeConfig?.storage?.persistAuth ?? DEFAULT_STORAGE_PERSIST_AUTH
  }

  return {
    isDark,
    schemaConfig: themeConfig.requestBody,
    reset,
    getState,
    getLocale,
    setLocale,
    getHighlighterTheme,
    /** @deprecated Use `getRequestBodyDefaultView` instead. */
    getSchemaDefaultView: getRequestBodyDefaultView,
    getRequestBodyDefaultView,
    /** @deprecated Use `setRequestBodyDefaultView` instead. */
    setSchemaDefaultView: setRequestBodyDefaultView,
    setRequestBodyDefaultView,
    getShowBaseURL,
    setShowBaseURL,
    getJsonViewerDeep,
    setJsonViewerDeep,
    getJsonViewerRenderer,
    setJsonViewerRenderer,
    getSchemaViewerDeep,
    setSchemaViewerDeep,
    getHeadingLevels,
    getHeadingLevel,
    setHeadingLevels,
    getResponseCodeSelector,
    setResponseCodeSelector,
    getResponseCodeMaxTabs,
    setResponseCodeMaxTabs,
    getResponseBodyDefaultView,
    setResponseBodyDefaultView,
    getPlaygroundJsonEditorMode,
    setPlaygroundJsonEditorMode,
    getPlaygroundJsonEditorMainMenuBar,
    setPlaygroundJsonEditorMainMenuBar,
    getPlaygroundJsonEditorNavigationBar,
    setPlaygroundJsonEditorNavigationBar,
    getPlaygroundJsonEditorStatusBar,
    setPlaygroundJsonEditorStatusBar,
    getPlaygroundExamplesBehavior,
    setPlaygroundExamplesBehavior,
    getPlaygroundXExampleBehavior,
    setPlaygroundXExampleBehavior,
    getSecurityDefaultScheme,
    setSecurityDefaultScheme,
    getOperationBadges,
    setOperationBadges,
    getOperationSlots,
    setOperationSlots,
    getOperationHiddenSlots,
    setOperationHiddenSlots,
    getOperationCols,
    setOperationCols,
    getOperationDefaultBaseUrl,
    getOperationServers,
    getI18nConfig,
    setI18nConfig,
    getSpecConfig,
    getWrapExamples,
    getSpecDisableDownload,
    setSpecConfig,
    getCodeSamplesDefaultLang,
    getCodeSamplesAvailableLanguages,
    getCodeSamplesGenerator,
    getCodeSamplesDefaultHeaders,
    setCodeSamplesConfig,
    getLinksPrefixesConfig,
    setLinksPrefixesConfig,
    getTagsLinkPrefix,
    getOperationsLinkPrefix,
    getServerConfig,
    setServerConfig,
    getServerAllowCustomServer,
    getMarkdownConfig,
    getExternalLinksNewTab,
    setMarkdownConfig,
    getOperationLinkConfig,
    getStoragePrefix,
    getStoragePersistAuth,
    setStorageConfig,
  }
}
