import {
  createContext,
  Suspense,
  useCallback,
  useContext,
  useEffect,
  useRef,
  useState
} from 'react'
import PropTypes from 'prop-types'
import { Modal, Spin, Flex } from 'antd'
import { LoadingOutlined } from '@ant-design/icons'
import { useLocation, useNavigate } from 'react-router-dom'
import { getModelByName } from '../../../database/ObjectModels'
import {
  buildActionUrl,
  findAction,
  getObjectIdFromSearch,
  stripModalActionParams
} from '../../../utils/modelActions'
import { AuthContext } from './AuthContext'

const ActionsContext = createContext()

const ActionsProvider = ({ children }) => {
  const navigate = useNavigate()
  const location = useLocation()
  const { userProfile } = useContext(AuthContext)
  const [currentObject, setCurrentObject] = useState(null)
  const [currentObjectType, setCurrentObjectType] = useState(null)
  const [modalAction, setModalAction] = useState(null)
  const [onModalOk, setOnModalOk] = useState(null)
  const lastHandledAction = useRef(null)

  const searchParams = new URLSearchParams(location.search)
  const actionName = searchParams.get('action')
  const actionObjectType = searchParams.get('actionObjectType')

  const clearAction = useCallback(() => {
    const nextUrl = stripModalActionParams(location.pathname, location.search)
    if (nextUrl !== location.pathname + location.search) {
      navigate(nextUrl, { replace: true })
    }
    setModalAction(null)
    lastHandledAction.current = null
  }, [location.pathname, location.search, navigate])

  const handleModalOk = useCallback(() => {
    onModalOk?.()
    clearAction()
  }, [clearAction, onModalOk])

  const callAction = useCallback(
    (action, objectData, objectType) => {
      setCurrentObject(objectData)
      setCurrentObjectType(objectType)
      const model = getModelByName(objectType)
      const url = buildActionUrl(
        model,
        action,
        objectData._id,
        location.pathname,
        location.search
      )
      navigate(url)
    },
    [location.pathname, location.search, navigate]
  )

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

    const model = getModelByName(actionObjectType)
    if (!model.pages?.length) return

    const onPage = model.pages.some(
      (page) => location.pathname === `${model.url}/${page.name}`
    )
    if (!onPage) return

    if (actionName) {
      const action = findAction(model, actionName)
      if (action?.type === 'modal') return
    }

    const params = new URLSearchParams(location.search)
    params.delete('actionObjectType')
    const query = params.toString()
    const nextUrl = location.pathname + (query ? `?${query}` : '')
    if (nextUrl !== location.pathname + location.search) {
      navigate(nextUrl, { replace: true })
    }
  }, [
    actionName,
    actionObjectType,
    location.pathname,
    location.search,
    navigate
  ])

  useEffect(() => {
    if (!actionName || !actionObjectType) {
      setModalAction(null)
      lastHandledAction.current = null
      return
    }

    const actionKey = `${actionObjectType}:${actionName}:${location.pathname}${location.search}`
    if (lastHandledAction.current === actionKey) return

    const model = getModelByName(actionObjectType)
    const action = findAction(model, actionName)
    if (!action) return

    if (action.type === 'modal') {
      lastHandledAction.current = actionKey
      setModalAction(action)
      return
    }

    if (action.type === 'page') {
      const pageName = action.pageName || action.name
      const objectId =
        getObjectIdFromSearch(actionObjectType, location.search) ||
        currentObject?._id
      if (!objectId) return

      const expectedPath = `${model.url}/${pageName}`
      if (location.pathname === expectedPath) {
        lastHandledAction.current = actionKey
        return
      }

      lastHandledAction.current = actionKey
      const url = buildActionUrl(
        model,
        action,
        objectId,
        location.pathname,
        location.search
      )
      navigate(url, { replace: true })
    }
  }, [
    actionName,
    actionObjectType,
    currentObject,
    location.pathname,
    location.search,
    navigate
  ])

  const modalObjectData = currentObject
    ? { ...currentObject, _user: userProfile }
    : { _user: userProfile }

  const [modelWidth, setModelWidth] = useState(520)
  useEffect(() => {
    if (modalAction?.modalWidth) {
      setModelWidth(modalAction.modalWidth)
    }
  }, [modalAction?.modalWidth])

  const [modalCentered, setModalCentered] = useState(false)
  useEffect(() => {
    if (modalAction?.modalCentered !== undefined) {
      setModalCentered(modalAction.modalCentered)
    }
  }, [modalAction?.modalCentered])

  return (
    <ActionsContext.Provider
      value={{
        currentObject,
        currentObjectType,
        setCurrentObject,
        setCurrentObjectType,
        callAction,
        clearAction,
        setOnModalOk
      }}
    >
      <Modal
        open={modalAction != null}
        destroyOnHidden={true}
        width={modelWidth}
        onCancel={clearAction}
        footer={null}
        centered={modalCentered}
      >
        <Suspense
          fallback={
            <Flex
              justify='center'
              align='center'
              style={{
                height: modalCentered ? '120px' : '357px',
                width: '100%'
              }}
            >
              <Spin indicator={<LoadingOutlined spin />} />
            </Flex>
          }
        >
          {modalAction?.content && modalObjectData
            ? modalAction.content(modalObjectData, { onOk: handleModalOk })
            : null}
        </Suspense>
      </Modal>
      {children}
    </ActionsContext.Provider>
  )
}

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

const useActions = () => useContext(ActionsContext)

// eslint-disable-next-line react-refresh/only-export-components
export { ActionsProvider, ActionsContext, useActions }
