import { describe, it, expect, vi, beforeEach } from 'vitest'
import { render, screen, waitFor, fireEvent } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { ExerciseList } from './exercise-list'

describe('ExerciseList', () => {
  const mockOnChange = vi.fn()

  beforeEach(() => {
    vi.clearAllMocks()
  })

  it('should render with empty list showing add button', () => {
    render(
      <ExerciseList
        value={[]}
        onChange={mockOnChange}
      />
    )

    expect(screen.getByText('Add Exercise')).toBeInTheDocument()
  })

  it('should add new exercise on button click', async () => {
    const user = userEvent.setup()
    
    render(
      <ExerciseList
        value={[{ name: 'Push-ups', duration: 20 }]}
        onChange={mockOnChange}
      />
    )

    const addButton = screen.getByText('Add Exercise')
    await user.click(addButton)

    expect(mockOnChange).toHaveBeenCalledWith([
      { name: 'Push-ups', duration: 20 },
      { name: '', duration: 20 },
    ])
  })

  it('should remove exercise on delete click', async () => {
    const user = userEvent.setup()
    
    render(
      <ExerciseList
        value={[
          { name: 'Push-ups', duration: 20 },
          { name: 'Squats', duration: 20 },
        ]}
        onChange={mockOnChange}
      />
    )

    const deleteButton = screen.getByTestId('delete-exercise-0')
    await user.click(deleteButton)

    expect(mockOnChange).toHaveBeenCalledWith([
      { name: 'Squats', duration: 20 },
    ])
  })

  it('should update exercise name', () => {
    render(
      <ExerciseList
        value={[{ name: 'Push-ups', duration: 20 }]}
        onChange={mockOnChange}
      />
    )

    // Get the input and simulate change directly using fireEvent
    const nameInput = screen.getByTestId('exercise-name-0')
    fireEvent.change(nameInput, { target: { value: 'Burpees' } })

    // Check that onChange was called with correct value
    expect(mockOnChange).toHaveBeenCalledWith([{ name: 'Burpees', duration: 20 }])
  })

  it('should update exercise duration', () => {
    render(
      <ExerciseList
        value={[{ name: 'Push-ups', duration: 20 }]}
        onChange={mockOnChange}
      />
    )

    // Get the input and simulate change directly using fireEvent
    const durationInput = screen.getByTestId('exercise-duration-0')
    fireEvent.change(durationInput, { target: { value: '30' } })

    // Check that onChange was called with correct value
    expect(mockOnChange).toHaveBeenCalledWith([{ name: 'Push-ups', duration: 30 }])
  })

  it('should disable delete button when only one exercise', () => {
    render(
      <ExerciseList
        value={[{ name: 'Push-ups', duration: 20 }]}
        onChange={mockOnChange}
      />
    )

    const deleteButton = screen.getByTestId('delete-exercise-0')
    expect(deleteButton).toBeDisabled()
  })

  it('should use workTimeDefault for new exercises', async () => {
    const user = userEvent.setup()
    
    render(
      <ExerciseList
        value={[]}
        onChange={mockOnChange}
        workTimeDefault={45}
      />
    )

    const addButton = screen.getByText('Add Exercise')
    await user.click(addButton)

    expect(mockOnChange).toHaveBeenCalledWith([
      { name: '', duration: 45 },
    ])
  })

  it('should move exercise up', async () => {
    const user = userEvent.setup()
    
    render(
      <ExerciseList
        value={[
          { name: 'First', duration: 20 },
          { name: 'Second', duration: 20 },
        ]}
        onChange={mockOnChange}
      />
    )

    const upButton = screen.getByTestId('move-up-1')
    expect(upButton).not.toBeDisabled()
    
    await user.click(upButton)
    expect(mockOnChange).toHaveBeenCalledWith([
      { name: 'Second', duration: 20 },
      { name: 'First', duration: 20 },
    ])
  })

  it('should move exercise down', async () => {
    const user = userEvent.setup()
    
    render(
      <ExerciseList
        value={[
          { name: 'First', duration: 20 },
          { name: 'Second', duration: 20 },
        ]}
        onChange={mockOnChange}
      />
    )

    const downButton = screen.getByTestId('move-down-0')
    expect(downButton).not.toBeDisabled()
    
    await user.click(downButton)
    expect(mockOnChange).toHaveBeenCalledWith([
      { name: 'Second', duration: 20 },
      { name: 'First', duration: 20 },
    ])
  })

  it('should disable move up on first item', () => {
    render(
      <ExerciseList
        value={[
          { name: 'First', duration: 20 },
          { name: 'Second', duration: 20 },
        ]}
        onChange={mockOnChange}
      />
    )

    const upButton = screen.getByTestId('move-up-0')
    expect(upButton).toBeDisabled()
  })

  it('should disable move down on last item', () => {
    render(
      <ExerciseList
        value={[
          { name: 'First', duration: 20 },
          { name: 'Second', duration: 20 },
        ]}
        onChange={mockOnChange}
      />
    )

    const downButton = screen.getByTestId('move-down-1')
    expect(downButton).toBeDisabled()
  })

  it('should respect disabled prop', () => {
    render(
      <ExerciseList
        value={[{ name: 'Push-ups', duration: 20 }]}
        onChange={mockOnChange}
        disabled
      />
    )

    expect(screen.getByTestId('exercise-name-0')).toBeDisabled()
    expect(screen.getByTestId('exercise-duration-0')).toBeDisabled()
    expect(screen.getByText('Add Exercise')).toBeDisabled()
  })

  it('should render correct number of exercises', () => {
    render(
      <ExerciseList
        value={[
          { name: 'First', duration: 20 },
          { name: 'Second', duration: 20 },
          { name: 'Third', duration: 20 },
        ]}
        onChange={mockOnChange}
      />
    )

    // Check that we have 3 exercise name inputs
    expect(screen.getByTestId('exercise-name-0')).toBeInTheDocument()
    expect(screen.getByTestId('exercise-name-1')).toBeInTheDocument()
    expect(screen.getByTestId('exercise-name-2')).toBeInTheDocument()
    
    // Verify move buttons have correct aria-labels
    expect(screen.getByTestId('move-up-0')).toHaveAttribute('aria-label', 'Move exercise 1 up')
    expect(screen.getByTestId('move-up-1')).toHaveAttribute('aria-label', 'Move exercise 2 up')
    expect(screen.getByTestId('move-up-2')).toHaveAttribute('aria-label', 'Move exercise 3 up')
  })
})
