/* oxlint-disable typescript/no-explicit-any */
import type { ListFilterNodeType } from '../types'
import type { PanelProps } from '@/types/workflow'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { renderWorkflowFlowComponent } from '@/app/components/workflow/__tests__/workflow-test-env'
import { BlockEnum, VarType } from '@/app/components/workflow/types'
import ExtractInput from '../components/extract-input'
import LimitConfig from '../components/limit-config'
import SubVariablePicker from '../components/sub-variable-picker'
import Node from '../node'
import Panel from '../panel'
import { OrderBy } from '../types'
import useConfig from '../use-config'

vi.mock('@/app/components/workflow/nodes/_base/hooks/use-available-var-list', () => ({
  default: vi.fn((_nodeId: string, options?: any) => ({
    availableVars: [
      { variable: ['node-1', 'size'], type: VarType.number },
      { variable: ['node-1', 'name'], type: VarType.string },
    ].filter((varPayload) => (options?.filterVar ? options.filterVar(varPayload) : true)),
    availableNodesWithParent: [{ id: 'node-1', data: { title: 'Answer', type: BlockEnum.Answer } }],
  })),
}))

vi.mock('@/app/components/workflow/nodes/_base/components/input-support-select-var', () => ({
  default: ({ value, onChange, placeholder, className, readOnly, onFocusChange }: any) => (
    <input
      value={value}
      placeholder={placeholder}
      className={className}
      readOnly={readOnly}
      onFocus={() => onFocusChange?.(true)}
      onBlur={() => onFocusChange?.(false)}
      onChange={(event) => onChange(event.target.value)}
    />
  ),
}))

vi.mock('@/app/components/workflow/nodes/_base/components/field', () => ({
  default: ({ title, operations, children }: any) => (
    <div>
      <div>{title}</div>
      <div>{operations}</div>
      {children}
    </div>
  ),
}))

vi.mock('@/app/components/workflow/nodes/_base/components/input-number-with-slider', () => ({
  default: ({ value, onChange }: any) => (
    <button type="button" onClick={() => onChange(value + 1)}>
      slider-{value}
    </button>
  ),
}))

vi.mock('@/app/components/workflow/nodes/_base/components/option-card', () => ({
  default: ({ title, onSelect }: any) => (
    <button type="button" onClick={onSelect}>
      {title}
    </button>
  ),
}))

vi.mock('@/app/components/workflow/nodes/_base/components/output-vars', () => ({
  default: ({ children }: any) => <div>{children}</div>,
  VarItem: ({ name, type }: any) => (
    <div>
      {name}:{type}
    </div>
  ),
}))

vi.mock('@/app/components/workflow/nodes/_base/components/split', () => ({
  default: () => <div>split</div>,
}))

vi.mock('@/app/components/workflow/nodes/_base/components/variable/var-reference-picker', () => ({
  default: ({ onChange }: any) => (
    <button type="button" onClick={() => onChange(['node-1', 'items'])}>
      pick-var
    </button>
  ),
}))

vi.mock('../components/filter-condition', () => ({
  default: ({ onChange }: any) => (
    <button type="button" onClick={() => onChange({ key: 'size' })}>
      filter-condition
    </button>
  ),
}))

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

const mockUseConfig = vi.mocked(useConfig)

const createData = (overrides: Partial<ListFilterNodeType> = {}): ListFilterNodeType => ({
  title: 'List Operator',
  desc: '',
  type: BlockEnum.ListFilter,
  variable: ['node-1', 'items'],
  var_type: VarType.arrayNumber,
  item_var_type: VarType.number,
  filter_by: {
    enabled: true,
    conditions: [{ key: 'size', comparison_operator: 'equal', value: '1' }] as any,
  },
  extract_by: { enabled: true, serial: '1' },
  limit: { enabled: true, size: 10 },
  order_by: { enabled: true, key: 'size', value: OrderBy.ASC },
  ...overrides,
})

const createConfigResult = (
  overrides: Partial<ReturnType<typeof useConfig>> = {},
): ReturnType<typeof useConfig> => ({
  readOnly: false,
  inputs: createData(),
  filterVar: vi.fn(() => true),
  varType: VarType.arrayNumber,
  itemVarType: VarType.number,
  itemVarTypeShowName: 'number',
  hasSubVariable: true,
  handleVarChanges: vi.fn(),
  handleFilterEnabledChange: vi.fn(),
  handleFilterChange: vi.fn(),
  handleLimitChange: vi.fn(),
  handleOrderByEnabledChange: vi.fn(),
  handleOrderByKeyChange: vi.fn(),
  handleOrderByTypeChange: vi.fn(() => vi.fn()),
  handleExtractsEnabledChange: vi.fn(),
  handleExtractsChange: vi.fn(),
  ...overrides,
})

const panelProps: PanelProps = {
  getInputVars: vi.fn(() => []),
  toVarInputs: vi.fn(() => []),
  runInputData: {},
  runInputDataRef: { current: {} },
  setRunInputData: vi.fn(),
  runResult: null,
}

const renderPanel = (data: ListFilterNodeType = createData()) =>
  render(<Panel id="node-1" data={data} panelProps={panelProps} />)

describe('list-operator path', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    mockUseConfig.mockReturnValue(createConfigResult())
  })

  // The list-operator path should expose extract, limit, ordering, and node variable previews.
  describe('Path Integration', () => {
    it('should update the extract input', async () => {
      const onChange = vi.fn()
      const { rerender } = render(
        <ExtractInput nodeId="node-1" readOnly={false} value="1" onChange={onChange} />,
      )

      fireEvent.change(screen.getByDisplayValue('1'), { target: { value: '2' } })
      fireEvent.focus(screen.getByDisplayValue('1'))
      expect(screen.getByDisplayValue('1')).toHaveClass('border-components-input-border-active')

      rerender(<ExtractInput nodeId="node-1" readOnly value="" onChange={onChange} />)

      expect(onChange).toHaveBeenCalled()
      expect(screen.getByRole('textbox')).toHaveAttribute('placeholder', '')
    })

    it('should change the selected sub variable', async () => {
      const user = userEvent.setup()
      const onChange = vi.fn()
      const { unmount } = render(<SubVariablePicker value="size" onChange={onChange} />)

      await user.click(screen.getByRole('combobox'))
      await user.click(screen.getByRole('option', { name: 'name' }))

      await waitFor(() => {
        expect(onChange).toHaveBeenCalledWith('name')
      })

      unmount()
      render(<SubVariablePicker value="" onChange={onChange} />)

      expect(screen.getByText('common.placeholder.select')).toBeInTheDocument()
    })

    it('should toggle limit and update the size slider', async () => {
      const user = userEvent.setup()
      const onChange = vi.fn()
      const { rerender } = render(
        <LimitConfig readonly={false} config={{ enabled: true, size: 10 }} onChange={onChange} />,
      )

      await user.click(screen.getByText('slider-10'))

      expect(onChange).toHaveBeenCalledWith({ enabled: true, size: 11 })

      rerender(
        <LimitConfig readonly={false} config={{ enabled: false, size: 10 }} onChange={onChange} />,
      )

      expect(screen.queryByText('slider-10')).not.toBeInTheDocument()
      await user.click(screen.getByRole('switch'))
      expect(onChange).toHaveBeenCalledWith({ enabled: true, size: 10 })
    })

    it('should render the selected input variable in the node preview', () => {
      renderWorkflowFlowComponent(<Node id="node-2" data={createData()} />, {
        nodes: [
          {
            id: 'node-1',
            position: { x: 0, y: 0 },
            data: { type: BlockEnum.Answer, title: 'Answer' } as any,
          },
        ],
        edges: [],
      })

      expect(screen.getByText('Answer')).toBeInTheDocument()
      expect(screen.getByText('items')).toBeInTheDocument()
    })

    it('should resolve system variables through the start node and return null without a variable', () => {
      const { rerender } = renderWorkflowFlowComponent(
        <Node id="node-2" data={createData({ variable: ['sys', 'files'] as any })} />,
        {
          nodes: [
            {
              id: 'start',
              position: { x: 0, y: 0 },
              data: { type: BlockEnum.Start, title: 'Start' } as any,
            },
          ],
          edges: [],
        },
      )

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

      rerender(<Node id="node-2" data={createData({ variable: [] as any })} />)

      expect(screen.queryByText('workflow.nodes.listFilter.inputVar')).not.toBeInTheDocument()
      expect(screen.queryByText('Start')).not.toBeInTheDocument()
    })

    it('should render the panel controls and output vars', async () => {
      const user = userEvent.setup()
      renderPanel()

      await user.click(screen.getByText('pick-var'))
      await user.click(screen.getByText('filter-condition'))
      await user.click(screen.getByText('workflow.nodes.listFilter.asc'))

      expect(screen.getByText('result:Array[number]')).toBeInTheDocument()
      expect(screen.getByText('first_record:number')).toBeInTheDocument()
      expect(screen.getByText('last_record:number')).toBeInTheDocument()
    })

    it('should hide disabled sections and render order controls without sub variables', () => {
      mockUseConfig.mockReturnValueOnce(
        createConfigResult({
          inputs: createData({
            variable: undefined as any,
            filter_by: { enabled: false, conditions: [] as any },
            extract_by: { enabled: false, serial: '' },
            order_by: { enabled: false, key: '', value: OrderBy.ASC },
          }),
          hasSubVariable: false,
        }),
      )

      const { rerender } = renderPanel()

      expect(screen.queryByText('filter-condition')).not.toBeInTheDocument()
      expect(screen.queryByDisplayValue('1')).not.toBeInTheDocument()
      expect(screen.queryByText('workflow.nodes.listFilter.asc')).not.toBeInTheDocument()

      mockUseConfig.mockReturnValueOnce(
        createConfigResult({
          inputs: createData({
            order_by: { enabled: true, key: '', value: OrderBy.ASC },
          }),
          hasSubVariable: false,
        }),
      )

      rerender(<Panel id="node-1" data={createData()} panelProps={panelProps} />)

      expect(screen.getByText('workflow.nodes.listFilter.asc')).toBeInTheDocument()
      expect(screen.queryByText('common.placeholder.select')).not.toBeInTheDocument()
    })
  })
})
