import { describe, test, vi, expect, beforeEach } from 'vitest'
import { render, screen, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { MeasurementToolsUI } from './measurement-tools'
import type { MeasurementToolsComponentProps } from './types'
import type { MeasurementMode } from '../types'
import {
  changeMeasurementModeOptions,
  DEFAULT_MEASUREMENT_TOOLS_MODES,
  DEFAULT_MEASUREMENT_TOOLS_UNITS,
} from '../../../tests/config'

describe('MeasurementToolsUI', () => {
  const mockOnActionToggle = vi.fn()
  const mockOnChangeMode = vi.fn()
  const mockOnChangeUnit = vi.fn()

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

  test('should enable measurement', async () => {
    const { container } = render(
      <Component
        enabled={false}
        modes={DEFAULT_MEASUREMENT_TOOLS_MODES}
        modeSelected={DEFAULT_MEASUREMENT_TOOLS_MODES.distance.value}
        units={DEFAULT_MEASUREMENT_TOOLS_UNITS}
        mockOnActionToggle={mockOnActionToggle}
        mockOnChangeMode={mockOnChangeMode}
        mockOnChangeUnit={mockOnChangeUnit}
      />,
    )

    const rulerButton = within(container).getByLabelText(
      'Click on the map to start measuring',
    )
    await userEvent.click(rulerButton)

    expect(mockOnActionToggle).toHaveBeenCalledWith(true)
  })

  for (const [mode, label] of Object.entries(changeMeasurementModeOptions)) {
    test(`should change mode to ${label}`, async () => {
      const { container } = render(
        <Component
          enabled
          modes={DEFAULT_MEASUREMENT_TOOLS_MODES}
          modeSelected={mode as MeasurementMode}
          units={DEFAULT_MEASUREMENT_TOOLS_UNITS}
          mockOnActionToggle={mockOnActionToggle}
          mockOnChangeMode={mockOnChangeMode}
          mockOnChangeUnit={mockOnChangeUnit}
        />,
      )

      const dropdownButton = within(container).getAllByRole('button')[1]!
      await userEvent.click(dropdownButton)

      const modeButton = await screen.findByRole('menuitem', {
        name: label,
      })
      await userEvent.click(modeButton)

      expect(mockOnChangeMode).toHaveBeenCalledWith(mode)
    })
  }

  test('should change meters and kilometers units', async () => {
    const { container } = render(
      <Component
        enabled
        modes={DEFAULT_MEASUREMENT_TOOLS_MODES}
        modeSelected={DEFAULT_MEASUREMENT_TOOLS_MODES.distance.value}
        units={DEFAULT_MEASUREMENT_TOOLS_UNITS}
        mockOnActionToggle={mockOnActionToggle}
        mockOnChangeMode={mockOnChangeMode}
        mockOnChangeUnit={mockOnChangeUnit}
      />,
    )

    const dropdownButton = within(container).getAllByRole('button')[1]!
    await userEvent.click(dropdownButton)

    const unitButton = await screen.findByText('Unit of measurement')
    await userEvent.click(unitButton)

    const dialog = await screen.findByRole('dialog')

    const radioGroup = within(dialog).getByRole('radiogroup')
    const kilometerRadioButton = within(radioGroup).getByText('Kilometers')
    await userEvent.click(kilometerRadioButton)

    const saveButton = within(dialog).getByText('Save')
    await userEvent.click(saveButton)
    expect(mockOnChangeUnit).toHaveBeenCalledWith('kilometer')
  })

  test('should change feet and miles units', async () => {
    const { container } = render(
      <Component
        enabled
        modes={DEFAULT_MEASUREMENT_TOOLS_MODES}
        modeSelected={DEFAULT_MEASUREMENT_TOOLS_MODES.distance.value}
        units={DEFAULT_MEASUREMENT_TOOLS_UNITS}
        mockOnActionToggle={mockOnActionToggle}
        mockOnChangeMode={mockOnChangeMode}
        mockOnChangeUnit={mockOnChangeUnit}
      />,
    )

    const dropdownButton = within(container).getAllByRole('button')[1]!
    await userEvent.click(dropdownButton)

    const unitButton = await screen.findByText('Unit of measurement')
    await userEvent.click(unitButton)

    const dialog = await screen.findByRole('dialog')

    const feetAndMilesButton = within(dialog).getByText('Feet and Miles')
    await userEvent.click(feetAndMilesButton)

    const radioGroup = await within(dialog).findByRole('radiogroup')
    const mileRadioButton = await within(radioGroup).findByText('Mile')
    await userEvent.click(mileRadioButton)

    const saveButton = within(dialog).getByText('Save')
    await userEvent.click(saveButton)
    expect(mockOnChangeUnit).toHaveBeenCalledWith('mile')
  })
})

interface ComponentProps {
  enabled: boolean
  modes: MeasurementToolsComponentProps['modes']
  modeSelected: MeasurementToolsComponentProps['modeSelected']
  units: MeasurementToolsComponentProps['units']
  mockOnActionToggle: MeasurementToolsComponentProps['onActionToggle']
  mockOnChangeMode: MeasurementToolsComponentProps['onChangeMode']
  mockOnChangeUnit: MeasurementToolsComponentProps['onChangeUnit']
}

// Aux
const Component = ({
  enabled,
  modes,
  modeSelected,
  units,
  mockOnActionToggle,
  mockOnChangeMode,
  mockOnChangeUnit,
}: ComponentProps) => {
  return (
    <MeasurementToolsUI
      value='1000m'
      enabled={enabled}
      modes={modes}
      modeSelected={modeSelected}
      units={units}
      onActionToggle={mockOnActionToggle}
      onChangeMode={mockOnChangeMode}
      onChangeUnit={mockOnChangeUnit}
    />
  )
}
