import { describe, it, expect, vi, beforeEach } from 'vitest'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { TagInput } from './tag-input'

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

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

  it('should render empty input with placeholder', () => {
    render(
      <TagInput
        value={[]}
        onChange={mockOnChange}
        placeholder="Add equipment..."
      />
    )

    expect(screen.getByPlaceholderText('Add equipment...')).toBeInTheDocument()
  })

  it('should add tag on Enter key', async () => {
    const user = userEvent.setup()
    
    render(
      <TagInput
        value={[]}
        onChange={mockOnChange}
      />
    )

    const input = screen.getByRole('textbox')
    await user.type(input, 'Dumbbells')
    await user.keyboard('{Enter}')

    expect(mockOnChange).toHaveBeenCalledWith(['Dumbbells'])
  })

  it('should add tag on blur', async () => {
    const user = userEvent.setup()
    
    render(
      <TagInput
        value={[]}
        onChange={mockOnChange}
      />
    )

    const input = screen.getByRole('textbox')
    await user.type(input, 'Yoga Mat')
    await user.tab() // blur

    expect(mockOnChange).toHaveBeenCalledWith(['Yoga Mat'])
  })

  it('should not add empty tag', async () => {
    const user = userEvent.setup()
    
    render(
      <TagInput
        value={[]}
        onChange={mockOnChange}
      />
    )

    const input = screen.getByRole('textbox')
    await user.type(input, '   ')
    await user.keyboard('{Enter}')

    expect(mockOnChange).not.toHaveBeenCalled()
  })

  it('should not add duplicate tags', async () => {
    const user = userEvent.setup()
    
    render(
      <TagInput
        value={['Dumbbells']}
        onChange={mockOnChange}
      />
    )

    const input = screen.getByRole('textbox')
    await user.type(input, 'Dumbbells')
    await user.keyboard('{Enter}')

    expect(mockOnChange).not.toHaveBeenCalled()
  })

  it('should remove tag on X click', async () => {
    const user = userEvent.setup()
    
    render(
      <TagInput
        value={['Dumbbells', 'Yoga Mat']}
        onChange={mockOnChange}
      />
    )

    // Find and click the remove button on first tag
    const removeButtons = screen.getAllByRole('button', { name: '' })
    await user.click(removeButtons[0])

    expect(mockOnChange).toHaveBeenCalledWith(['Yoga Mat'])
  })

  it('should remove last tag on Backspace when input empty', async () => {
    const user = userEvent.setup()
    
    render(
      <TagInput
        value={['Dumbbells', 'Yoga Mat']}
        onChange={mockOnChange}
      />
    )

    const input = screen.getByRole('textbox')
    await user.click(input)
    await user.keyboard('{Backspace}')

    expect(mockOnChange).toHaveBeenCalledWith(['Dumbbells'])
  })

  it('should not remove tag on Backspace when input has text', async () => {
    const user = userEvent.setup()
    
    render(
      <TagInput
        value={['Dumbbells']}
        onChange={mockOnChange}
      />
    )

    const input = screen.getByRole('textbox')
    await user.type(input, 'Yoga')
    await user.keyboard('{Backspace}')

    expect(mockOnChange).not.toHaveBeenCalled()
  })

  it('should display all tags', () => {
    render(
      <TagInput
        value={['Dumbbells', 'Yoga Mat', 'Resistance Band']}
        onChange={mockOnChange}
      />
    )

    expect(screen.getByText('Dumbbells')).toBeInTheDocument()
    expect(screen.getByText('Yoga Mat')).toBeInTheDocument()
    expect(screen.getByText('Resistance Band')).toBeInTheDocument()
  })

  it('should trim whitespace from tags', async () => {
    const user = userEvent.setup()
    
    render(
      <TagInput
        value={[]}
        onChange={mockOnChange}
      />
    )

    const input = screen.getByRole('textbox')
    await user.type(input, '  Dumbbells  ')
    await user.keyboard('{Enter}')

    expect(mockOnChange).toHaveBeenCalledWith(['Dumbbells'])
  })

  it('should respect disabled prop', () => {
    render(
      <TagInput
        value={['Dumbbells']}
        onChange={mockOnChange}
        disabled
      />
    )

    expect(screen.getByRole('textbox')).toBeDisabled()
    // Remove button on tag should not be present when disabled
    const removeButtons = screen.queryAllByRole('button', { name: '' })
    expect(removeButtons).toHaveLength(0)
  })

  it('should clear input after adding tag', async () => {
    const user = userEvent.setup()
    
    render(
      <TagInput
        value={[]}
        onChange={mockOnChange}
      />
    )

    const input = screen.getByRole('textbox') as HTMLInputElement
    await user.type(input, 'Dumbbells')
    await user.keyboard('{Enter}')

    expect(input.value).toBe('')
  })

  it('should handle multiple tags addition', async () => {
    const user = userEvent.setup()
    const { rerender } = render(
      <TagInput
        value={[]}
        onChange={mockOnChange}
      />
    )

    const input = screen.getByRole('textbox')
    
    // Add first tag
    await user.type(input, 'Dumbbells')
    await user.keyboard('{Enter}')
    expect(mockOnChange).toHaveBeenCalledWith(['Dumbbells'])
    
    // Rerender with updated value
    rerender(
      <TagInput
        value={['Dumbbells']}
        onChange={mockOnChange}
      />
    )
    
    // Add second tag
    await user.type(input, 'Yoga Mat')
    await user.keyboard('{Enter}')
    expect(mockOnChange).toHaveBeenCalledWith(['Dumbbells', 'Yoga Mat'])
    
    // Rerender with updated value
    rerender(
      <TagInput
        value={['Dumbbells', 'Yoga Mat']}
        onChange={mockOnChange}
      />
    )
    
    // Add third tag
    await user.type(input, 'Resistance Band')
    await user.keyboard('{Enter}')
    expect(mockOnChange).toHaveBeenCalledWith(['Dumbbells', 'Yoga Mat', 'Resistance Band'])
  })
})
