import { isTouchDevice } from '@/lib/utils'
import { Event } from 'nostr-tools'
import {
  createContext,
  ReactNode,
  RefObject,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState
} from 'react'
import modalManager from '@/services/modal-manager.service'
import { useScreenSize } from './ScreenSizeProvider'
import { useUserPreferences } from './UserPreferencesProvider'

export type TNavigationColumn = 0 | 1 | 2 // 0=sidebar, 1=primary, 2=secondary

export type TActionType = 'reply' | 'repost' | 'quote' | 'react' | 'zap'

export type TActionMode = {
  active: boolean
  selectedAction: TActionType | null
  noteEvent: Event | null
}

export type TItemMeta = {
  type: 'sidebar' | 'note' | 'settings'
  event?: Event
  onActivate?: () => void
}

type TRegisteredItem = {
  ref: RefObject<HTMLElement>
  meta?: TItemMeta
}

type TSettingsHandlers = {
  onUp: () => void
  onDown: () => void
  onEnter: () => void
  onEscape: () => boolean // return true if handled
}

type TKeyboardNavigationContext = {
  // Column focus
  activeColumn: TNavigationColumn
  setActiveColumn: (column: TNavigationColumn) => void

  // Item selection per column
  selectedIndex: Record<TNavigationColumn, number>
  setSelectedIndex: (column: TNavigationColumn, index: number) => void
  resetPrimarySelection: () => void
  offsetSelection: (column: TNavigationColumn, offset: number) => void
  clearColumn: (column: TNavigationColumn) => void

  // Registered items per column
  registerItem: (
    column: TNavigationColumn,
    index: number,
    ref: RefObject<HTMLElement>,
    meta?: TItemMeta
  ) => void
  unregisterItem: (column: TNavigationColumn, index: number) => void
  getItemCount: (column: TNavigationColumn) => number

  // Action mode
  actionMode: TActionMode
  enterActionMode: (noteEvent: Event) => void
  exitActionMode: () => void
  cycleAction: (direction?: 1 | -1) => void

  // Visual state
  isItemSelected: (column: TNavigationColumn, index: number) => boolean

  // Settings accordion
  openAccordionItem: string | null
  setOpenAccordionItem: (value: string | null) => void

  // Settings page handlers
  registerSettingsHandlers: (handlers: TSettingsHandlers) => void
  unregisterSettingsHandlers: () => void

  // Keyboard nav enabled
  isEnabled: boolean
}

const ACTIONS: TActionType[] = ['reply', 'repost', 'quote', 'react', 'zap']

const KeyboardNavigationContext = createContext<TKeyboardNavigationContext | undefined>(undefined)

export function useKeyboardNavigation() {
  const context = useContext(KeyboardNavigationContext)
  if (!context) {
    throw new Error('useKeyboardNavigation must be used within KeyboardNavigationProvider')
  }
  return context
}

// Helper to check if an input element is focused
function isInputFocused(): boolean {
  const activeElement = document.activeElement
  if (!activeElement) return false
  const tagName = activeElement.tagName.toLowerCase()
  return (
    tagName === 'input' ||
    tagName === 'textarea' ||
    activeElement.getAttribute('contenteditable') === 'true'
  )
}

export function KeyboardNavigationProvider({
  children,
  secondaryStackLength,
  sidebarDrawerOpen,
  onBack,
  onCloseSecondary
}: {
  children: ReactNode
  secondaryStackLength: number
  sidebarDrawerOpen: boolean
  onBack?: () => void
  onCloseSecondary?: () => void
}) {
  const { isSmallScreen } = useScreenSize()
  const { enableSingleColumnLayout } = useUserPreferences()

  const [activeColumn, setActiveColumn] = useState<TNavigationColumn>(1)
  const [selectedIndex, setSelectedIndexState] = useState<Record<TNavigationColumn, number>>({
    0: 0,
    1: 0,
    2: 0
  })
  const [actionMode, setActionMode] = useState<TActionMode>({
    active: false,
    selectedAction: null,
    noteEvent: null
  })
  const [openAccordionItem, setOpenAccordionItem] = useState<string | null>(null)
  const [isEnabled, setIsEnabled] = useState(false)

  // Item registration per column
  const itemsRef = useRef<Record<TNavigationColumn, Map<number, TRegisteredItem>>>({
    0: new Map(),
    1: new Map(),
    2: new Map()
  })

  // Settings page handlers
  const settingsHandlersRef = useRef<TSettingsHandlers | null>(null)

  const registerSettingsHandlers = useCallback((handlers: TSettingsHandlers) => {
    settingsHandlersRef.current = handlers
  }, [])

  const unregisterSettingsHandlers = useCallback(() => {
    settingsHandlersRef.current = null
  }, [])

  const setSelectedIndex = useCallback((column: TNavigationColumn, index: number) => {
    setSelectedIndexState((prev) => ({
      ...prev,
      [column]: index
    }))
  }, [])

  const resetPrimarySelection = useCallback(() => {
    setSelectedIndex(1, 0)
    // Also switch focus to primary column
    setActiveColumn(1)
  }, [setSelectedIndex])

  const offsetSelection = useCallback(
    (column: TNavigationColumn, offset: number) => {
      setSelectedIndexState((prev) => ({
        ...prev,
        [column]: Math.max(0, prev[column] + offset)
      }))
    },
    []
  )

  const clearColumn = useCallback((column: TNavigationColumn) => {
    itemsRef.current[column].clear()
    setSelectedIndexState((prev) => ({
      ...prev,
      [column]: 0
    }))
  }, [])

  const registerItem = useCallback(
    (column: TNavigationColumn, index: number, ref: RefObject<HTMLElement>, meta?: TItemMeta) => {
      itemsRef.current[column].set(index, { ref, meta })
    },
    []
  )

  const unregisterItem = useCallback((column: TNavigationColumn, index: number) => {
    itemsRef.current[column].delete(index)
  }, [])

  const getItemCount = useCallback((column: TNavigationColumn) => {
    return itemsRef.current[column].size
  }, [])

  const isItemSelected = useCallback(
    (column: TNavigationColumn, index: number) => {
      return isEnabled && activeColumn === column && selectedIndex[column] === index
    },
    [isEnabled, activeColumn, selectedIndex]
  )

  const getAvailableColumns = useCallback((): TNavigationColumn[] => {
    if (isSmallScreen || enableSingleColumnLayout) {
      // Single column mode
      if (sidebarDrawerOpen) return [0]
      if (secondaryStackLength > 0) return [2]
      return [1]
    }
    // Desktop 2-column mode
    if (secondaryStackLength > 0) return [0, 1, 2]
    return [0, 1]
  }, [isSmallScreen, enableSingleColumnLayout, sidebarDrawerOpen, secondaryStackLength])

  const moveColumn = useCallback(
    (direction: 1 | -1) => {
      const available = getAvailableColumns()
      const currentIdx = available.indexOf(activeColumn)
      if (currentIdx === -1) {
        setActiveColumn(available[0])
        return
      }
      const newIdx = Math.max(0, Math.min(available.length - 1, currentIdx + direction))
      setActiveColumn(available[newIdx])
    },
    [activeColumn, getAvailableColumns]
  )

  const scrollItemIntoView = useCallback(
    (ref: HTMLElement, direction: 'up' | 'down', isAtEdge = false) => {
      // At edges, use start/end to ensure item is fully visible
      // Otherwise use 'nearest' to minimize scrolling
      ref.scrollIntoView({
        behavior: 'smooth',
        block: isAtEdge ? (direction === 'up' ? 'start' : 'end') : 'nearest'
      })
    },
    []
  )

  const moveItem = useCallback(
    (direction: 1 | -1) => {
      const items = itemsRef.current[activeColumn]
      if (items.size === 0) return

      // Get sorted indices
      const indices = Array.from(items.keys()).sort((a, b) => a - b)
      if (indices.length === 0) return

      const currentSelected = selectedIndex[activeColumn]
      let currentIdx = indices.indexOf(currentSelected)
      let newIdx: number

      if (currentIdx === -1) {
        // Selection not found - find the nearest valid index
        // This can happen when items are filtered/hidden or list changes
        let nearestIdx = 0
        let minDistance = Infinity
        for (let i = 0; i < indices.length; i++) {
          const distance = Math.abs(indices[i] - currentSelected)
          if (distance < minDistance) {
            minDistance = distance
            nearestIdx = i
          }
        }
        // Adjust based on direction: if going up, prefer index below target; if going down, prefer index above
        if (direction === -1 && indices[nearestIdx] > currentSelected && nearestIdx > 0) {
          nearestIdx--
        } else if (direction === 1 && indices[nearestIdx] < currentSelected && nearestIdx < indices.length - 1) {
          nearestIdx++
        }
        currentIdx = nearestIdx
        // Set selection to nearest valid index immediately
        const nearestItemIndex = indices[currentIdx]
        if (nearestItemIndex !== undefined) {
          setSelectedIndex(activeColumn, nearestItemIndex)
          const item = items.get(nearestItemIndex)
          if (item?.ref.current) {
            scrollItemIntoView(item.ref.current, direction === -1 ? 'up' : 'down', false)
          }
        }
        return
      }

      // Clamp to valid range (no wrap-around)
      newIdx = Math.max(0, Math.min(indices.length - 1, currentIdx + direction))

      const newItemIndex = indices[newIdx]
      if (newItemIndex === undefined) return

      setSelectedIndex(activeColumn, newItemIndex)

      // Check if at edge
      const isAtEdge = newIdx === 0 || newIdx === indices.length - 1

      // Scroll into view
      const item = items.get(newItemIndex)
      if (item?.ref.current) {
        scrollItemIntoView(item.ref.current, direction === -1 ? 'up' : 'down', isAtEdge)
      }
    },
    [activeColumn, selectedIndex, setSelectedIndex, scrollItemIntoView]
  )

  const jumpToEdge = useCallback(
    (edge: 'top' | 'bottom') => {
      const items = itemsRef.current[activeColumn]
      if (items.size === 0) return

      // Get sorted indices
      const indices = Array.from(items.keys()).sort((a, b) => a - b)
      if (indices.length === 0) return

      const newIdx = edge === 'top' ? 0 : indices.length - 1
      const newItemIndex = indices[newIdx]
      if (newItemIndex === undefined) return

      setSelectedIndex(activeColumn, newItemIndex)

      // Scroll into view (always at edge for jumpToEdge)
      const item = items.get(newItemIndex)
      if (item?.ref.current) {
        scrollItemIntoView(item.ref.current, edge === 'top' ? 'up' : 'down', true)
      }
    },
    [activeColumn, setSelectedIndex, scrollItemIntoView]
  )

  const enterActionMode = useCallback((noteEvent: Event) => {
    setActionMode({
      active: true,
      selectedAction: 'reply',
      noteEvent
    })
  }, [])

  const exitActionMode = useCallback(() => {
    setActionMode({
      active: false,
      selectedAction: null,
      noteEvent: null
    })
  }, [])

  const cycleAction = useCallback(
    (direction: 1 | -1 = 1) => {
      setActionMode((prev) => {
        if (!prev.active) {
          // Enter action mode
          const item = itemsRef.current[activeColumn].get(selectedIndex[activeColumn])
          if (item?.meta?.type === 'note' && item.meta.event) {
            return {
              active: true,
              selectedAction: 'reply',
              noteEvent: item.meta.event
            }
          }
          return prev
        }

        const currentIdx = prev.selectedAction ? ACTIONS.indexOf(prev.selectedAction) : 0
        const newIdx = (currentIdx + direction + ACTIONS.length) % ACTIONS.length
        return {
          ...prev,
          selectedAction: ACTIONS[newIdx]
        }
      })
    },
    [activeColumn, selectedIndex]
  )

  const handleEnter = useCallback(() => {
    if (actionMode.active) {
      // Execute the selected action
      const item = itemsRef.current[activeColumn].get(selectedIndex[activeColumn])
      if (item?.ref.current && actionMode.selectedAction) {
        const stuffStats = item.ref.current.querySelector('[data-stuff-stats]')
        const actionButton = stuffStats?.querySelector(
          `[data-action="${actionMode.selectedAction}"]`
        ) as HTMLButtonElement | null
        actionButton?.click()
        exitActionMode()
      }
      return
    }

    // Activate the current item
    const item = itemsRef.current[activeColumn].get(selectedIndex[activeColumn])
    if (!item) return

    // If activating a sidebar item, reset column 1 selection and switch focus
    if (activeColumn === 0 && item.meta?.type === 'sidebar') {
      setSelectedIndex(1, 0)
      setActiveColumn(1)
    }

    if (item.meta?.onActivate) {
      item.meta.onActivate()
    } else if (item.ref.current) {
      // Click the element
      item.ref.current.click()
    }
  }, [activeColumn, selectedIndex, actionMode, exitActionMode, setSelectedIndex])

  const handleEscape = useCallback(() => {
    if (actionMode.active) {
      exitActionMode()
      return
    }

    // Settings: close accordion
    if (openAccordionItem) {
      setOpenAccordionItem(null)
      return
    }

    // Single column/mobile: go back
    if ((isSmallScreen || enableSingleColumnLayout) && secondaryStackLength > 0) {
      onBack?.()
      return
    }

    // Third column: close all secondary pages and return to primary column
    if (activeColumn === 2 && secondaryStackLength > 0) {
      onCloseSecondary?.()
      setActiveColumn(1)
      return
    }

    // Go to sidebar in all column views
    if (activeColumn !== 0) {
      setActiveColumn(0)
      // Reset sidebar selection to ensure valid item is selected
      setSelectedIndex(0, 0)
    }
  }, [
    actionMode.active,
    exitActionMode,
    openAccordionItem,
    isSmallScreen,
    enableSingleColumnLayout,
    secondaryStackLength,
    onBack,
    onCloseSecondary,
    activeColumn,
    setSelectedIndex
  ])

  // Enable keyboard nav on first arrow key press (disabled on touch devices)
  useEffect(() => {
    // Don't enable keyboard navigation on touch devices
    if (isTouchDevice()) return

    const handleFirstKeyPress = (e: KeyboardEvent) => {
      if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) {
        setIsEnabled(true)
      }
    }

    if (!isEnabled) {
      window.addEventListener('keydown', handleFirstKeyPress)
      return () => window.removeEventListener('keydown', handleFirstKeyPress)
    }
  }, [isEnabled])

  // Main keyboard handler
  useEffect(() => {
    if (!isEnabled) return

    const handleKeyDown = (e: KeyboardEvent) => {
      // Skip if in input or modal open
      if (isInputFocused()) return
      if (modalManager.hasOpenModal?.()) return

      // Check for settings handlers first
      const settingsHandlers = settingsHandlersRef.current

      switch (e.key) {
        case 'ArrowLeft':
          e.preventDefault()
          // Left arrow: column 2 -> column 1, column 1 -> column 0
          if (activeColumn === 2) {
            setActiveColumn(1)
          } else if (activeColumn === 1) {
            setActiveColumn(0)
          }
          break
        case 'ArrowRight':
          e.preventDefault()
          moveColumn(1)
          break
        case 'ArrowUp':
          e.preventDefault()
          // Only use settings handlers when on column 1 (primary)
          if (settingsHandlers && activeColumn === 1) {
            settingsHandlers.onUp()
          } else {
            moveItem(-1)
          }
          break
        case 'ArrowDown':
          e.preventDefault()
          // Only use settings handlers when on column 1 (primary)
          if (settingsHandlers && activeColumn === 1) {
            settingsHandlers.onDown()
          } else {
            moveItem(1)
          }
          break
        case 'PageUp':
          e.preventDefault()
          jumpToEdge('top')
          break
        case 'PageDown':
          e.preventDefault()
          jumpToEdge('bottom')
          break
        case 'Tab':
          // Only intercept Tab for action mode on notes
          {
            const item = itemsRef.current[activeColumn].get(selectedIndex[activeColumn])
            if (item?.meta?.type === 'note') {
              e.preventDefault()
              cycleAction(e.shiftKey ? -1 : 1)
            }
          }
          break
        case 'Enter':
          e.preventDefault()
          // Only use settings handlers when on column 1 (primary)
          if (settingsHandlers && activeColumn === 1) {
            settingsHandlers.onEnter()
          } else {
            handleEnter()
          }
          break
        case 'Escape':
          e.preventDefault()
          // Only use settings handlers when on column 1 (primary)
          if (settingsHandlers && activeColumn === 1) {
            const handled = settingsHandlers.onEscape()
            if (!handled) {
              handleEscape()
            }
          } else {
            handleEscape()
          }
          break
        case 'Backspace':
          e.preventDefault()
          // Navigate back (like browser back button)
          onBack?.()
          break
      }
    }

    window.addEventListener('keydown', handleKeyDown)
    return () => window.removeEventListener('keydown', handleKeyDown)
  }, [
    isEnabled,
    moveColumn,
    moveItem,
    jumpToEdge,
    cycleAction,
    handleEnter,
    handleEscape,
    activeColumn,
    selectedIndex,
    onBack
  ])

  // Update active column when layout changes
  useEffect(() => {
    const available = getAvailableColumns()
    if (!available.includes(activeColumn)) {
      setActiveColumn(available[0])
    }
  }, [getAvailableColumns, activeColumn])

  // Auto-switch columns when secondary stack changes
  const prevSecondaryStackLength = useRef(secondaryStackLength)
  useEffect(() => {
    if (secondaryStackLength > prevSecondaryStackLength.current && isEnabled) {
      // Secondary stack grew, switch to column 2
      setActiveColumn(2)
      setSelectedIndex(2, 0)
    } else if (secondaryStackLength < prevSecondaryStackLength.current && isEnabled) {
      // Secondary stack shrank, switch back to column 1
      setActiveColumn(1)
    }
    prevSecondaryStackLength.current = secondaryStackLength
  }, [secondaryStackLength, isEnabled, setSelectedIndex])

  const value = useMemo(
    () => ({
      activeColumn,
      setActiveColumn,
      selectedIndex,
      setSelectedIndex,
      resetPrimarySelection,
      offsetSelection,
      clearColumn,
      registerItem,
      unregisterItem,
      getItemCount,
      actionMode,
      enterActionMode,
      exitActionMode,
      cycleAction,
      isItemSelected,
      openAccordionItem,
      setOpenAccordionItem,
      registerSettingsHandlers,
      unregisterSettingsHandlers,
      isEnabled
    }),
    [
      activeColumn,
      selectedIndex,
      setSelectedIndex,
      resetPrimarySelection,
      offsetSelection,
      clearColumn,
      registerItem,
      unregisterItem,
      getItemCount,
      actionMode,
      enterActionMode,
      exitActionMode,
      cycleAction,
      isItemSelected,
      openAccordionItem,
      registerSettingsHandlers,
      unregisterSettingsHandlers,
      isEnabled
    ]
  )

  return (
    <KeyboardNavigationContext.Provider value={value}>
      {children}
    </KeyboardNavigationContext.Provider>
  )
}
