import { createContext, useCallback, useEffect, useRef, useState } from 'react'
import PropTypes from 'prop-types'
import { useNavigate } from 'react-router-dom'
import desktopBridge, {
  isElectrobunBridgeReady,
  isElectrobunDesktop
} from '../../../electrobun-bridge.js'

const electron = window.require ? window.require('electron') : null
const ipcRenderer = electron ? electron.ipcRenderer : null

// eslint-disable-next-line react-refresh/only-export-components
export function isElectron() {
  if (isElectrobunDesktop()) {
    return true
  }

  if (
    typeof window !== 'undefined' &&
    window.process &&
    window.process.type === 'renderer'
  ) {
    return true
  }

  if (
    typeof navigator === 'object' &&
    typeof navigator.userAgent === 'string' &&
    navigator.userAgent.indexOf('Electron') >= 0
  ) {
    return true
  }

  return false
}

const ElectronContext = createContext()

function isInHorizontalScrollContainer(element) {
  let node = element
  while (node && node !== document.body) {
    const { overflowX } = window.getComputedStyle(node)
    if (
      (overflowX === 'auto' || overflowX === 'scroll') &&
      node.scrollWidth > node.clientWidth
    ) {
      return true
    }
    node = node.parentElement
  }
  return false
}

const ElectronProvider = ({ children }) => {
  const [platform, setPlatform] = useState('unknown')
  const [isMaximized, setIsMaximized] = useState(false)
  const [isFullScreen, setIsFullScreen] = useState(false)
  const [electronAvailable] = useState(isElectron())
  const useElectrobun = isElectrobunBridgeReady() || isElectrobunDesktop()
  const navigate = useNavigate()
  const lastNavigationAtRef = useRef(0)

  const navigateHistory = useCallback(
    (direction) => {
      const now = Date.now()
      if (now - lastNavigationAtRef.current < 300) return
      lastNavigationAtRef.current = now

      if (direction === 'back') {
        navigate(-1)
      } else if (direction === 'forward') {
        navigate(1)
      }
    },
    [navigate]
  )

  const applyWindowState = useCallback((state) => {
    if (state && typeof state.isMaximized === 'boolean') {
      setIsMaximized(state.isMaximized)
    }
    if (state && typeof state.isFullScreen === 'boolean') {
      setIsFullScreen(state.isFullScreen)
    }
  }, [])

  const openExternalUrl = (url) => {
    if (useElectrobun) {
      void desktopBridge.openExternalUrl(url).catch((error) => {
        console.warn('[ElectronContext] Failed to open external url:', error)
      })
      return true
    }
    if (electronAvailable && ipcRenderer) {
      ipcRenderer.invoke('open-external-url', url)
      return true
    }
    return false
  }

  const openInternalUrl = (url) => {
    if (useElectrobun) {
      void desktopBridge.openInternalUrl(url).catch((error) => {
        console.warn('[ElectronContext] Failed to open internal url:', error)
      })
      return true
    }
    if (electronAvailable && ipcRenderer) {
      ipcRenderer.invoke('open-internal-url', url)
      return true
    }
    return false
  }

  useEffect(() => {
    if (!electronAvailable) return

    if (useElectrobun) {
      desktopBridge.getOsInfo().then((info) => {
        if (info?.platform) {
          setPlatform(info.platform)
          if (info.platform === 'darwin') {
            document.documentElement.classList.add('macos-vibrancy')
          }
        }
      })

      desktopBridge.getWindowState().then(applyWindowState)

      const unsubWindowState = desktopBridge.onMessage(
        'windowState',
        applyWindowState
      )
      const unsubNavigate = desktopBridge.onMessage('navigate', (url) => {
        if (url.toLowerCase() == '/favicon.ico') {
          return
        }
        console.log('[ElectronContext] Navigating to:', url)
        navigate(url)
      })
      const unsubNavigationGesture = desktopBridge.onMessage(
        'navigationGesture',
        navigateHistory
      )

      return () => {
        unsubWindowState()
        unsubNavigate()
        unsubNavigationGesture()
      }
    }

    if (!ipcRenderer) return

    ipcRenderer.invoke('os-info').then((info) => {
      if (info?.platform) {
        setPlatform(info.platform)
        if (info.platform === 'darwin') {
          document.documentElement.classList.add('macos-vibrancy')
        }
      }
    })

    ipcRenderer.invoke('window-state').then(applyWindowState)

    const windowStateHandler = (_event, state) => {
      applyWindowState(state)
    }
    ipcRenderer.on('window-state', windowStateHandler)

    const navigateHandler = (_event, url) => {
      if (url.toLowerCase() == '/favicon.ico') {
        return
      }
      console.log('[ElectronContext] Navigating to:', url)
      navigate(url)
    }
    ipcRenderer.on('navigate', navigateHandler)

    const navigationGestureHandler = (_event, direction) => {
      navigateHistory(direction)
    }
    ipcRenderer.on('navigation-gesture', navigationGestureHandler)

    return () => {
      ipcRenderer.removeListener('navigate', navigateHandler)
      ipcRenderer.removeListener('navigation-gesture', navigationGestureHandler)
      ipcRenderer.removeListener('window-state', windowStateHandler)
    }
  }, [
    applyWindowState,
    electronAvailable,
    navigate,
    navigateHistory,
    useElectrobun
  ])

  useEffect(() => {
    if (!electronAvailable || platform !== 'darwin') return

    let accumulatedDeltaX = 0
    let resetTimer

    const handleWheel = (event) => {
      if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey)
        return
      if (Math.abs(event.deltaX) < Math.abs(event.deltaY)) return
      if (Math.abs(event.deltaX) < 2) return
      if (isInHorizontalScrollContainer(event.target)) return

      accumulatedDeltaX += event.deltaX
      clearTimeout(resetTimer)
      resetTimer = setTimeout(() => {
        if (Math.abs(accumulatedDeltaX) > 60) {
          navigateHistory(accumulatedDeltaX > 0 ? 'forward' : 'back')
        }
        accumulatedDeltaX = 0
      }, 80)
    }

    window.addEventListener('wheel', handleWheel, { passive: true })

    return () => {
      window.removeEventListener('wheel', handleWheel)
      clearTimeout(resetTimer)
    }
  }, [electronAvailable, navigateHistory, platform])

  const handleWindowControl = (action) => {
    if (useElectrobun) {
      void desktopBridge.windowControl(action)
      return
    }
    if (electronAvailable && ipcRenderer) {
      ipcRenderer.send('window-control', action)
    }
  }

  const getAuthSession = async () => {
    if (!electronAvailable) return null
    if (useElectrobun) return await desktopBridge.getAuthSession()
    if (!ipcRenderer) return null
    return await ipcRenderer.invoke('auth-session-get')
  }

  const setAuthSession = async (session) => {
    if (!electronAvailable) return false
    if (useElectrobun) {
      const result = await desktopBridge.setAuthSession(session)
      return result?.ok ?? false
    }
    if (!ipcRenderer) return false
    return await ipcRenderer.invoke('auth-session-set', session)
  }

  const clearAuthSession = async () => {
    if (!electronAvailable) return false
    if (useElectrobun) {
      const result = await desktopBridge.clearAuthSession()
      return result?.ok ?? false
    }
    if (!ipcRenderer) return false
    return await ipcRenderer.invoke('auth-session-clear')
  }

  const getAppSettings = useCallback(async () => {
    if (!electronAvailable) return {}
    if (useElectrobun) return await desktopBridge.getAppSettings()
    if (!ipcRenderer) return {}
    return await ipcRenderer.invoke('app-settings-get')
  }, [electronAvailable, useElectrobun])

  const setAppSettings = useCallback(
    async (settings) => {
      if (!electronAvailable) return false
      if (useElectrobun) {
        const result = await desktopBridge.setAppSettings(settings)
        return result?.ok ?? false
      }
      if (!ipcRenderer) return false
      return await ipcRenderer.invoke('app-settings-set', settings)
    },
    [electronAvailable, useElectrobun]
  )

  const startAppUpdate = useCallback(
    async (update) => {
      if (!electronAvailable) return false
      if (useElectrobun) {
        const result = await desktopBridge.startAppUpdate(update)
        return result?.ok ?? false
      }
      if (!ipcRenderer) return false
      return await ipcRenderer.invoke('app-update-start', update)
    },
    [electronAvailable, useElectrobun]
  )

  const checkAppUpdateResult = useCallback(async () => {
    if (!electronAvailable || !useElectrobun) return null
    return await desktopBridge.checkAppUpdateResult()
  }, [electronAvailable, useElectrobun])

  const checkDuplicateInstallations = useCallback(async () => {
    if (!electronAvailable || !useElectrobun) return null
    return await desktopBridge.checkDuplicateInstallations()
  }, [electronAvailable, useElectrobun])

  const removeDuplicateInstallations = useCallback(async () => {
    if (!electronAvailable || !useElectrobun) return false
    const result = await desktopBridge.removeDuplicateInstallations()
    return result?.ok ?? false
  }, [electronAvailable, useElectrobun])

  const onDuplicateInstallationsRemoved = useCallback(
    (handler) => {
      if (
        !electronAvailable ||
        !useElectrobun ||
        typeof handler !== 'function'
      ) {
        return () => {}
      }
      return desktopBridge.onMessage('duplicateInstallationsRemoved', handler)
    },
    [electronAvailable, useElectrobun]
  )

  const onAppUpdateProgress = useCallback(
    (handler) => {
      if (!electronAvailable || typeof handler !== 'function') {
        return () => {}
      }

      if (useElectrobun) {
        return desktopBridge.onMessage('appUpdateProgress', handler)
      }

      if (!ipcRenderer) return () => {}

      const progressHandler = (_event, progress) => {
        handler(progress)
      }

      ipcRenderer.on('app-update-progress', progressHandler)

      return () => {
        ipcRenderer.removeListener('app-update-progress', progressHandler)
      }
    },
    [electronAvailable, useElectrobun]
  )

  const onCheckForUpdatesRequest = useCallback(
    (handler) => {
      if (!electronAvailable || typeof handler !== 'function') {
        return () => {}
      }

      if (useElectrobun) {
        return desktopBridge.onMessage('checkForUpdates', handler)
      }

      if (!ipcRenderer) return () => {}

      const checkHandler = () => {
        handler()
      }

      ipcRenderer.on('check-for-updates', checkHandler)

      return () => {
        ipcRenderer.removeListener('check-for-updates', checkHandler)
      }
    },
    [electronAvailable, useElectrobun]
  )

  const getToken = async () => {
    const session = await getAuthSession()
    return session?.token || null
  }

  const setToken = async (token) => {
    const session = (await getAuthSession()) || {}
    return await setAuthSession({ ...session, token })
  }

  const resizeSpotlightWindow = async (height) => {
    if (!electronAvailable) return false
    try {
      if (useElectrobun) {
        const result = await desktopBridge.resizeSpotlightWindow(height)
        return result?.ok ?? false
      }
      if (!ipcRenderer) return false
      return await ipcRenderer.invoke('spotlight-window-resize', height)
    } catch (error) {
      console.warn(
        '[ElectronContext] Failed to resize spotlight window:',
        error
      )
      return false
    }
  }

  const setSidebarViewMenu = useCallback(
    async (sections) => {
      if (!electronAvailable) return false
      if (useElectrobun) {
        const result = await desktopBridge.setSidebarViewMenu(sections)
        return result?.ok ?? false
      }
      if (!ipcRenderer) return false
      return await ipcRenderer.invoke('set-sidebar-view-menu', sections)
    },
    [electronAvailable, useElectrobun]
  )

  const getElectronVersion = useCallback(async () => {
    if (!electronAvailable) return null
    if (useElectrobun) return await desktopBridge.getAppVersion()
    if (!ipcRenderer) return null
    return await ipcRenderer.invoke('electron-version')
  }, [electronAvailable, useElectrobun])

  const getAppEngine = useCallback(async () => {
    if (!electronAvailable) return 'native'
    if (useElectrobun) {
      const engine = await desktopBridge.getAppEngine()
      return engine === 'chromium' ? 'chromium' : 'native'
    }
    return 'native'
  }, [electronAvailable, useElectrobun])

  return (
    <ElectronContext.Provider
      value={{
        platform,
        isMaximized,
        isFullScreen,
        isElectron: electronAvailable,
        handleWindowControl,
        openExternalUrl,
        openInternalUrl,
        getAuthSession,
        setAuthSession,
        clearAuthSession,
        getAppSettings,
        setAppSettings,
        startAppUpdate,
        checkAppUpdateResult,
        checkDuplicateInstallations,
        removeDuplicateInstallations,
        onDuplicateInstallationsRemoved,
        onAppUpdateProgress,
        onCheckForUpdatesRequest,
        getToken,
        setToken,
        resizeSpotlightWindow,
        setSidebarViewMenu,
        getElectronVersion,
        getAppEngine
      }}
    >
      {children}
    </ElectronContext.Provider>
  )
}

ElectronProvider.propTypes = {
  children: PropTypes.node.isRequired
}

export { ElectronContext, ElectronProvider }
