import { Editor } from '@tiptap/core'
import Document from '@tiptap/extension-document'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import { afterEach, beforeEach, describe, expect, it } from 'vite-plus/test'

import { DragHandle } from './drag-handle.js'

describe('DragHandle', () => {
  let editor: Editor
  let dragHandleElement: HTMLElement

  beforeEach(() => {
    dragHandleElement = document.createElement('div')
    dragHandleElement.classList.add('drag-handle')

    editor = new Editor({
      element: document.body,
      extensions: [
        Document,
        Paragraph,
        Text,
        DragHandle.configure({
          render: () => dragHandleElement,
        }),
      ],
      content: '<p>Hello World</p>',
    })
  })

  afterEach(() => {
    editor.destroy()
  })

  describe('data-dragging attribute', () => {
    it('should initialize data-dragging attribute to false', () => {
      expect(dragHandleElement.dataset.dragging).toBe('false')
    })

    it('should set data-dragging to true on dragstart', () => {
      const dragStartEvent = new DragEvent('dragstart', {
        bubbles: true,
        cancelable: true,
        dataTransfer: new DataTransfer(),
      })

      dragHandleElement.dispatchEvent(dragStartEvent)

      expect(dragHandleElement.dataset.dragging).toBe('true')
    })

    it('should set data-dragging to false on dragend', () => {
      const dragStartEvent = new DragEvent('dragstart', {
        bubbles: true,
        cancelable: true,
        dataTransfer: new DataTransfer(),
      })

      dragHandleElement.dispatchEvent(dragStartEvent)

      expect(dragHandleElement.dataset.dragging).toBe('true')

      const dragEndEvent = new DragEvent('dragend', {
        bubbles: true,
        cancelable: true,
      })

      dragHandleElement.dispatchEvent(dragEndEvent)

      expect(dragHandleElement.dataset.dragging).toBe('false')
    })

    it('should maintain data-dragging state through multiple drag cycles', () => {
      const dataTransfer = new DataTransfer()

      for (let i = 0; i < 3; i += 1) {
        const dragStartEvent = new DragEvent('dragstart', {
          bubbles: true,
          cancelable: true,
          dataTransfer,
        })

        dragHandleElement.dispatchEvent(dragStartEvent)
        expect(dragHandleElement.dataset.dragging).toBe('true')

        const dragEndEvent = new DragEvent('dragend', {
          bubbles: true,
          cancelable: true,
        })

        dragHandleElement.dispatchEvent(dragEndEvent)
        expect(dragHandleElement.dataset.dragging).toBe('false')
      }
    })
  })

  describe('element properties', () => {
    it('should have draggable set to true', () => {
      expect(dragHandleElement.draggable).toBe(true)
    })

    it('should have pointer events set to auto initially', () => {
      expect(dragHandleElement.style.pointerEvents).toBe('auto')
    })

    it('should render the handle wrapper above editor decorations via a z-index', () => {
      // The wrapper competes in the stacking context with positioned editor
      // content and decorations (for example the Pages page header/footer chrome
      // at z-index 2). Without a z-index the handle is painted behind that chrome
      // and becomes impossible to click, so the wrapper must assert its own.
      const wrapper = dragHandleElement.parentElement

      expect(wrapper).not.toBeNull()
      expect(wrapper?.style.zIndex).toBe('10')
    })
  })
})
