import type { AgentConfig } from '@/models/debug'
import { act, fireEvent, render, screen } from '@testing-library/react'
import { MAX_ITERATIONS_NUM } from '@/config'
import { AgentSetting } from '../index'

vi.mock('@langgenius/dify-ui/slider', () => ({
  Slider: (props: {
    className?: string
    min?: number
    max?: number
    value: number
    onValueChange: (value: number) => void
  }) => (
    <input
      type="range"
      className={`slider ${props.className ?? ''}`}
      min={props.min}
      max={props.max}
      value={props.value}
      onChange={(e) => props.onValueChange(Number(e.target.value))}
    />
  ),
}))

const basePayload = {
  enabled: true,
  strategy: 'react',
  max_iteration: 5,
  tools: [],
}

const renderModal = (props?: Partial<React.ComponentProps<typeof AgentSetting>>) => {
  const onCancel = vi.fn()
  const onSave = vi.fn()
  const utils = render(
    <AgentSetting
      isChatModel
      payload={basePayload as AgentConfig}
      isFunctionCall={false}
      onCancel={onCancel}
      onSave={onSave}
      {...props}
    />,
  )
  return { ...utils, onCancel, onSave }
}

describe('AgentSetting', () => {
  it('should render agent mode description and default prompt section when not function call', () => {
    renderModal()

    expect(screen.getByText('appDebug.agent.agentMode')).toBeInTheDocument()
    expect(screen.getByText('appDebug.agent.agentModeType.ReACT')).toBeInTheDocument()
    expect(screen.getByText('tools.builtInPromptTitle')).toBeInTheDocument()
  })

  it('should display function call mode when isFunctionCall true', () => {
    renderModal({ isFunctionCall: true })

    expect(screen.getByText('appDebug.agent.agentModeType.functionCall')).toBeInTheDocument()
    expect(screen.queryByText('tools.builtInPromptTitle')).not.toBeInTheDocument()
  })

  it('should update iteration via slider and number input', () => {
    renderModal()
    const slider = screen.getByRole('slider')
    const numberInput = screen.getByRole('spinbutton')

    fireEvent.change(slider, { target: { value: '7' } })
    expect(screen.getAllByDisplayValue('7')).toHaveLength(2)

    fireEvent.change(numberInput, { target: { value: '2' } })
    expect(screen.getAllByDisplayValue('2')).toHaveLength(2)
  })

  it('should clamp iteration value within min/max range', () => {
    renderModal()

    const numberInput = screen.getByRole('spinbutton')

    fireEvent.change(numberInput, { target: { value: '0' } })
    expect(screen.getAllByDisplayValue('1')).toHaveLength(2)

    fireEvent.change(numberInput, { target: { value: '999' } })
    expect(screen.getAllByDisplayValue(String(MAX_ITERATIONS_NUM))).toHaveLength(2)
  })

  it('should call onCancel when cancel button clicked', () => {
    const { onCancel } = renderModal()
    fireEvent.click(screen.getByRole('button', { name: 'common.operation.cancel' }))
    expect(onCancel).toHaveBeenCalled()
  })

  it('should call onCancel when Escape is pressed', () => {
    const { onCancel } = renderModal()

    fireEvent.keyDown(document, { key: 'Escape' })

    expect(onCancel).toHaveBeenCalledTimes(1)
  })

  it('should call onCancel when the backdrop is clicked', () => {
    const { onCancel } = renderModal()
    const dialog = screen.getByRole('dialog')

    fireEvent.pointerDown(document.body, { target: document.body })
    fireEvent.mouseDown(document.body, { target: document.body })
    fireEvent.click(document.body, { target: document.body })

    expect(dialog).toBeInTheDocument()
    expect(onCancel).toHaveBeenCalledTimes(1)
  })

  it('should call onCancel when close button clicked', () => {
    const { onCancel } = renderModal()

    fireEvent.click(screen.getByRole('button', { name: 'common.operation.close' }))

    expect(onCancel).toHaveBeenCalledTimes(1)
  })

  it('should call onSave with updated payload', async () => {
    const { onSave } = renderModal()
    const numberInput = screen.getByRole('spinbutton')
    fireEvent.change(numberInput, { target: { value: '6' } })

    await act(async () => {
      fireEvent.click(screen.getByRole('button', { name: 'common.operation.save' }))
    })

    expect(onSave).toHaveBeenCalledWith(expect.objectContaining({ max_iteration: 6 }))
  })
})
