import type { SearchResult } from '../../types'
import { renderWithConsoleQuery as render } from '@/test/console/query-data'
import { slashAction } from '../slash'
import { SlashCommandProvider } from '../slash-provider'

vi.mock('react-i18next', async () => {
  const { createReactI18nextLanguageMock } = await import('@/test/i18n-mock')
  return createReactI18nextLanguageMock('ja')
})

const {
  mockSetTheme,
  mockSetLocale,
  mockExecuteCommand,
  mockRegister,
  mockSearch,
  mockUnregister,
  featureFlag,
} = vi.hoisted(() => ({
  mockSetTheme: vi.fn(),
  mockSetLocale: vi.fn(),
  mockExecuteCommand: vi.fn(),
  mockRegister: vi.fn(),
  mockSearch: vi.fn(),
  mockUnregister: vi.fn(),
  // Mutable holder so each test can flip the feature-preview flag before render.
  featureFlag: { enabled: false },
}))

vi.mock('@/config', async (importOriginal) => {
  const actual = await importOriginal<typeof import('@/config')>()

  return {
    ...actual,
    get ENABLE_FEATURE_PREVIEW() {
      return featureFlag.enabled
    },
  }
})

vi.mock('next-themes', () => ({
  useTheme: () => ({
    setTheme: mockSetTheme,
  }),
}))
vi.mock('@/i18n-config', () => ({
  setLocaleOnClient: mockSetLocale,
}))

vi.mock('../command-bus', () => ({
  executeCommand: (...args: unknown[]) => mockExecuteCommand(...args),
}))

vi.mock('../registry', () => ({
  slashCommandRegistry: {
    register: (...args: unknown[]) => mockRegister(...args),
    search: (...args: unknown[]) => mockSearch(...args),
    unregister: (...args: unknown[]) => mockUnregister(...args),
  },
}))

describe('slashAction', () => {
  beforeEach(() => {
    vi.clearAllMocks()
  })

  it('should expose translated title and description', () => {
    expect(slashAction.title).toBe('gotoAnything.actions.slashTitle')
    expect(slashAction.description).toBe('gotoAnything.actions.slashDesc')
  })

  it('should execute command results and ignore non-command results', () => {
    slashAction.action?.({
      id: 'cmd-1',
      title: 'Command',
      type: 'command',
      data: {
        command: 'navigation.docs',
        args: { path: '/docs' },
      },
    } as SearchResult)

    slashAction.action?.({
      id: 'app-1',
      title: 'App',
      type: 'app',
      data: {} as never,
    } as SearchResult)

    expect(mockExecuteCommand).toHaveBeenCalledTimes(1)
    expect(mockExecuteCommand).toHaveBeenCalledWith('navigation.docs', { path: '/docs' })
  })

  it('should delegate search to the slash command registry with the active language', async () => {
    mockSearch.mockResolvedValue([
      { id: 'theme', title: '/theme', type: 'command', data: { command: 'theme' } },
    ])

    expect(slashAction.source).toBe('local')
    if (slashAction.source !== 'local') throw new Error('Expected a local slash action')
    const results = await slashAction.search('/theme dark', 'dark')

    expect(mockSearch).toHaveBeenCalledWith('/theme dark', 'ja')
    expect(results).toEqual([
      { id: 'theme', title: '/theme', type: 'command', data: { command: 'theme' } },
    ])
  })
})

describe('SlashCommandProvider', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    // Default: feature preview off, so /create and /refine are NOT registered.
    featureFlag.enabled = false
  })

  it('should not register the /create and /refine preview commands when the feature flag is off', () => {
    const { unmount } = render(<SlashCommandProvider />)

    expect(mockRegister.mock.calls.map((call) => call[0].name)).toEqual([
      'theme',
      'language',
      'forum',
      'docs',
      'community',
      'account',
      'go',
    ])
    expect(mockRegister).toHaveBeenCalledWith(expect.objectContaining({ name: 'theme' }), {
      setTheme: mockSetTheme,
    })
    expect(mockRegister).toHaveBeenCalledWith(expect.objectContaining({ name: 'language' }), {
      setLocale: mockSetLocale,
    })

    unmount()

    // Unregister is always called for the preview commands (a no-op when they
    // were never registered) so toggling the flag off mid-session stays clean.
    expect(mockUnregister.mock.calls.map((call) => call[0])).toEqual([
      'theme',
      'language',
      'forum',
      'docs',
      'community',
      'account',
      'go',
      'create',
      'refine',
    ])
  })

  it('should register the /create and /refine preview commands when the feature flag is on', () => {
    featureFlag.enabled = true

    const { unmount } = render(<SlashCommandProvider />)

    expect(mockRegister.mock.calls.map((call) => call[0].name)).toEqual([
      'theme',
      'language',
      'forum',
      'docs',
      'community',
      'account',
      'go',
      'create',
      'refine',
    ])

    unmount()

    expect(mockUnregister.mock.calls.map((call) => call[0])).toEqual([
      'theme',
      'language',
      'forum',
      'docs',
      'community',
      'account',
      'go',
      'create',
      'refine',
    ])
  })
})
