import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { fireEvent, render, screen } from '@testing-library/react'
import { Provider } from '../../provider/widget-provider'
import { clearAllWidgetStores, getWidgetStore } from '../../stores'
import { LockSelection } from './lock-selection'
import { filterByLockedItems } from './transforms'

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

const DATA = [
  [
    { name: 'A', value: 10 },
    { name: 'B', value: 20 },
    { name: 'C', value: 30 },
  ],
]

describe('<LockSelection>', () => {
  it('disables the trigger when nothing is selected and nothing is locked', () => {
    render(
      <Provider id='ls1' data={DATA}>
        <LockSelection
          selection={[]}
          lockedItems={[]}
          onLockChange={() => undefined}
          labels={{ disabled: 'Pick rows' }}
        />
      </Provider>,
    )
    const btn = screen.getByRole('button', { name: 'Pick rows' })
    expect(btn.getAttribute('disabled')).not.toBeNull()
  })

  it('shows the lock label when selection is non-empty and not locked', () => {
    render(
      <Provider id='ls2' data={DATA}>
        <LockSelection
          selection={['A']}
          lockedItems={[]}
          onLockChange={() => undefined}
          labels={{ lock: 'Lock these' }}
        />
      </Provider>,
    )
    const btn = screen.getByRole('button', { name: 'Lock these' })
    expect(btn.getAttribute('aria-pressed')).toBe('false')
    expect(btn.getAttribute('disabled')).toBeNull()
  })

  it('shows the unlock label when locked and aria-pressed=true', () => {
    render(
      <Provider id='ls3' data={DATA}>
        <LockSelection
          selection={['A']}
          lockedItems={['A']}
          onLockChange={() => undefined}
          labels={{ unlock: 'Release' }}
        />
      </Provider>,
    )
    const btn = screen.getByRole('button', { name: 'Release' })
    expect(btn.getAttribute('aria-pressed')).toBe('true')
  })

  it('snapshots selection on lock — onLockChange called with current selection', () => {
    const onLockChange = vi.fn()
    render(
      <Provider id='ls4' data={DATA}>
        <LockSelection
          selection={['A', 'B']}
          lockedItems={[]}
          onLockChange={onLockChange}
          labels={{ lock: 'Lock' }}
        />
      </Provider>,
    )
    fireEvent.click(screen.getByRole('button', { name: 'Lock' }))
    expect(onLockChange).toHaveBeenCalledTimes(1)
    expect(onLockChange).toHaveBeenCalledWith(['A', 'B'])
  })

  it('clears the lock — onLockChange called with empty array', () => {
    const onLockChange = vi.fn()
    render(
      <Provider id='ls5' data={DATA}>
        <LockSelection
          selection={['A']}
          lockedItems={['A', 'B']}
          onLockChange={onLockChange}
          labels={{ unlock: 'Unlock' }}
        />
      </Provider>,
    )
    fireEvent.click(screen.getByRole('button', { name: 'Unlock' }))
    expect(onLockChange).toHaveBeenCalledWith([])
  })

  it('registers a data transform that filters when locked, restores when unlocked', () => {
    const { rerender } = render(
      <Provider id='ls6' data={DATA}>
        <LockSelection
          selection={['A']}
          lockedItems={['A']}
          onLockChange={() => undefined}
        />
      </Provider>,
    )
    const locked = getWidgetStore('ls6').getState().data as unknown[][]
    expect(locked).toEqual([[{ name: 'A', value: 10 }]])

    rerender(
      <Provider id='ls6' data={DATA}>
        <LockSelection
          selection={['A']}
          lockedItems={[]}
          onLockChange={() => undefined}
        />
      </Provider>,
    )
    const unlocked = getWidgetStore('ls6').getState().data as unknown[][]
    // With no lockedItems and the transform disabled, the pipeline returns
    // raw data. All three rows should be present.
    expect(unlocked.flat().length).toBe(3)
  })

  it('lets the destination feed a different lockedItems set later', () => {
    const { rerender } = render(
      <Provider id='ls7' data={DATA}>
        <LockSelection
          selection={['A']}
          lockedItems={['A']}
          onLockChange={() => undefined}
        />
      </Provider>,
    )
    expect(
      (getWidgetStore('ls7').getState().data as unknown[][]).flat(),
    ).toEqual([{ name: 'A', value: 10 }])

    rerender(
      <Provider id='ls7' data={DATA}>
        <LockSelection
          selection={['A', 'B', 'C']}
          lockedItems={['B', 'C']}
          onLockChange={() => undefined}
        />
      </Provider>,
    )
    expect(
      (getWidgetStore('ls7').getState().data as unknown[][]).flat(),
    ).toEqual([
      { name: 'B', value: 20 },
      { name: 'C', value: 30 },
    ])
  })
})

describe('filterByLockedItems', () => {
  it('returns input unchanged when locked set is empty', () => {
    expect(filterByLockedItems(DATA, [])).toBe(DATA)
  })

  it('keeps only items whose name is in the locked set, preserving series shape', () => {
    const out = filterByLockedItems(DATA, ['A', 'C']) as unknown[][]
    expect(out).toEqual([
      [
        { name: 'A', value: 10 },
        { name: 'C', value: 30 },
      ],
    ])
  })

  it('handles numeric names', () => {
    const data = [
      [
        { name: 1, value: 10 },
        { name: 2, value: 20 },
      ],
    ]
    expect(filterByLockedItems(data, [2])).toEqual([[{ name: 2, value: 20 }]])
  })

  it('returns input unchanged when input is not array-shaped', () => {
    expect(filterByLockedItems('nope', ['A'])).toBe('nope')
  })
})
