import {
  useCodeMirrorStateContext,
  useCodeMirrorViewContext,
} from '@/features/source-editor/components/codemirror-context'
import { usePermissionsContext } from '@/features/ide-react/context/permissions-context'
import { useEditorPropertiesContext } from '@/features/ide-react/context/editor-properties-context'
import { useProjectContext } from '@/shared/context/project-context'
import useSynctex from '@/features/pdf-preview/hooks/use-synctex'
import { useDetachCompileContext } from '@/shared/context/detach-compile-context'
import { useLayoutContext } from '@/shared/context/layout-context'
import { useFeatureFlag } from '@/shared/context/split-test-context'
import { useTranslation } from 'react-i18next'
import { useCallback, useEffect, useRef } from 'react'
import {
  formatShortcut,
  useCommandRegistry,
} from '@/features/ide-react/context/command-registry-context'
import { closeContextMenuEffect } from '../extensions/context-menu'
import * as commands from '../extensions/toolbar/commands'
import {
  cutSelection,
  copySelection,
  pasteWithoutFormatting,
  pasteWithFormatting,
} from '../commands/clipboard'
import { showClipboardPasteErrorToast } from '../components/clipboard-toasts'
import { isVisual } from '../extensions/visual/visual'
import { useEditorContext } from '@/shared/context/editor-context'
import { useTrackingChangesMode } from '@/shared/hooks/use-tracking-changes-mode'
import {
  sendContextMenuEvent,
  ContextMenuItemSegmentation,
} from '../utils/context-menu-analytics'
import { isCursorOnEmptyLine } from '../utils/is-cursor-on-empty-line'

export const useContextMenuItems = () => {
  const view = useCodeMirrorViewContext()
  const state = useCodeMirrorStateContext()
  const permissions = usePermissionsContext()
  const { wantTrackChanges } = useEditorPropertiesContext()
  const { syncToPdf, syncToPdfInFlight, canSyncToPdf } = useSynctex()
  const { pdfUrl, pdfViewer } = useDetachCompileContext()
  const {
    detachRole,
    changeLayout,
    pdfLayout,
    view: ideView,
  } = useLayoutContext()
  const visualPreviewEnabled = useFeatureFlag('visual-preview')
  const { t } = useTranslation()
  const { shortcuts } = useCommandRegistry()
  const { features } = useProjectContext()
  const requestedPdfSyncRef = useRef(false)
  const { setUpgradeTrackChangesModal } = useEditorContext()
  const trackingChangesMode = useTrackingChangesMode()
  const isReview = trackingChangesMode === 'review'

  const closeMenu = useCallback(() => {
    view.dispatch({ effects: closeContextMenuEffect.of(null) })
    view.focus()
  }, [view])

  // Handle closing the menu when it loses focus, e.g. click outside the editor
  const onToggle = (show: boolean) => {
    if (!show) {
      // Skip closing if a sync to PDF is in flight
      if (requestedPdfSyncRef.current) {
        return
      }

      closeMenu()
    }
  }

  // Wait for syncToPdf to finish before closing the menu
  useEffect(() => {
    if (requestedPdfSyncRef.current && !syncToPdfInFlight) {
      closeMenu()
      // Clear the synchronous flag when the close completes
      requestedPdfSyncRef.current = false
    }
  }, [syncToPdfInFlight, closeMenu])

  const hasSelection = !state.selection.main.empty
  const canEdit = permissions.write || permissions.trackedWrite

  // Determine layout states for PDF sync functionality
  const isPdfDetached = detachRole === 'detacher'
  const isEditorOnly =
    pdfLayout === 'flat' && ideView === 'editor' && !isPdfDetached

  const jumpToLocationInPdfEnabled =
    pdfUrl && pdfViewer !== 'native' && !visualPreviewEnabled && canSyncToPdf

  const wrapForContextMenu = useCallback(
    (
      item: ContextMenuItemSegmentation,
      command: () => Promise<boolean> | boolean
    ) =>
      async () => {
        sendContextMenuEvent('menu-click', {
          location: 'editor-context-menu',
          item,
        })
        const result = await command()
        if (result !== false) {
          view.focus()
          closeMenu()
        }
      },
    [view, closeMenu]
  )

  const getShortcut = useCallback(
    (id: string) => {
      const shortcut = shortcuts[id]?.[0]
      return shortcut ? formatShortcut(shortcut) : undefined
    },
    [shortcuts]
  )

  const inVisualMode = isVisual(view)

  const handleCut = wrapForContextMenu('cut', () => cutSelection(view))
  const handleCopy = wrapForContextMenu('copy', () => copySelection(view))
  const handlePaste = wrapForContextMenu('paste', async () => {
    const result = await (inVisualMode
      ? pasteWithFormatting(view)
      : pasteWithoutFormatting(view))
    if (result === false) {
      showClipboardPasteErrorToast(getShortcut('paste'))
    }
    return result
  })
  const handlePasteSpecial = wrapForContextMenu(
    inVisualMode ? 'paste-without-formatting' : 'paste-with-formatting',
    async () => {
      const result = await (inVisualMode
        ? pasteWithoutFormatting(view)
        : pasteWithFormatting(view))
      if (result === false) {
        showClipboardPasteErrorToast(getShortcut('paste'))
      }
      return result
    }
  )
  const handleDelete = wrapForContextMenu('delete', () =>
    commands.deleteSelection(view)
  )

  const handleToggleTrackChanges = wrapForContextMenu(
    wantTrackChanges ? 'back-to-editing' : 'suggest-edits',
    () => {
      // Matching the logic in review toggle to ensure consistency for server pro
      if (!features.trackChanges && !isReview) {
        setUpgradeTrackChangesModal({
          show: true,
          location: 'editor-context-menu',
        })
        return true
      }
      window.dispatchEvent(new Event('toggle-track-changes'))
      return true
    }
  )

  const handleComment = wrapForContextMenu('comment', () => {
    commands.addComment('editor-context-menu')
    return true
  })

  // Sync-to-PDF is special: it needs to wait for async completion before closing
  const handleSyncToPdf = useCallback(() => {
    // Switch to split view only when in editor-only mode with non-detached PDF
    if (isEditorOnly) {
      changeLayout('sideBySide')
    }

    sendContextMenuEvent('menu-click', {
      location: 'editor-context-menu',
      item: 'jump-to-location-in-pdf',
    })
    sendContextMenuEvent('jump-to-location', {
      method: 'editor-context-menu',
      direction: 'code-location-in-pdf',
    })
    requestedPdfSyncRef.current = true
    syncToPdf()
    view.focus()
  }, [syncToPdf, view, changeLayout, isEditorOnly])

  return {
    closeMenu,
    onToggle,
    menuItems: [
      {
        label: t('cut'),
        handler: handleCut,
        disabled: false,
        show: canEdit,
        shortcut: getShortcut('cut'),
      },
      {
        label: t('copy'),
        handler: handleCopy,
        disabled: false,
        show: true,
        shortcut: getShortcut('copy'),
      },
      {
        label: t('paste'),
        handler: handlePaste,
        disabled: false,
        show: canEdit,
        shortcut: getShortcut('paste'),
      },
      {
        label: inVisualMode
          ? t('paste_without_formatting')
          : t('paste_with_formatting'),
        handler: handlePasteSpecial,
        disabled: false,
        show: canEdit,
        shortcut: inVisualMode ? getShortcut('paste-special') : undefined,
      },
      {
        label: t('delete'),
        handler: handleDelete,
        disabled: !hasSelection,
        show: canEdit,
        shortcut: undefined,
      },
      {
        label: t('jump_to_location_in_pdf'),
        handler: handleSyncToPdf,
        disabled: syncToPdfInFlight,
        separatorAbove: true,
        show: jumpToLocationInPdfEnabled,
        shortcut: undefined,
      },
      {
        label: wantTrackChanges ? t('back_to_editing') : t('suggest_edits'),
        handler: handleToggleTrackChanges,
        disabled: false,
        separatorAbove: true,
        show: canEdit && features.trackChangesVisible,
        shortcut: getShortcut('toggle-track-changes'),
      },
      {
        label: t('comment'),
        handler: handleComment,
        disabled: isCursorOnEmptyLine(state),
        show: permissions.comment,
        shortcut: getShortcut('insert-comment'),
      },
    ].filter(item => item.show),
  }
}
