import { render, screen, act } from '@testing-library/react'
import { fireEvent } from '@testing-library/react'
import { renderHook } from '@testing-library/react'
import { CommandPaletteProvider, useCommandPalette } from '../command-palette-context'

// Wrapper that exposes the context value through data attributes so we can
// assert on it without tying tests to component internals.
function ContextProbe() {
  const { isOpen, open, close, toggle } = useCommandPalette()
  return (
    <div
      data-testid="probe"
      data-open={String(isOpen)}
    >
      <button data-testid="btn-open" onClick={open}>open</button>
      <button data-testid="btn-close" onClick={close}>close</button>
      <button data-testid="btn-toggle" onClick={toggle}>toggle</button>
    </div>
  )
}

function renderWithProvider() {
  return render(
    <CommandPaletteProvider>
      <ContextProbe />
    </CommandPaletteProvider>
  )
}

function probe() {
  return screen.getByTestId('probe')
}

describe('CommandPaletteProvider / useCommandPalette', () => {
  describe('initial state', () => {
    it('starts with isOpen = false', () => {
      renderWithProvider()
      expect(probe().dataset.open).toBe('false')
    })
  })

  describe('open()', () => {
    it('sets isOpen to true', () => {
      renderWithProvider()
      fireEvent.click(screen.getByTestId('btn-open'))
      expect(probe().dataset.open).toBe('true')
    })

    it('is idempotent — calling open twice leaves isOpen true', () => {
      renderWithProvider()
      fireEvent.click(screen.getByTestId('btn-open'))
      fireEvent.click(screen.getByTestId('btn-open'))
      expect(probe().dataset.open).toBe('true')
    })
  })

  describe('close()', () => {
    it('sets isOpen to false', () => {
      renderWithProvider()
      fireEvent.click(screen.getByTestId('btn-open'))
      fireEvent.click(screen.getByTestId('btn-close'))
      expect(probe().dataset.open).toBe('false')
    })

    it('is idempotent — calling close when already closed keeps isOpen false', () => {
      renderWithProvider()
      fireEvent.click(screen.getByTestId('btn-close'))
      expect(probe().dataset.open).toBe('false')
    })
  })

  describe('toggle()', () => {
    it('opens when currently closed', () => {
      renderWithProvider()
      fireEvent.click(screen.getByTestId('btn-toggle'))
      expect(probe().dataset.open).toBe('true')
    })

    it('closes when currently open', () => {
      renderWithProvider()
      fireEvent.click(screen.getByTestId('btn-open'))
      fireEvent.click(screen.getByTestId('btn-toggle'))
      expect(probe().dataset.open).toBe('false')
    })

    it('toggles back and forth correctly', () => {
      renderWithProvider()
      const btn = screen.getByTestId('btn-toggle')
      fireEvent.click(btn)
      expect(probe().dataset.open).toBe('true')
      fireEvent.click(btn)
      expect(probe().dataset.open).toBe('false')
      fireEvent.click(btn)
      expect(probe().dataset.open).toBe('true')
    })
  })

  describe('Cmd+K / Ctrl+K keyboard shortcut', () => {
    it('toggles isOpen on Cmd+K (metaKey)', () => {
      renderWithProvider()
      act(() => {
        fireEvent.keyDown(document, { key: 'k', metaKey: true })
      })
      expect(probe().dataset.open).toBe('true')
    })

    it('toggles isOpen on Ctrl+K (ctrlKey)', () => {
      renderWithProvider()
      act(() => {
        fireEvent.keyDown(document, { key: 'k', ctrlKey: true })
      })
      expect(probe().dataset.open).toBe('true')
    })

    it('toggles closed on second Cmd+K', () => {
      renderWithProvider()
      act(() => { fireEvent.keyDown(document, { key: 'k', metaKey: true }) })
      act(() => { fireEvent.keyDown(document, { key: 'k', metaKey: true }) })
      expect(probe().dataset.open).toBe('false')
    })

    it('does not toggle on plain k keypress', () => {
      renderWithProvider()
      act(() => { fireEvent.keyDown(document, { key: 'k' }) })
      expect(probe().dataset.open).toBe('false')
    })

    it('removes the global keydown listener on unmount (no state update after unmount)', () => {
      const { unmount } = renderWithProvider()
      unmount()
      // Should not throw when dispatching after unmount
      expect(() => {
        act(() => { fireEvent.keyDown(document, { key: 'k', metaKey: true }) })
      }).not.toThrow()
    })
  })

  describe('Escape key', () => {
    it('closes the palette when open', () => {
      renderWithProvider()
      // Open it first
      fireEvent.click(screen.getByTestId('btn-open'))
      expect(probe().dataset.open).toBe('true')

      act(() => { fireEvent.keyDown(document, { key: 'Escape' }) })
      expect(probe().dataset.open).toBe('false')
    })

    it('does nothing when palette is already closed', () => {
      renderWithProvider()
      act(() => { fireEvent.keyDown(document, { key: 'Escape' }) })
      expect(probe().dataset.open).toBe('false')
    })
  })

  describe('useCommandPalette — usage outside provider', () => {
    it('throws when used outside CommandPaletteProvider', () => {
      // Suppress the expected React error boundary console output
      const spy = jest.spyOn(console, 'error').mockImplementation(() => {})
      expect(() => {
        renderHook(() => useCommandPalette())
      }).toThrow('useCommandPalette must be used within a CommandPaletteProvider')
      spy.mockRestore()
    })
  })
})
