import type { Model, ModelItem } from '../../declarations'
import { fireEvent, render, screen } from '@testing-library/react'
import { tooltipManager } from '@/app/components/base/tooltip/TooltipManager'
import {
  ConfigurationMethodEnum,
  ModelFeatureEnum,
  ModelStatusEnum,
  ModelTypeEnum,
} from '../../declarations'
import Popup from '../popup'

let mockLanguage = 'en_US'

const mockSetShowAccountSettingModal = vi.hoisted(() => vi.fn())
vi.mock('@/context/modal-context', () => ({
  useModalContext: () => ({
    setShowAccountSettingModal: mockSetShowAccountSettingModal,
  }),
}))

const mockSupportFunctionCall = vi.hoisted(() => vi.fn())
vi.mock('@/utils/tool-call', () => ({
  supportFunctionCall: mockSupportFunctionCall,
}))

vi.mock('../../hooks', async () => {
  const actual = await vi.importActual<typeof import('../../hooks')>('../../hooks')
  return {
    ...actual,
    useLanguage: () => mockLanguage,
  }
})

vi.mock('../popup-item', () => ({
  default: ({ model }: { model: Model }) => <div>{model.provider}</div>,
}))

const makeModelItem = (overrides: Partial<ModelItem> = {}): ModelItem => ({
  model: 'gpt-4',
  label: { en_US: 'GPT-4', zh_Hans: 'GPT-4' },
  model_type: ModelTypeEnum.textGeneration,
  fetch_from: ConfigurationMethodEnum.predefinedModel,
  status: ModelStatusEnum.active,
  model_properties: {},
  load_balancing_enabled: false,
  ...overrides,
})

const makeModel = (overrides: Partial<Model> = {}): Model => ({
  provider: 'openai',
  icon_small: { en_US: '', zh_Hans: '' },
  label: { en_US: 'OpenAI', zh_Hans: 'OpenAI' },
  models: [makeModelItem()],
  status: ModelStatusEnum.active,
  ...overrides,
})

describe('Popup', () => {
  let closeActiveTooltipSpy: ReturnType<typeof vi.spyOn>

  beforeEach(() => {
    vi.clearAllMocks()
    mockLanguage = 'en_US'
    mockSupportFunctionCall.mockReturnValue(true)
    closeActiveTooltipSpy = vi.spyOn(tooltipManager, 'closeActiveTooltip')
  })

  it('should filter models by search and allow clearing search', () => {
    render(
      <Popup
        modelList={[makeModel()]}
        onSelect={vi.fn()}
        onHide={vi.fn()}
      />,
    )

    expect(screen.getByText('openai')).toBeInTheDocument()

    const input = screen.getByPlaceholderText('datasetSettings.form.searchModel')
    fireEvent.change(input, { target: { value: 'not-found' } })
    expect(screen.getByText('No model found for “not-found”')).toBeInTheDocument()

    fireEvent.change(input, { target: { value: '' } })
    expect((input as HTMLInputElement).value).toBe('')
    expect(screen.getByText('openai')).toBeInTheDocument()
  })

  it('should filter by scope features including toolCall and non-toolCall checks', () => {
    const modelList = [
      makeModel({ models: [makeModelItem({ features: [ModelFeatureEnum.toolCall, ModelFeatureEnum.vision] })] }),
    ]

    // When tool-call support is missing, it should be filtered out.
    mockSupportFunctionCall.mockReturnValue(false)
    const { unmount } = render(
      <Popup
        modelList={modelList}
        onSelect={vi.fn()}
        onHide={vi.fn()}
        scopeFeatures={[ModelFeatureEnum.toolCall, ModelFeatureEnum.vision]}
      />,
    )
    expect(screen.getByText('No model found for “”')).toBeInTheDocument()

    // When tool-call support exists, the non-toolCall feature check should also pass.
    unmount()
    mockSupportFunctionCall.mockReturnValue(true)
    const { unmount: unmount2 } = render(
      <Popup
        modelList={modelList}
        onSelect={vi.fn()}
        onHide={vi.fn()}
        scopeFeatures={[ModelFeatureEnum.toolCall, ModelFeatureEnum.vision]}
      />,
    )
    expect(screen.getByText('openai')).toBeInTheDocument()

    unmount2()
    const { unmount: unmount3 } = render(
      <Popup
        modelList={modelList}
        onSelect={vi.fn()}
        onHide={vi.fn()}
        scopeFeatures={[ModelFeatureEnum.vision]}
      />,
    )
    expect(screen.getByText('openai')).toBeInTheDocument()

    // When features are missing, non-toolCall feature checks should fail.
    unmount3()
    render(
      <Popup
        modelList={[makeModel({ models: [makeModelItem({ features: undefined })] })]}
        onSelect={vi.fn()}
        onHide={vi.fn()}
        scopeFeatures={[ModelFeatureEnum.vision]}
      />,
    )
    expect(screen.getByText('No model found for “”')).toBeInTheDocument()
  })

  it('should match labels from other languages when current language key is missing', () => {
    mockLanguage = 'fr_FR'

    render(
      <Popup
        modelList={[makeModel()]}
        onSelect={vi.fn()}
        onHide={vi.fn()}
      />,
    )

    fireEvent.change(
      screen.getByPlaceholderText('datasetSettings.form.searchModel'),
      { target: { value: 'gpt' } },
    )

    expect(screen.getByText('openai')).toBeInTheDocument()
  })

  it('should filter out model when features array exists but does not include required scopeFeature', () => {
    const modelWithToolCallOnly = makeModel({
      models: [makeModelItem({ features: [ModelFeatureEnum.toolCall] })],
    })

    render(
      <Popup
        modelList={[modelWithToolCallOnly]}
        onSelect={vi.fn()}
        onHide={vi.fn()}
        scopeFeatures={[ModelFeatureEnum.vision]}
      />,
    )

    // The model item should be filtered out because it has toolCall but not vision
    expect(screen.queryByText('openai')).not.toBeInTheDocument()
  })

  it('should close tooltip on scroll', () => {
    const { container } = render(
      <Popup
        modelList={[makeModel()]}
        onSelect={vi.fn()}
        onHide={vi.fn()}
      />,
    )

    fireEvent.scroll(container.firstElementChild as HTMLElement)
    expect(closeActiveTooltipSpy).toHaveBeenCalled()
  })

  it('should open provider settings when clicking footer link', () => {
    render(
      <Popup
        modelList={[makeModel()]}
        onSelect={vi.fn()}
        onHide={vi.fn()}
      />,
    )

    fireEvent.click(screen.getByText('common.model.settingsLink'))

    expect(mockSetShowAccountSettingModal).toHaveBeenCalledWith({
      payload: 'provider',
    })
  })

  it('should call onHide when footer settings link is clicked', () => {
    const mockOnHide = vi.fn()
    render(
      <Popup
        modelList={[makeModel()]}
        onSelect={vi.fn()}
        onHide={mockOnHide}
      />,
    )

    fireEvent.click(screen.getByText('common.model.settingsLink'))

    expect(mockOnHide).toHaveBeenCalled()
  })

  it('should match model label when searchText is non-empty and label key exists for current language', () => {
    render(
      <Popup
        modelList={[makeModel()]}
        onSelect={vi.fn()}
        onHide={vi.fn()}
      />,
    )

    // GPT-4 label has en_US key, so modelItem.label[language] is defined
    const input = screen.getByPlaceholderText('datasetSettings.form.searchModel')
    fireEvent.change(input, { target: { value: 'gpt' } })

    expect(screen.getByText('openai')).toBeInTheDocument()
  })
})
