/**
 * Render tests for BrushToggle covering the previously-uncovered branches
 * in src/widgets/actions/brush-toggle/brush-toggle.tsx (0/72 baseline).
 *
 * Coverage focus:
 * - useWidgetSelector default (defaultEnabled when no store entry)
 * - Initial seed useEffect only fires when brush is undefined
 * - handleToggle: brush=false → true (clears rectangles + invokes consumer
 *   callback) vs brush=true → false (preserves rectangles)
 * - Consumer-driven clear effect (`selections === 0` branch with vs without
 *   rectangles/selection)
 * - lastSelectionRef gating (callback fires only once per new selection)
 * - Custom labels, custom Icon, custom IconButtonProps
 *
 * Does NOT render BrushOverlay logic — that's a separate file with its own
 * 177-branch surface. BrushOverlay is included via the BrushToggle JSX, but
 * its rendering happens lazily and the overlay's pointer logic isn't
 * exercised here.
 */
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { render, screen, fireEvent } from '@testing-library/react'
import { ThemeProvider, createTheme } from '@mui/material/styles'
import { useWidgetStore, widgetStoreActions } from '../../stores/widget-store'
import { BrushToggle } from './brush-toggle'
import type { BrushState } from './types'

const theme = createTheme({
  palette: {
    common: { white: '#ffffff', black: '#000000' },
    secondary: { main: '#3366ff' },
    // @ts-expect-error - `black` is a CARTO-specific MUI palette extension
    black: { 60: 'rgba(0,0,0,0.6)' },
  },
})

function renderWithTheme(ui: React.ReactElement) {
  return render(<ThemeProvider theme={theme}>{ui}</ThemeProvider>)
}

beforeEach(() => {
  useWidgetStore.getState().clearWidgets()
})

describe('BrushToggle render', () => {
  it('renders with brush off (default)', () => {
    renderWithTheme(<BrushToggle id='bt-1' />)
    const button = screen.getByRole('button')
    expect(button.getAttribute('data-active')).toBe('false')
  })

  it('seeds the widget store on mount when brush is undefined', () => {
    renderWithTheme(<BrushToggle id='bt-seed' defaultEnabled />)
    const widget = widgetStoreActions.getWidget<BrushState>('bt-seed')
    expect(widget?.brush).toBe(true)
  })

  it('does NOT seed when brush is already set in the store', () => {
    widgetStoreActions.setWidget('bt-noseed', { brush: false })
    renderWithTheme(<BrushToggle id='bt-noseed' defaultEnabled />)
    const widget = widgetStoreActions.getWidget<BrushState>('bt-noseed')
    expect(widget?.brush).toBe(false)
  })

  it('uses custom labels for aria-label', () => {
    renderWithTheme(
      <BrushToggle
        id='bt-labels'
        labels={{ enable: 'Brush on', disable: 'Brush off' }}
      />,
    )
    expect(screen.getByRole('button', { name: 'Brush on' })).toBeDefined()
  })

  it('respects an explicit ariaLabel', () => {
    renderWithTheme(
      <BrushToggle
        id='bt-aria'
        labels={{ ariaLabel: 'Toggle brush selection' }}
      />,
    )
    expect(
      screen.getByRole('button', { name: 'Toggle brush selection' }),
    ).toBeDefined()
  })

  it('renders custom Icon when provided', () => {
    renderWithTheme(
      <BrushToggle id='bt-icon' Icon={<span data-testid='custom-icon' />} />,
    )
    expect(screen.getByTestId('custom-icon')).toBeDefined()
  })
})

describe('BrushToggle interactions', () => {
  it('handleToggle: brush=false → true clears rectangles and invokes consumer callback with empty selection', () => {
    const onBrushSelected = vi.fn()
    widgetStoreActions.setWidget('bt-on', { brush: false })
    renderWithTheme(
      <BrushToggle id='bt-on' onBrushSelected={onBrushSelected} />,
    )
    fireEvent.click(screen.getByRole('button'))
    const w = widgetStoreActions.getWidget<BrushState>('bt-on')
    expect(w?.brush).toBe(true)
    expect(w?.brushRects).toEqual([])
    expect(onBrushSelected).toHaveBeenCalledWith({
      dataIndex: [],
      seriesIndex: 0,
    })
  })

  it('handleToggle: brush=true → false does NOT clear rectangles', () => {
    widgetStoreActions.setWidget('bt-off', {
      brush: true,
      brushRects: [{ x: 1, y: 2, w: 3, h: 4 }],
    })
    const onBrushSelected = vi.fn()
    renderWithTheme(
      <BrushToggle id='bt-off' onBrushSelected={onBrushSelected} />,
    )
    fireEvent.click(screen.getByRole('button'))
    const w = widgetStoreActions.getWidget<BrushState>('bt-off')
    expect(w?.brush).toBe(false)
    // Rectangles preserved
    expect(w?.brushRects).toHaveLength(1)
    // Consumer callback not invoked on the disable transition
    expect(onBrushSelected).not.toHaveBeenCalled()
  })

  it('works when onBrushSelected is not provided (`?.` short-circuit)', () => {
    widgetStoreActions.setWidget('bt-no-cb', { brush: false })
    renderWithTheme(<BrushToggle id='bt-no-cb' />)
    fireEvent.click(screen.getByRole('button'))
    const w = widgetStoreActions.getWidget<BrushState>('bt-no-cb')
    expect(w?.brush).toBe(true)
  })
})

describe('BrushToggle consumer-driven clear (selections=0)', () => {
  it('clears stored rectangles + selection when `selections` transitions to 0', () => {
    widgetStoreActions.setWidget('bt-clear', {
      brush: true,
      brushRects: [{ x: 1, y: 2, w: 3, h: 4 }],
      brushSelection: { dataIndex: [1, 2, 3], seriesIndex: 0 },
    })
    renderWithTheme(<BrushToggle id='bt-clear' selections={0} />)
    const w = widgetStoreActions.getWidget<BrushState>('bt-clear')
    expect(w?.brushRects).toEqual([])
    expect(w?.brushSelection?.dataIndex).toEqual([])
  })

  it('is a no-op when there is nothing to clear', () => {
    widgetStoreActions.setWidget('bt-clear-nop', {
      brush: true,
      brushRects: [],
      brushSelection: { dataIndex: [], seriesIndex: 0 },
    })
    renderWithTheme(<BrushToggle id='bt-clear-nop' selections={0} />)
    const w = widgetStoreActions.getWidget<BrushState>('bt-clear-nop')
    // Still empty; effect should have short-circuited
    expect(w?.brushRects).toEqual([])
  })

  it('does nothing when `selections` is non-zero', () => {
    widgetStoreActions.setWidget('bt-nonzero', {
      brush: true,
      brushRects: [{ x: 0, y: 0, w: 1, h: 1 }],
      brushSelection: { dataIndex: [5], seriesIndex: 0 },
    })
    renderWithTheme(<BrushToggle id='bt-nonzero' selections={3} />)
    const w = widgetStoreActions.getWidget<BrushState>('bt-nonzero')
    expect(w?.brushRects).toHaveLength(1)
    expect(w?.brushSelection?.dataIndex).toEqual([5])
  })
})

describe('BrushToggle selection-callback effect', () => {
  it('invokes onBrushSelected when the store selection changes', () => {
    const onBrushSelected = vi.fn()
    widgetStoreActions.setWidget('bt-sel', {
      brush: true,
      brushSelection: { dataIndex: [1, 2], seriesIndex: 0 },
    })
    renderWithTheme(
      <BrushToggle id='bt-sel' onBrushSelected={onBrushSelected} />,
    )
    expect(onBrushSelected).toHaveBeenCalledWith({
      dataIndex: [1, 2],
      seriesIndex: 0,
    })
  })

  it('does not double-invoke for the same selection reference', () => {
    const onBrushSelected = vi.fn()
    widgetStoreActions.setWidget('bt-sel-dup', {
      brush: true,
      brushSelection: { dataIndex: [1], seriesIndex: 0 },
    })
    const { rerender } = renderWithTheme(
      <BrushToggle id='bt-sel-dup' onBrushSelected={onBrushSelected} />,
    )
    rerender(
      <ThemeProvider theme={theme}>
        <BrushToggle id='bt-sel-dup' onBrushSelected={onBrushSelected} />
      </ThemeProvider>,
    )
    expect(onBrushSelected).toHaveBeenCalledTimes(1)
  })
})
