import type { ReactElement } from 'react'
import type { ToolWithProvider } from '../../types'
import { screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { useMarketplacePlugins } from '@/app/components/plugins/marketplace/query'
import { PluginCategoryEnum } from '@/app/components/plugins/types'
import { useGetLanguage } from '@/context/i18n'
import useTheme from '@/hooks/use-theme'
import { renderWithConsoleQuery } from '@/test/console/query-data'
import { Theme } from '@/types/app'
import { BlockEnum } from '../../types'
import DataSources from '../data-sources'

vi.mock('@/context/i18n', () => ({
  useGetLanguage: vi.fn(),
}))

vi.mock('@/hooks/use-theme', () => ({
  default: vi.fn(),
}))

vi.mock('@/app/components/plugins/marketplace/query', () => ({
  useMarketplacePlugins: vi.fn(),
}))

const mockUseGetLanguage = vi.mocked(useGetLanguage)
const mockUseTheme = vi.mocked(useTheme)
const mockUseMarketplacePlugins = vi.mocked(useMarketplacePlugins)

let enableMarketplaceForRender = false
const render = (ui: ReactElement) =>
  renderWithConsoleQuery(ui, {
    systemFeatures: { enable_marketplace: enableMarketplaceForRender },
  })

type UseMarketplacePluginsReturn = ReturnType<typeof useMarketplacePlugins>

const createToolProvider = (overrides: Partial<ToolWithProvider> = {}): ToolWithProvider => ({
  id: 'langgenius/file',
  name: 'file',
  author: 'Dify',
  description: { en_US: 'desc', zh_Hans: '描述' },
  icon: 'icon',
  label: { en_US: 'File Source', zh_Hans: '文件源' },
  type: 'local_file',
  team_credentials: {},
  is_team_authorization: false,
  allow_delete: false,
  labels: [],
  plugin_id: 'langgenius/file',
  meta: { version: '1.0.0' },
  tools: [
    {
      name: 'local-file',
      author: 'Dify',
      label: { en_US: 'Local File', zh_Hans: '本地文件' },
      description: { en_US: 'Load local files', zh_Hans: '加载本地文件' },
      parameters: [],
      labels: [],
      output_schema: {},
    },
  ],
  ...overrides,
})

const createMarketplacePluginsMock = (
  overrides: Partial<UseMarketplacePluginsReturn> = {},
): UseMarketplacePluginsReturn =>
  ({
    data: undefined,
    ...overrides,
  }) as UseMarketplacePluginsReturn

describe('DataSources', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    enableMarketplaceForRender = false
    mockUseGetLanguage.mockReturnValue('en_US')
    mockUseTheme.mockReturnValue({ theme: Theme.light } as ReturnType<typeof useTheme>)
    mockUseMarketplacePlugins.mockReturnValue(createMarketplacePluginsMock())
  })

  // Data source tools should filter by search and normalize the default value payload.
  describe('Selection', () => {
    it('should add default file extensions for the built-in local file data source', async () => {
      const user = userEvent.setup()
      const onSelect = vi.fn()

      render(<DataSources searchText="" onSelect={onSelect} dataSources={[createToolProvider()]} />)

      await user.click(screen.getByText('File Source'))
      await user.click(screen.getByText('Local File'))

      expect(onSelect).toHaveBeenCalledWith(
        BlockEnum.DataSource,
        expect.objectContaining({
          provider_name: 'file',
          datasource_name: 'local-file',
          datasource_label: 'Local File',
          fileExtensions: expect.arrayContaining(['txt', 'pdf', 'md']),
        }),
      )
    })

    it('should filter providers by search text', () => {
      render(
        <DataSources
          searchText="searchable"
          onSelect={vi.fn()}
          dataSources={[
            createToolProvider({
              id: 'searchable-provider',
              name: 'searchable-provider',
              label: { en_US: 'Searchable Source', zh_Hans: '可搜索源' },
              tools: [
                {
                  name: 'searchable-tool',
                  author: 'Dify',
                  label: { en_US: 'Searchable Tool', zh_Hans: '可搜索工具' },
                  description: { en_US: 'desc', zh_Hans: '描述' },
                  parameters: [],
                  labels: [],
                  output_schema: {},
                },
              ],
            }),
            createToolProvider({
              id: 'other-provider',
              name: 'other-provider',
              label: { en_US: 'Other Source', zh_Hans: '其他源' },
            }),
          ]}
        />,
      )

      expect(screen.getByText('Searchable Source')).toBeInTheDocument()
      expect(screen.queryByText('Other Source')).not.toBeInTheDocument()
    })
  })

  // Marketplace search should only run when enabled and a search term is present.
  describe('Marketplace Search', () => {
    it('should debounce marketplace requests while keeping datasource filtering responsive', async () => {
      enableMarketplaceForRender = true

      const onSelect = vi.fn()
      const dataSources = [createToolProvider()]
      const { rerender } = render(
        <DataSources searchText="" onSelect={onSelect} dataSources={dataSources} />,
      )

      rerender(<DataSources searchText="i" onSelect={onSelect} dataSources={dataSources} />)
      expect(
        screen.queryByRole('link', { name: /plugin\.findMoreInMarketplace/ }),
      ).not.toBeInTheDocument()
      rerender(<DataSources searchText="in" onSelect={onSelect} dataSources={dataSources} />)
      rerender(<DataSources searchText="invoice" onSelect={onSelect} dataSources={dataSources} />)

      expect(
        mockUseMarketplacePlugins.mock.calls
          .map(([params]) => params)
          .filter((params) => params?.query && params.query !== 'invoice'),
      ).toEqual([])

      await waitFor(() => {
        expect(mockUseMarketplacePlugins).toHaveBeenLastCalledWith({
          query: 'invoice',
          category: PluginCategoryEnum.datasource,
        })
      })
    })
  })
})
