import type { DimSdkStore } from '../src/dimsdk/core/store.ts'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { nextTick } from 'vue'
import { getLocale, setLocale } from '../src/dimsdk/i18n/index.ts'

const { clearClipboardMock } = vi.hoisted(() => ({
  clearClipboardMock: vi.fn(async () => true),
}))

vi.mock('../src/dimsdk/pages/utils/clipboard.ts', () => ({
  clearClipboard: clearClipboardMock,
}))

const {
  useLanguageDialog,
  usePluginsDialogs,
  useSettingsDialogs,
  useThemeDialog,
  useToolSettingsDialogs,
} = await import('../src/dimsdk/pages/utils/settings-dialogs.ts')

type FakeStore = {
  state: {
    paused: null
    approval: {
      active: null
      mode: 'auto' | 'normal' | 'strict'
    }
    config: {
      context_window: number
      temperature?: number
      interleaved_thinking?: boolean
      model?: string
    }
    ui: {
      locale: 'en' | 'zh-CN'
      theme: { mode: string }
      shortcuts: {
        commands?: string
        timeline?: string
        newSession?: string
      }
    }
    provider: {
      activeId: string
      connections: Record<string, { apiKey?: string, baseUrl?: string }>
      customProviders: Array<unknown>
      custom: { models: Array<{ id: string, name?: string }> }
    }
    context: {
      env: Record<string, string | undefined>
    }
    tools: {
      websearch: {
        apiKey: string
        apiEndpoint: string
        numResults: number
      }
    }
    mcp: {
      servers: []
    }
  }
  setEnvVar: ReturnType<typeof vi.fn<(key: string, value: string | undefined) => void>>
  setThemeMode: ReturnType<typeof vi.fn<(mode: string) => void>>
}

const realProcess = globalThis.process

function setProcessEnv(
  env: Record<string, string | undefined>,
  platform: NodeJS.Platform = 'linux',
): void {
  Object.defineProperty(globalThis, 'process', {
    configurable: true,
    value: {
      ...realProcess,
      env,
      platform,
    },
  })
}

function createStore(
  overrides?: Partial<FakeStore['state']>,
): FakeStore & DimSdkStore {
  const state: FakeStore['state'] = {
    paused: null,
    approval: {
      active: null,
      mode: 'normal',
    },
    config: {
      context_window: 8192,
      temperature: 0.3,
      interleaved_thinking: false,
      model: '',
    },
    ui: {
      locale: 'en',
      theme: { mode: 'dark' },
      shortcuts: {
        commands: 'meta+j',
        timeline: 'meta+h',
        newSession: 'meta+n',
      },
    },
    provider: {
      activeId: '',
      connections: {},
      customProviders: [],
      custom: { models: [] },
    },
    context: {
      env: {},
    },
    tools: {
      websearch: {
        apiKey: '',
        apiEndpoint: 'https://google.serper.dev/search?format=json',
        numResults: 10,
      },
    },
    mcp: {
      servers: [],
    },
  }

  Object.assign(state, overrides)

  const store: FakeStore = {
    state,
    setEnvVar: vi.fn((key: string, value: string | undefined) => {
      if (!value)
        delete state.context.env[key]
      else
        state.context.env[key] = value
    }),
    setThemeMode: vi.fn((mode: string) => {
      state.ui.theme.mode = mode
    }),
  }

  return store as FakeStore & DimSdkStore
}

afterEach(() => {
  clearClipboardMock.mockClear()
  setLocale('en')
  Object.defineProperty(globalThis, 'process', {
    configurable: true,
    value: realProcess,
  })
})

describe('settings dialogs ui helpers', () => {
  it('applies config fields from typed state/env without legacy fallbacks', async () => {
    const processEnv: Record<string, string | undefined> = {}
    setProcessEnv(processEnv)
    const store = createStore({
      context: {
        env: {
          DIMCODE_CONTEXT_WINDOW: '12345',
          DIMCODE_MAX_TOKENS: '777',
        },
      },
    })
    const toasts: string[] = []

    const dialog = useSettingsDialogs({
      store,
      nodeLike: true,
      showToast: (text: string) => {
        toasts.push(text)
      },
    })

    dialog.openConfig()
    await Promise.resolve()

    expect(dialog.contextWindow.value).toBe('12345')
    expect(dialog.maxTokens.value).toBe('777')

    dialog.contextWindow.value = '4567'
    dialog.maxTokens.value = '333'
    dialog.temperature.value = '0.7'
    dialog.interleavedThinking.value = true
    dialog.shortcutCommands.value = 'meta+k'
    dialog.shortcutTimeline.value = 'meta+l'
    dialog.shortcutNewSession.value = 'meta+m'

    dialog.applyConfig()
    dialog.applyToolApprovals('strict')

    expect(store.state.config.context_window).toBe(4567)
    expect(store.state.config.temperature).toBe(0.7)
    expect(store.state.config.interleaved_thinking).toBe(true)
    expect(store.state.ui.shortcuts).toEqual({
      commands: 'meta+k',
      timeline: 'meta+l',
      newSession: 'meta+m',
    })
    expect(store.state.approval.mode).toBe('strict')
    expect(store.state.context.env).toMatchObject({
      DIMCODE_CONTEXT_WINDOW: '4567',
      DIMCODE_MAX_TOKENS: '333',
      DIMCODE_TEMPERATURE: '0.7',
      DIMCODE_INTERLEAVED_THINKING: '1',
      DIMCODE_TOOL_APPROVALS: 'strict',
    })
    expect(processEnv).toMatchObject({
      DIMCODE_CONTEXT_WINDOW: '4567',
      DIMCODE_MAX_TOKENS: '333',
      DIMCODE_TEMPERATURE: '0.7',
      DIMCODE_INTERLEAVED_THINKING: '1',
      DIMCODE_TOOL_APPROVALS: 'strict',
    })
    expect(toasts.length).toBeGreaterThanOrEqual(2)
  })

  it('previews and applies locales through typed ui state', async () => {
    const processEnv: Record<string, string | undefined> = {}
    setProcessEnv(processEnv)
    const store = createStore({
      ui: {
        locale: 'en',
        theme: { mode: 'dark' },
        shortcuts: {
          commands: 'meta+j',
          timeline: 'meta+h',
          newSession: 'meta+n',
        },
      },
    })

    const dialog = useLanguageDialog({
      store,
      nodeLike: true,
      showToast: () => {},
    })

    dialog.openLanguage()
    dialog.locale.value = 'zh-CN'
    await nextTick()
    expect(getLocale()).toBe('zh-CN')

    dialog.closeLanguage()
    expect(getLocale()).toBe('en')

    dialog.openLanguage()
    dialog.locale.value = 'zh-CN'
    await nextTick()
    dialog.applyLanguage()

    expect(store.state.ui.locale).toBe('zh-CN')
    expect(store.state.context.env.DIMCODE_LANG).toBe('zh-CN')
    expect(processEnv.DIMCODE_LANG).toBe('zh-CN')
    expect(getLocale()).toBe('zh-CN')
  })

  it('previews theme changes and applies them via the store API', async () => {
    const store = createStore({
      ui: {
        locale: 'en',
        theme: { mode: 'dark' },
        shortcuts: {
          commands: 'meta+j',
          timeline: 'meta+h',
          newSession: 'meta+n',
        },
      },
    })

    const dialog = useThemeDialog({
      store,
      showToast: () => {},
    })

    dialog.openTheme()
    dialog.themeMode.value = 'monokai-light'
    await nextTick()
    expect(store.state.ui.theme.mode).toBe('monokai-light')

    dialog.closeTheme()
    expect(store.state.ui.theme.mode).toBe('dark')

    dialog.openTheme()
    dialog.themeMode.value = 'light'
    dialog.applyTheme()

    expect(store.setThemeMode).toHaveBeenCalledWith('light')
    expect(store.state.ui.theme.mode).toBe('light')
  })

  it('writes tool and plugin api keys through the typed tools state', () => {
    const store = createStore({
      tools: {
        websearch: {
          apiKey: 'sk-old',
          apiEndpoint: 'https://google.serper.dev/search?format=json',
          numResults: 10,
        },
      },
    })

    const toolDialog = useToolSettingsDialogs({
      store,
      showToast: () => {},
    })
    toolDialog.openToolSettings()
    toolDialog.apiKeyDraft.value = 'sk-tool'
    toolDialog.applyToolSettings()

    const pluginDialog = usePluginsDialogs({
      store,
      showToast: () => {},
    })
    pluginDialog.openSerperConfig()
    pluginDialog.pluginApiKeyDraft.value = 'sk-plugin'
    pluginDialog.applySerperConfig()

    expect(store.state.tools.websearch.apiKey).toBe('sk-plugin')
    expect(clearClipboardMock).toHaveBeenCalledTimes(2)
  })
})
