import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { act, fireEvent, render, screen } from '@testing-library/react'
import type { ECharts } from 'echarts'
import { BrushToggle } from './brush-toggle'
import { Provider } from '../../provider/widget-provider'
import {
  applyTransforms,
  clearAllWidgetStores,
  getWidgetStore,
  setEchartInstance,
} from '../../stores'

interface ChartHandlers {
  finished?: () => void
}

interface MockChart {
  dispatchAction: ReturnType<typeof vi.fn>
  on: (ev: string, cb: () => void) => void
  off: (ev: string, cb: () => void) => void
  __handlers: ChartHandlers
}

function makeMockChart(): MockChart {
  const handlers: ChartHandlers = {}
  return {
    dispatchAction: vi.fn(),
    on: (ev, cb) => {
      if (ev === 'finished') handlers.finished = cb
    },
    off: (ev) => {
      if (ev === 'finished') handlers.finished = undefined
    },
    __handlers: handlers,
  }
}

beforeEach(() => clearAllWidgetStores())
afterEach(() => clearAllWidgetStores())

describe('<BrushToggle>', () => {
  it('starts disabled by default', () => {
    render(
      <Provider id='bt-1' data={[]}>
        <BrushToggle />
      </Provider>,
    )
    expect(screen.getByLabelText('Enable brush selection')).toBeTruthy()
  })

  it('respects initialEnabled=true', () => {
    render(
      <Provider id='bt-2' data={[]}>
        <BrushToggle initialEnabled />
      </Provider>,
    )
    expect(screen.getByLabelText('Disable brush selection')).toBeTruthy()
  })

  it('clicking enables the transform — brush config appears in the post-pipeline option', () => {
    render(
      <Provider id='bt-3' data={[1]}>
        <BrushToggle />
      </Provider>,
    )
    fireEvent.click(screen.getByLabelText('Enable brush selection'))
    // configTransforms now apply inside <Widget.Echart>; replay the
    // pipeline locally over a blank structural option.
    const configTransforms = getWidgetStore('bt-3').getState().configTransforms
    const opts = applyTransforms({}, configTransforms) as {
      brush?: { xAxisIndex?: string }
    }
    expect(opts.brush).toBeTruthy()
    expect(opts.brush?.xAxisIndex).toBe('all')
  })

  it('dispatches takeGlobalCursor with brush config when enabled and a chart is registered', () => {
    const chart = makeMockChart()
    setEchartInstance('bt-4', chart as unknown as ECharts)
    render(
      <Provider id='bt-4' data={[1]}>
        <BrushToggle initialEnabled />
      </Provider>,
    )
    expect(chart.dispatchAction).toHaveBeenCalledWith({
      type: 'takeGlobalCursor',
      key: 'brush',
      brushOption: { brushType: 'lineX', brushMode: 'multiple' },
    })
  })

  it("re-dispatches takeGlobalCursor on the chart's 'finished' event", () => {
    const chart = makeMockChart()
    setEchartInstance('bt-5', chart as unknown as ECharts)
    render(
      <Provider id='bt-5' data={[1]}>
        <BrushToggle initialEnabled />
      </Provider>,
    )
    chart.dispatchAction.mockClear()
    act(() => chart.__handlers.finished?.())
    expect(chart.dispatchAction).toHaveBeenCalledWith({
      type: 'takeGlobalCursor',
      key: 'brush',
      brushOption: { brushType: 'lineX', brushMode: 'multiple' },
    })
  })

  it('on disable: clears the global cursor and wipes drawn areas', () => {
    const chart = makeMockChart()
    setEchartInstance('bt-6', chart as unknown as ECharts)
    render(
      <Provider id='bt-6' data={[1]}>
        <BrushToggle initialEnabled />
      </Provider>,
    )
    chart.dispatchAction.mockClear()
    fireEvent.click(screen.getByLabelText('Disable brush selection'))
    expect(chart.dispatchAction).toHaveBeenCalledWith({
      type: 'takeGlobalCursor',
    })
    expect(chart.dispatchAction).toHaveBeenCalledWith({
      type: 'brush',
      areas: [],
    })
  })

  it('honors brushType=rect and brushMode=single', () => {
    const chart = makeMockChart()
    setEchartInstance('bt-7', chart as unknown as ECharts)
    render(
      <Provider id='bt-7' data={[1]}>
        <BrushToggle initialEnabled brushType='rect' brushMode='single' />
      </Provider>,
    )
    expect(chart.dispatchAction).toHaveBeenCalledWith({
      type: 'takeGlobalCursor',
      key: 'brush',
      brushOption: { brushType: 'rect', brushMode: 'single' },
    })
  })

  it('wipes drawn brush areas when an external selection is cleared while enabled', () => {
    const chart = makeMockChart()
    setEchartInstance('bt-9', chart as unknown as ECharts)
    const { rerender } = render(
      <Provider id='bt-9' data={[1]}>
        <BrushToggle initialEnabled selection={['a', 'b']} />
      </Provider>,
    )
    chart.dispatchAction.mockClear()
    rerender(
      <Provider id='bt-9' data={[1]}>
        <BrushToggle initialEnabled selection={[]} />
      </Provider>,
    )
    expect(chart.dispatchAction).toHaveBeenCalledWith({
      type: 'brush',
      areas: [],
    })
  })

  it('attaches the cursor reactively when the chart arrives *after* the toggle mounts', () => {
    const chart = makeMockChart()
    render(
      <Provider id='bt-8' data={[1]}>
        <BrushToggle initialEnabled />
      </Provider>,
    )
    // Chart not yet registered — no dispatch should have happened.
    expect(chart.dispatchAction).not.toHaveBeenCalled()
    // Simulate the bridge mounting and publishing the instance.
    act(() => setEchartInstance('bt-8', chart as unknown as ECharts))
    expect(chart.dispatchAction).toHaveBeenCalledWith({
      type: 'takeGlobalCursor',
      key: 'brush',
      brushOption: { brushType: 'lineX', brushMode: 'multiple' },
    })
  })
})
