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

import config from '../../../config'
import {
  getModelByName,
  getModelByPrefix
} from '../../../database/ObjectModels'
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
import FilamentStockState from '../common/FilamentStockState'
import SubJobState from '../common/SubJobState'

const SpotlightContext = createContext()

const SpotlightProvider = ({ children }) => {
  const { Text } = Typography
  const navigate = useNavigate()
  const [showModal, setShowModal] = useState(false)
  const [loading, setLoading] = useState(false)
  const [query, setQuery] = useState('')
  const [listData, setListData] = useState([])
  const [messageApi, contextHolder] = message.useMessage()
  const [inputPrefix, setInputPrefix] = useState({ prefix: '', mode: null })

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

  const showSpotlight = (defaultQuery = '') => {
    setQuery(defaultQuery)
    setShowModal(true)

    // Set prefix based on default query if provided
    if (defaultQuery) {
      // Check if the default query contains a prefix
      const upperQuery = defaultQuery.toUpperCase()
      const prefixInfo = parsePrefix(upperQuery)

      if (prefixInfo) {
        setInputPrefix(prefixInfo)
        // Set the query to only the part after the prefix and mode character
        const remainingValue = defaultQuery.substring(
          prefixInfo.prefix.length + 1
        )
        setQuery(remainingValue)
        checkAndFetchData(defaultQuery)
      } else {
        setInputPrefix(null)
        checkAndFetchData(defaultQuery)
      }
    } else {
      setInputPrefix(null)
      // Only clear data if we're opening with an empty query and no existing data
      if (listData.length === 0) {
        setListData([])
      }
    }

    // Focus will be handled in useEffect for proper timing after modal renders
  }

  // 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.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 response

      // 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)

        response = await axios.get(`${config.backendUrl}/spotlight/${prefix}`, {
          params: params,
          headers: {
            Accept: 'application/json'
          },
          withCredentials: true
        })
      } else {
        // For other modes (:, ^), use the original behavior
        response = await axios.get(
          `${config.backendUrl}/spotlight/${encodeURIComponent(searchQuery.trim())}`,
          {
            headers: {
              Accept: 'application/json'
            },
            withCredentials: true
          }
        )
      }

      setLoading(false)
      // If the query contains a prefix mode character, and the response is an object, wrap it in an array
      if (
        /[:?^]/.test(searchQuery) &&
        response.data &&
        !Array.isArray(response.data) &&
        typeof response.data === 'object'
      ) {
        setListData([response.data])
      } else {
        setListData(response.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) => {
    if (action && action.url) {
      // Try to get the id (support _id or id)
      const itemId = item._id || item.id
      const url = action.url(itemId)
      if (url && url !== '#') {
        navigate(url)
        setShowModal(false)
      }
    }
  }

  // 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 = getDefaultRowAction(model)
        if (defaultAction) {
          triggerRowAction(defaultAction, item)
        }
        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 = getDefaultRowAction(model)
          if (defaultAction) {
            triggerRowAction(defaultAction, item)
          }
        }
        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 (showModal && 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)
    }
  }, [showModal])

  // Focus input when inputPrefix changes
  useEffect(() => {
    if (showModal) {
      // Only clear data if there's no existing data and no current query
      if (listData.length === 0 && !query) {
        setListData([])
      }
      focusInput()
    }
  }, [inputPrefix, showModal])

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

  // 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 lookup'
      case '?':
        return 'Filter'
      case '^':
        return 'Search'
      default:
        return ''
    }
  }

  return (
    <SpotlightContext.Provider value={{ showSpotlight }}>
      {contextHolder}
      <Modal
        open={showModal}
        onCancel={() => setShowModal(false)}
        closeIcon={null}
        footer={null}
        width={700}
        styles={{ content: { padding: 0 } }}
        destroyOnHidden={true}
      >
        <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 && (
            <div style={{ marginLeft: '18px', marginRight: '14px' }}>
              <List
                dataSource={listData}
                renderItem={(item, index) => {
                  let type = item.objectType || inputPrefix?.type
                  const model = getModelByName(type)
                  const Icon = model.icon
                  const rowActions = 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'>
                          {Icon ? <Icon style={{ fontSize: '20px' }} /> : null}

                          {item.name ? (
                            <Text ellipsis style={{ maxWidth: 170 }}>
                              {item.name}
                            </Text>
                          ) : null}
                          {model.type == 'printer' ? (
                            <PrinterState
                              printer={item}
                              showName={false}
                              showProgress={false}
                              showId={false}
                            />
                          ) : null}
                          {model.type == 'job' ? (
                            <JobState
                              job={item}
                              showQuantity={false}
                              showProgress={false}
                              showId={false}
                            />
                          ) : null}
                          {model.type == 'subjob' ? (
                            <SubJobState
                              subJob={item}
                              showProgress={false}
                              showId={false}
                            />
                          ) : null}
                          {model.type == 'filamentstock' ? (
                            <Flex gap={'small'}>
                              <FilamentStockState
                                filamentStock={item}
                                showId={false}
                                showProgress={false}
                              />
                            </Flex>
                          ) : null}
                          <IdDisplay id={item._id} type={type} longId={false} />
                        </Flex>
                        <Flex gap={'small'}>
                          {rowActions
                            .filter((action) => !action?.default)
                            .map((action, i) => (
                              <Button
                                key={action.name || i}
                                icon={
                                  action.icon ? (
                                    React.createElement(action.icon)
                                  ) : (
                                    <InfoCircleIcon />
                                  )
                                }
                                size={'small'}
                                type={'text'}
                                onClick={() => triggerRowAction(action, item)}
                              />
                            ))}
                          {rowActions
                            .filter((action) => action?.default == true)
                            .map((action, i) => (
                              <Button
                                key={action.name || i}
                                icon={
                                  action.icon ? (
                                    React.createElement(action.icon)
                                  ) : (
                                    <InfoCircleIcon />
                                  )
                                }
                                size={'small'}
                                type={'text'}
                                onClick={() => triggerRowAction(action, item)}
                              />
                            ))}
                          {shortcutText && <Text keyboard>{shortcutText}</Text>}
                        </Flex>
                      </Flex>
                    </List.Item>
                  )
                }}
              ></List>
            </div>
          )}
        </Flex>
      </Modal>
      {children}
    </SpotlightContext.Provider>
  )
}

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

export { SpotlightProvider, SpotlightContext }
