import { describe, expect, test, vi } from 'vitest'
import { BasemapsControls } from './basemap-controls'
import type { BasemapsControlsChildrenProps } from './types'
import { render } from '../tests/providers'
import { MAP_ID } from '../tests/config'
import { CartoBasemaps } from './const'

describe('BasemapsControls', () => {
  test('renders with initial basemap', () => {
    const mockChildren = vi.fn((props: BasemapsControlsChildrenProps) => (
      <div data-testid='basemap-value'>{props.basemap}</div>
    ))

    const { container } = render(
      <BasemapsControls mapId={MAP_ID}>{mockChildren}</BasemapsControls>,
    )

    expect(mockChildren).toHaveBeenCalled()
    const basemapDiv = container.querySelector('[data-testid="basemap-value"]')
    expect(basemapDiv?.textContent).toBe(CartoBasemaps.positron)
  })

  test('provides setBasemap function', () => {
    let capturedProps: BasemapsControlsChildrenProps = undefined!

    render(
      <BasemapsControls mapId={MAP_ID}>
        {(props: BasemapsControlsChildrenProps) => {
          capturedProps = props
          return null
        }}
      </BasemapsControls>,
    )

    expect(capturedProps).not.toBeNull()
    expect(typeof capturedProps?.setBasemap).toBe('function')
  })

  test('setBasemap updates basemap', () => {
    let capturedProps: BasemapsControlsChildrenProps = undefined!

    const { rerender } = render(
      <BasemapsControls mapId={MAP_ID}>
        {(props: BasemapsControlsChildrenProps) => {
          capturedProps = props
          return <div data-testid='basemap-value'>{props.basemap}</div>
        }}
      </BasemapsControls>,
    )

    expect(capturedProps?.basemap).toBe(CartoBasemaps.positron)

    // Change basemap
    capturedProps?.setBasemap(CartoBasemaps.darkMatter)

    // Force re-render to see the change
    rerender(
      <BasemapsControls mapId={MAP_ID}>
        {(props: BasemapsControlsChildrenProps) => {
          capturedProps = props
          return <div data-testid='basemap-value'>{props.basemap}</div>
        }}
      </BasemapsControls>,
    )

    expect(capturedProps?.basemap).toBe(CartoBasemaps.darkMatter)
  })

  test('setBasemap function is memoized with mapId', () => {
    let firstSetBasemap: BasemapsControlsChildrenProps['setBasemap'] | null =
      null
    let secondSetBasemap: BasemapsControlsChildrenProps['setBasemap'] | null =
      null

    const { rerender } = render(
      <BasemapsControls mapId={MAP_ID}>
        {(props: BasemapsControlsChildrenProps) => {
          firstSetBasemap = props.setBasemap
          return null
        }}
      </BasemapsControls>,
    )

    rerender(
      <BasemapsControls mapId={MAP_ID}>
        {(props: BasemapsControlsChildrenProps) => {
          secondSetBasemap = props.setBasemap
          return null
        }}
      </BasemapsControls>,
    )

    // Should be the same function reference due to useCallback
    expect(firstSetBasemap).toBe(secondSetBasemap)
  })
})
