import { describe, test, expect, beforeEach, vi } from 'vitest'
import { render, screen, fireEvent } from '@testing-library/react'
import { RangeUI } from './range-ui'
import { useWidgetStore } from '../stores/widget-store'
import type { RangeWidgetState } from './types'

describe('RangeUI', () => {
  beforeEach(() => {
    useWidgetStore.getState().clearWidgets()
  })

  test('returns null when widget does not exist', () => {
    const { container } = render(<RangeUI id='non-existent' />)
    expect(container.firstChild).toBeNull()
  })

  test('renders with single range item', () => {
    useWidgetStore.getState().setWidget<RangeWidgetState>('test-range', {
      type: 'range',
      data: [{ min: 0, max: 100, value: [20, 80] }],
    })

    render(<RangeUI id='test-range' />)

    const inputs = screen.getAllByRole('textbox')
    expect(inputs).toHaveLength(2)
    expect((inputs[0] as HTMLInputElement).value).toBe('20')
    expect((inputs[1] as HTMLInputElement).value).toBe('80')
  })

  test('renders with multiple range items', () => {
    useWidgetStore.getState().setWidget<RangeWidgetState>('test-range', {
      type: 'range',
      data: [
        { min: 0, max: 100, value: [20, 80] },
        { min: 0, max: 50, value: [10, 40] },
      ],
    })

    render(<RangeUI id='test-range' />)

    const inputs = screen.getAllByRole('textbox')
    expect(inputs).toHaveLength(4)
    expect((inputs[0] as HTMLInputElement).value).toBe('20')
    expect((inputs[1] as HTMLInputElement).value).toBe('80')
    expect((inputs[2] as HTMLInputElement).value).toBe('10')
    expect((inputs[3] as HTMLInputElement).value).toBe('40')
  })

  test('uses formatter when provided', () => {
    useWidgetStore.getState().setWidget<RangeWidgetState>('test-range', {
      type: 'range',
      data: [{ min: 0, max: 100, value: [20, 80] }],
      formatter: (val: number) => `$${val}`,
    })

    render(<RangeUI id='test-range' />)

    const inputs = screen.getAllByRole('textbox')
    expect((inputs[0] as HTMLInputElement).value).toBe('$20')
    expect((inputs[1] as HTMLInputElement).value).toBe('$80')
  })

  test('handles onChange callback', () => {
    const onChange = vi.fn()
    useWidgetStore.getState().setWidget<RangeWidgetState>('test-range', {
      type: 'range',
      data: [{ min: 0, max: 100, value: [20, 80] }],
      onChange,
    })

    render(<RangeUI id='test-range' />)

    const minInput = screen.getAllByRole('textbox')[0]
    fireEvent.change(minInput!, { target: { value: '30' } })
    fireEvent.blur(minInput!)

    expect(onChange).toHaveBeenCalledWith([30, 80], 0)
  })

  test('renders disabled state', () => {
    useWidgetStore.getState().setWidget<RangeWidgetState>('test-range', {
      type: 'range',
      data: [{ min: 0, max: 100, disabled: true }],
    })

    render(<RangeUI id='test-range' />)

    const inputs = screen.getAllByRole('textbox')
    expect((inputs[0] as HTMLInputElement).disabled).toBe(true)
    expect((inputs[1] as HTMLInputElement).disabled).toBe(true)
  })

  test('handles empty data array', () => {
    useWidgetStore.getState().setWidget<RangeWidgetState>('test-range', {
      type: 'range',
      data: [],
    })

    const { container } = render(<RangeUI id='test-range' />)
    expect(container.firstChild).toBeNull()
  })

  test('handles undefined data', () => {
    useWidgetStore.getState().setWidget<RangeWidgetState>('test-range', {
      type: 'range',
    })

    const { container } = render(<RangeUI id='test-range' />)
    expect(container.firstChild).toBeNull()
  })

  test('uses default min/max when value is not provided', () => {
    useWidgetStore.getState().setWidget<RangeWidgetState>('test-range', {
      type: 'range',
      data: [{ min: 10, max: 90 }],
    })

    render(<RangeUI id='test-range' />)

    const inputs = screen.getAllByRole('textbox')
    expect((inputs[0] as HTMLInputElement).value).toBe('10')
    expect((inputs[1] as HTMLInputElement).value).toBe('90')
  })

  test('validates min input on blur', () => {
    const onChange = vi.fn()
    useWidgetStore.getState().setWidget<RangeWidgetState>('test-range', {
      type: 'range',
      data: [{ min: 0, max: 100, value: [20, 80] }],
      onChange,
    })

    render(<RangeUI id='test-range' />)

    const minInput = screen.getAllByRole('textbox')[0]

    // Try to set min higher than max
    fireEvent.change(minInput!, { target: { value: '90' } })
    fireEvent.blur(minInput!)

    // Should clamp to max value
    expect(onChange).toHaveBeenCalledWith([80, 80], 0)
  })

  test('validates max input on blur', () => {
    const onChange = vi.fn()
    useWidgetStore.getState().setWidget<RangeWidgetState>('test-range', {
      type: 'range',
      data: [{ min: 0, max: 100, value: [20, 80] }],
      onChange,
    })

    render(<RangeUI id='test-range' />)

    const maxInput = screen.getAllByRole('textbox')[1]

    // Try to set max lower than min
    fireEvent.change(maxInput!, { target: { value: '10' } })
    fireEvent.blur(maxInput!)

    // Should clamp to min value
    expect(onChange).toHaveBeenCalledWith([20, 20], 0)
  })

  test('updates when widget data changes', () => {
    useWidgetStore.getState().setWidget<RangeWidgetState>('test-range', {
      type: 'range',
      data: [{ min: 0, max: 100, value: [20, 80] }],
    })

    const { rerender } = render(<RangeUI id='test-range' />)

    let inputs = screen.getAllByRole('textbox')
    expect((inputs[0] as HTMLInputElement).value).toBe('20')
    expect((inputs[1] as HTMLInputElement).value).toBe('80')

    useWidgetStore.getState().setWidget<RangeWidgetState>('test-range', {
      data: [{ min: 0, max: 100, value: [30, 70] }],
    })

    rerender(<RangeUI id='test-range' />)

    inputs = screen.getAllByRole('textbox')
    expect((inputs[0] as HTMLInputElement).value).toBe('30')
    expect((inputs[1] as HTMLInputElement).value).toBe('70')
  })

  test('handles widget removal gracefully', () => {
    useWidgetStore.getState().setWidget<RangeWidgetState>('test-range', {
      type: 'range',
      data: [{ min: 0, max: 100 }],
    })

    const { container, rerender } = render(<RangeUI id='test-range' />)
    expect(container.firstChild).toBeTruthy()

    useWidgetStore.getState().removeWidget('test-range')

    rerender(<RangeUI id='test-range' />)
    expect(container.firstChild).toBeNull()
  })
})
