import {
  Input,
  Flex,
  List,
  Typography,
  Modal,
  Spin,
  message,
  Form,
  Button,
  Card,
  Divider
} from 'antd'
import {
  createContext,
  useEffect,
  useState,
  useRef,
  createElement,
  useContext,
  useCallback
} from 'react'
import axios from 'axios'
import { LoadingOutlined } from '@ant-design/icons'
import PropTypes from 'prop-types'
import { useNavigate } from 'react-router-dom'
import StateDisplay from '../common/StateDisplay'
import IdDisplay from '../common/IdDisplay'

import config from '../../../config'
import {
  getModelByName,
  getModelByPrefix,
  searchModelsByLabel
} from '../../../database/ObjectModels'
import { buildActionUrl } from '../../../utils/modelActions'
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
import MenuIcon from '../../Icons/MenuIcon'
import { ApiServerContext } from './ApiServerContext'
import { AuthContext } from './AuthContext'
import { ElectronContext } from './ElectronContext'

const SpotlightContext = createContext()

const SpotlightContent = ({
  isActive,
  openKey,
  defaultQuery,
  onRequestClose,
  isElectron
}) => {
  const { Text } = Typography
  const navigate = useNavigate()
  const [loading, setLoading] = useState(false)
  const [query, setQuery] = useState('')
  const [listData, setListData] = useState([])
  const [messageApi, contextHolder] = message.useMessage()
  const [inputPrefix, setInputPrefix] = useState(null)
  const { fetchSpotlightData } = useContext(ApiServerContext)
  const { token } = useContext(AuthContext)
  const { openInternalUrl } = useContext(ElectronContext)

  // Refs for throttling/debouncing
  const lastFetchTime = useRef(0)
  const pendingQuery = useRef(null)
  const fetchTimeoutRef = useRef(null)
  const inputRef = useRef(null)
  const formRef = useRef(null)

  // Helper function to parse prefix and mode from query
  const parsePrefix = (query) => {
    // Check for prefix format: XXX: or XXX? or XXX^
    if (query.length >= 4) {
      const potentialPrefix = query.substring(0, 3)
      const modeChar = query[3]

      // Check if it's a valid mode character
      if ([':', '?', '^'].includes(modeChar)) {
        const prefixModel = getModelByPrefix(potentialPrefix)

        if (prefixModel && prefixModel.prefix === potentialPrefix) {
          return {
            ...prefixModel,
            mode: modeChar
          }
        }
      }
    }
    return null
  }

  const fetchData = async (searchQuery) => {
    if (!searchQuery || !searchQuery.trim()) return

    try {
      // Update last fetch time
      lastFetchTime.current = Date.now()
      // Clear any pending queries
      pendingQuery.current = null

      setLoading(true)
      setListData([])

      let data

      // Check if we have a prefix with ? mode (filter mode)
      if (inputPrefix && inputPrefix.mode === '?') {
        // For filter mode, parse the searchQuery which is in format like "VEN?name=Tom"
        const queryParts = searchQuery.split('?')
        const prefix = queryParts[0]
        const queryParams = queryParts[1] || ''

        // Parse the query parameters
        const params = new URLSearchParams(queryParams)

        // For ? mode, use axios directly with query params
        const response = await axios.get(
          `${config.backendUrl}/spotlight/${prefix}`,
          {
            params: params,
            headers: {
              Accept: 'application/json',
              Authorization: `Bearer ${token}`
            }
          }
        )
        data = response.data
      } else {
        // For other modes (:, ^), use fetchSpotlightData from ApiServerContext
        data = await fetchSpotlightData(searchQuery.trim())
      }

      setLoading(false)

      const models = searchModelsByLabel(searchQuery.trim()).map((model) => ({
        type: model.name,
        name: model.labelPlural,
        icon: model.icon,
        actions: [
          {
            default: true,
            row: true,
            icon: MenuIcon,
            url: () => {
              return model.url
            }
          }
        ]
      }))
      // If the query contains a prefix mode character, and the response is an object, wrap it in an array
      if (
        /[:?^]/.test(searchQuery) &&
        data &&
        !Array.isArray(data) &&
        typeof data === 'object'
      ) {
        setListData([...models, data])
      } else {
        setListData([...models, ...data])
      }

      // Check if there's a pending query after this fetch completes
      if (pendingQuery.current !== null) {
        const timeToNextFetch = Math.max(
          0,
          1000 - (Date.now() - lastFetchTime.current)
        )
        scheduleNextFetch(timeToNextFetch)
      }
    } catch (error) {
      setLoading(false)
      messageApi.error('An error occurred while fetching data.')
      console.error('Spotlight fetch error:', error)
    }
  }

  const checkAndFetchData = (searchQuery) => {
    // Store the latest query
    pendingQuery.current = searchQuery

    // Calculate time since last fetch
    const now = Date.now()
    const timeSinceLastFetch = now - lastFetchTime.current

    // If we've waited at least 1 second since last fetch, fetch immediately
    if (timeSinceLastFetch >= 1000) {
      if (fetchTimeoutRef.current) {
        clearTimeout(fetchTimeoutRef.current)
        fetchTimeoutRef.current = null
      }
      fetchData(searchQuery)
    } else {
      // Otherwise, schedule fetch for when 1 second has passed
      if (!fetchTimeoutRef.current) {
        const timeToWait = 1000 - timeSinceLastFetch
        scheduleNextFetch(timeToWait)
      }
      // We don't need to do anything if a fetch is already scheduled
      // as the latest query is already stored in pendingQuery
    }
  }

  const scheduleNextFetch = (delay) => {
    if (fetchTimeoutRef.current) {
      clearTimeout(fetchTimeoutRef.current)
    }

    fetchTimeoutRef.current = setTimeout(() => {
      fetchTimeoutRef.current = null
      if (pendingQuery.current !== null) {
        fetchData(pendingQuery.current)
      }
    }, delay)
  }

  const handleSpotlightChange = (formData) => {
    const newQuery = formData.query || ''
    setQuery(newQuery)

    // Build the full search query with prefix if available
    let fullQuery = newQuery
    if (inputPrefix) {
      fullQuery = inputPrefix.prefix + inputPrefix.mode + newQuery
    }

    // Check if we need to fetch data
    checkAndFetchData(fullQuery)
  }

  // Focus the input element
  const focusInput = () => {
    setTimeout(() => {
      if (inputRef.current) {
        const input = inputRef.current.input
        if (input) {
          input.focus()
        }
      }
    }, 50)
  }

  // Custom handler for input changes to handle prefix logic
  const handleInputChange = (e) => {
    const value = e.target.value

    // If the input is empty or being cleared
    if (!value || value.trim() === '') {
      // Only clear the prefix if the input is completely empty
      if (value === '') {
        setInputPrefix(null)
      }
      if (formRef.current) {
        formRef.current.setFieldsValue({ query: value })
      }
      return
    }

    // Check if the input contains a prefix (format: XXX:, XXX?, or XXX^)
    const upperValue = value.toUpperCase()
    const prefixInfo = parsePrefix(upperValue)

    // If it's a valid prefix
    if (prefixInfo) {
      setInputPrefix(prefixInfo)
      // Remove the prefix from the input value, keeping only what comes after the mode character
      const remainingValue = value.substring(4)
      if (formRef.current) {
        formRef.current.setFieldsValue({ query: remainingValue })
      }
      setQuery(remainingValue)
      return
    }

    // Update the form value normally
    if (formRef.current) {
      formRef.current.setFieldsValue({ query: value })
    }
  }

  // Helper to get row actions and default action for a model
  const getRowActions = (model) =>
    (model?.actions || []).filter((action) => action.row === true)

  const getDefaultRowAction = (model) =>
    getRowActions(model).find((action) => action.default)

  const triggerRowAction = (action, item, model) => {
    if (action) {
      const itemId = item._id || item.id
      const url = buildActionUrl(model, action, itemId, '', '')
      if (url && url !== '#') {
        if (isElectron) {
          openInternalUrl(url)
        } else {
          navigate(url)
        }
        if (onRequestClose) onRequestClose()
      }
    }
  }

  // Handle key down events for backspace behavior and navigation
  const handleKeyDown = (e) => {
    if (listData.length > 0) {
      // Enter key - trigger default action for first item
      if (e.key === 'Enter') {
        e.preventDefault()
        const item = listData[0]
        let type = item.type || item.objectType || inputPrefix?.type
        const model = getModelByName(type)
        const defaultAction = item?.type
          ? getDefaultRowAction(item)
          : model
            ? getDefaultRowAction(model)
            : null
        if (defaultAction) {
          triggerRowAction(defaultAction, item, model)
        }
        return
      }
      // Number keys 1-9 - trigger default action for corresponding item
      if (/^[1-9]$/.test(e.key)) {
        e.preventDefault()
        const index = parseInt(e.key, 10)
        if (index < listData.length) {
          const item = listData[index]
          let type = item.type || item.objectType || inputPrefix?.type
          const model = getModelByName(type)
          const defaultAction = item?.type
            ? getDefaultRowAction(item)
            : model
              ? getDefaultRowAction(model)
              : null
          if (defaultAction) {
            triggerRowAction(defaultAction, item, model)
          }
        }
        return
      }
    }
    // If backspace is pressed and there's a prefix but the input is empty
    if (e.key === 'Backspace' && inputPrefix && query === '') {
      setInputPrefix(null)
      e.preventDefault()
      return
    }
  }

  // Focus and select text in input when modal becomes visible
  useEffect(() => {
    if (isActive && inputRef.current) {
      // Use a small timeout to ensure the modal is fully rendered and visible
      setTimeout(() => {
        const input = inputRef.current.input
        if (input) {
          input.focus()
          input.select() // Select all text
        }
      }, 50)
    }
  }, [isActive])

  // Focus input when inputPrefix changes
  useEffect(() => {
    if (isActive) {
      focusInput()
    }
  }, [inputPrefix, isActive])

  // Update form value when query changes
  useEffect(() => {
    if (isActive && formRef.current) {
      formRef.current.setFieldsValue({ query: query })
    }
  }, [query, isActive])

  // Apply/refresh defaultQuery when the component is opened (modal) or mounted (standalone)
  useEffect(() => {
    if (!isActive) return

    const nextDefaultQuery = defaultQuery || ''

    // Set prefix based on default query if provided
    if (nextDefaultQuery) {
      const upperQuery = nextDefaultQuery.toUpperCase()
      const prefixInfo = parsePrefix(upperQuery)

      if (prefixInfo) {
        setInputPrefix(prefixInfo)
        const remainingValue = nextDefaultQuery.substring(
          prefixInfo.prefix.length + 1
        )
        setQuery(remainingValue)
        checkAndFetchData(nextDefaultQuery)
      } else {
        setInputPrefix(null)
        setQuery(nextDefaultQuery)
        checkAndFetchData(nextDefaultQuery)
      }
    } else {
      setQuery('')
      setInputPrefix(null)
      // Keep previous listData behavior (don’t force-clear on open)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [openKey, isActive])

  // Cleanup on unmount
  useEffect(() => {
    return () => {
      if (fetchTimeoutRef.current) {
        clearTimeout(fetchTimeoutRef.current)
      }
    }
  }, [])

  // Helper function to get mode description
  const getModeDescription = (mode) => {
    switch (mode) {
      case ':':
        return 'ID/Reference lookup'
      case '?':
        return 'Filter'
      case '^':
        return 'Search'
      default:
        return ''
    }
  }

  const spotlightContent = (
    <Flex vertical>
      <Form ref={formRef} onValuesChange={handleSpotlightChange}>
        <Form.Item name='query' initialValue={query} style={{ margin: 0 }}>
          <Input
            ref={inputRef}
            placeholder='Enter a query or scan a barcode...'
            size='large'
            addonBefore={
              inputPrefix ? (
                <Flex align='center' gap='small'>
                  <Text style={{ fontSize: 20 }}>{inputPrefix.prefix}</Text>
                  <Text style={{ fontSize: 20 }} type='secondary'>
                    {inputPrefix.mode}
                  </Text>
                </Flex>
              ) : undefined
            }
            suffix={
              <Flex align='center' gap='small'>
                {inputPrefix?.mode && (
                  <Text type='secondary' style={{ fontSize: '12px' }}>
                    {getModeDescription(inputPrefix.mode)}
                  </Text>
                )}
                <Spin
                  indicator={<LoadingOutlined />}
                  spinning={loading}
                  size='small'
                />
              </Flex>
            }
            onChange={handleInputChange}
            onKeyDown={handleKeyDown}
          />
        </Form.Item>
      </Form>

      {listData.length > 0 && (
        <>
          {isElectron && <Divider style={{ margin: 0 }} />}
          <div style={{ marginLeft: '18px', marginRight: '14px' }}>
            <List
              dataSource={listData}
              renderItem={(item, index) => {
                let type = item.objectType || inputPrefix?.type || item?.type
                const model = getModelByName(type)
                const Icon = model?.icon
                const rowActions = item?.type
                  ? item.actions
                  : getRowActions(model)
                let shortcutText = ''
                if (index === 0) {
                  shortcutText = 'ENTER'
                } else if (index <= 9) {
                  shortcutText = index.toString()
                }
                return (
                  <List.Item>
                    <Flex
                      gap={'middle'}
                      style={{ width: '100%' }}
                      align='center'
                      justify='space-between'
                    >
                      <Flex
                        gap={'small'}
                        align='center'
                        style={{ flexGrow: 1 }}
                      >
                        {Icon ? <Icon style={{ fontSize: '20px' }} /> : null}

                        {item.name ? (
                          <Text
                            ellipsis
                            style={{
                              maxWidth: 170,
                              flexGrow: 1,
                              width: '100%'
                            }}
                          >
                            {item.name}
                          </Text>
                        ) : null}
                        {item?.state ? (
                          <StateDisplay
                            state={item.state}
                            showName={false}
                            showProgress={false}
                            showId={false}
                          />
                        ) : null}
                        {item?._id ? (
                          <IdDisplay id={item._id} type={type} longId={false} />
                        ) : null}
                      </Flex>
                      <Flex gap={'small'}>
                        {rowActions
                          .filter((action) => !action?.default)
                          .map((action, i) => (
                            <Button
                              key={action.name || i}
                              icon={
                                action.icon ? (
                                  createElement(action.icon)
                                ) : (
                                  <InfoCircleIcon />
                                )
                              }
                              size={'small'}
                              type={'text'}
                              onClick={() => triggerRowAction(action, item, model)}
                            />
                          ))}
                        {rowActions
                          .filter((action) => action?.default == true)
                          .map((action, i) => (
                            <Button
                              key={action.name || i}
                              icon={
                                action.icon ? (
                                  createElement(action.icon)
                                ) : (
                                  <InfoCircleIcon />
                                )
                              }
                              size={'small'}
                              type={'text'}
                              onClick={() => triggerRowAction(action, item, model)}
                            />
                          ))}
                        {shortcutText && <Text keyboard>{shortcutText}</Text>}
                      </Flex>
                    </Flex>
                  </List.Item>
                )
              }}
            ></List>
          </div>
        </>
      )}
    </Flex>
  )

  return (
    <>
      {contextHolder}
      {spotlightContent}
    </>
  )
}

SpotlightContent.propTypes = {
  isActive: PropTypes.bool,
  openKey: PropTypes.number,
  defaultQuery: PropTypes.string,
  onRequestClose: PropTypes.func,
  isElectron: PropTypes.bool
}

SpotlightContent.defaultProps = {
  isActive: true,
  openKey: 0,
  defaultQuery: '',
  onRequestClose: null
}

const SpotlightProvider = ({ children }) => {
  const [showModal, setShowModal] = useState(false)
  const [openKey, setOpenKey] = useState(0)
  const [defaultQuery, setDefaultQuery] = useState('')

  const showSpotlight = (nextDefaultQuery = '') => {
    setDefaultQuery(nextDefaultQuery)
    setOpenKey((k) => k + 1)
    setShowModal(true)
  }

  return (
    <SpotlightContext.Provider value={{ showSpotlight }}>
      <Modal
        open={showModal}
        onCancel={() => setShowModal(false)}
        closeIcon={null}
        footer={null}
        width={700}
        styles={{ content: { padding: 0, borderRadius: '12px' } }}
        destroyOnHidden={true}
      >
        <SpotlightContent
          isActive={showModal}
          openKey={openKey}
          defaultQuery={defaultQuery}
          onRequestClose={() => setShowModal(false)}
        />
      </Modal>
      {children}
    </SpotlightContext.Provider>
  )
}

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

const ElectronSpotlightContentPage = () => {
  const cardRef = useRef(null)
  const resizeTimeoutRef = useRef(null)
  const { resizeSpotlightWindow, isElectron } = useContext(ElectronContext)

  // Function to measure and resize window
  const updateWindowHeight = useCallback(() => {
    if (!cardRef.current || !isElectron || !resizeSpotlightWindow) return

    // Clear any pending resize
    if (resizeTimeoutRef.current) {
      clearTimeout(resizeTimeoutRef.current)
    }

    // Debounce the resize to avoid too many calls
    resizeTimeoutRef.current = setTimeout(() => {
      try {
        const cardElement = cardRef.current
        if (!cardElement) return

        // Get the scroll height of the card
        const scrollHeight = cardElement.scrollHeight

        // Add some padding for better appearance (e.g., 10px top and bottom)
        const padding = 0
        const newHeight = scrollHeight + padding * 2

        // Set minimum height to prevent window from being too small
        const minHeight = 30
        const finalHeight = Math.max(newHeight, minHeight)

        // Call IPC to resize the window
        resizeSpotlightWindow(finalHeight)
      } catch (error) {
        console.warn('Failed to update spotlight window height:', error)
      }
    }, 100) // 100ms debounce
  }, [isElectron, resizeSpotlightWindow])

  // Use ResizeObserver to watch for content changes
  useEffect(() => {
    if (!cardRef.current || !isElectron || !resizeSpotlightWindow) return

    const cardElement = cardRef.current

    // Create ResizeObserver to watch for size changes
    const resizeObserver = new ResizeObserver(() => {
      updateWindowHeight()
    })

    resizeObserver.observe(cardElement)

    // Initial measurement after a short delay to ensure content is rendered
    const initialTimeout = setTimeout(() => {
      updateWindowHeight()
    }, 200)

    return () => {
      resizeObserver.disconnect()
      if (resizeTimeoutRef.current) {
        clearTimeout(resizeTimeoutRef.current)
      }
      clearTimeout(initialTimeout)
    }
  }, [isElectron, resizeSpotlightWindow, updateWindowHeight])

  return (
    <div
      ref={cardRef}
      style={{
        maxWidth: 700,
        padding: 0
      }}
      className='electron-spotlight-content'
    >
      <Card
        styles={{ body: { padding: 0 } }}
        variant={'borderless'}
        style={{ border: 'none', backgroundColor: 'transparent' }}
      >
        <SpotlightContent
          isActive={true}
          openKey={1}
          defaultQuery=''
          isElectron={true}
        />
      </Card>
    </div>
  )
}

export {
  SpotlightProvider,
  SpotlightContext,
  SpotlightContent,
  ElectronSpotlightContentPage
}
