import { describe, it, expect, vi } from 'vitest'
import { act, renderHook } from '@testing-library/react'
import { useChartSelection } from './use-chart-selection'

describe('useChartSelection', () => {
  it('clicking an item toggles it into and out of the selection', () => {
    const onSelectionChange = vi.fn()
    const { result, rerender } = renderHook(
      ({ selection }: { selection: readonly string[] }) =>
        useChartSelection<string>({
          selection,
          onSelectionChange,
          keyFromClick: (p) => (p as { name?: string }).name ?? null,
          keysFromBrush: () => [],
        }),
      { initialProps: { selection: [] as readonly string[] } },
    )

    act(() => {
      result.current.onEvents.click?.({ name: 'A' })
    })
    expect(onSelectionChange).toHaveBeenLastCalledWith(['A'])

    rerender({ selection: ['A'] })
    act(() => {
      result.current.onEvents.click?.({ name: 'A' })
    })
    expect(onSelectionChange).toHaveBeenLastCalledWith([])
  })

  it('emits the brushed selection only on brushEnd (brushSelected just caches)', () => {
    const onSelectionChange = vi.fn()
    const { result } = renderHook(() =>
      useChartSelection<number>({
        selection: [99],
        onSelectionChange,
        keyFromClick: () => null,
        keysFromBrush: (selected) => selected.flatMap((s) => s.dataIndex),
      }),
    )

    // Mid-drag updates fire brushSelected — these should NOT call back.
    act(() => {
      result.current.onEvents.brushSelected?.({
        batch: [{ selected: [{ seriesIndex: 0, dataIndex: [3] }] }],
      })
      result.current.onEvents.brushSelected?.({
        batch: [{ selected: [{ seriesIndex: 0, dataIndex: [3, 7] }] }],
      })
      result.current.onEvents.brushSelected?.({
        batch: [{ selected: [{ seriesIndex: 0, dataIndex: [3, 7, 11] }] }],
      })
    })
    expect(onSelectionChange).not.toHaveBeenCalled()

    // Releasing the brush fires brushEnd — that's when we commit.
    act(() => {
      result.current.onEvents.brushEnd?.({})
    })
    expect(onSelectionChange).toHaveBeenCalledTimes(1)
    expect(onSelectionChange).toHaveBeenLastCalledWith([3, 7, 11])
  })

  it('reports null `selection` when the consumer passes nothing or an empty list', () => {
    const { result, rerender } = renderHook(
      ({ selection }: { selection?: readonly string[] }) =>
        useChartSelection<string>({
          selection,
          onSelectionChange: () => undefined,
          keyFromClick: () => null,
          keysFromBrush: () => [],
        }),
      {
        initialProps: { selection: undefined } as {
          selection?: readonly string[]
        },
      },
    )
    expect(result.current.selection).toBeNull()
    rerender({ selection: [] })
    expect(result.current.selection).toBeNull()
    rerender({ selection: ['A'] })
    expect(result.current.selection).toEqual(['A'])
  })

  it('is a no-op when `onSelectionChange` is missing', () => {
    const { result } = renderHook(() =>
      useChartSelection<string>({
        selection: [],
        keyFromClick: (p) => (p as { name?: string }).name ?? null,
        keysFromBrush: () => ['x'],
      }),
    )
    // Neither call should throw — handler short-circuits without a callback.
    expect(() => result.current.onEvents.click?.({ name: 'A' })).not.toThrow()
    expect(() =>
      result.current.onEvents.brushSelected?.({
        batch: [{ selected: [{ seriesIndex: 0, dataIndex: [0] }] }],
      }),
    ).not.toThrow()
  })

  it('skips clicks where keyFromClick returns null (e.g. axis-label clicks)', () => {
    const onSelectionChange = vi.fn()
    const { result } = renderHook(() =>
      useChartSelection<string>({
        selection: [],
        onSelectionChange,
        keyFromClick: () => null,
        keysFromBrush: () => [],
      }),
    )
    act(() => {
      result.current.onEvents.click?.({ name: 'A' })
    })
    expect(onSelectionChange).not.toHaveBeenCalled()
  })
})
