import { useState, useEffect, useContext, useMemo } from 'react'
import { Space, Button, TreeSelect, Tag } from 'antd'
import PlusIcon from '../../Icons/PlusIcon'
import PropTypes from 'prop-types'
import { ApiServerContext } from '../context/ApiServerContext'
import TagsDisplay from './TagsDisplay'

const { SHOW_CHILD } = TreeSelect

const TagsInput = ({
  value = [],
  onChange,
  propertyName,
  modelType,
  placeholder = 'Select or create tags',
  disabled = false
}) => {
  const { getModelPropertyValues } = useContext(ApiServerContext)
  const [options, setOptions] = useState([])
  const [searchValue, setSearchValue] = useState('')
  const [loading, setLoading] = useState(false)
  const [delayedLoading, setDelayedLoading] = useState(true)

  const tags = useMemo(
    () => (Array.isArray(value) ? value : []),
    [value]
  )

  useEffect(() => {
    let cancelled = false

    const loadOptions = async () => {
      if (!modelType || !propertyName) return
      setLoading(true)
      try {
        const values = await getModelPropertyValues(modelType, propertyName)
        if (cancelled) return
        const unique = [
          ...new Set(
            (values || []).filter(
              (v) => v != null && v !== '' && typeof v === 'string'
            )
          )
        ]
        setOptions(unique.map((tag) => ({ label: tag, value: tag })))
      } finally {
        if (!cancelled) setLoading(false)
      }
    }

    loadOptions()
    return () => {
      cancelled = true
    }
  }, [modelType, propertyName, getModelPropertyValues])

  useEffect(() => {
    if (!loading) {
      const timer = setTimeout(() => setDelayedLoading(false), 100)
      return () => clearTimeout(timer)
    }
    setDelayedLoading(true)
  }, [loading])

  const mergedOptions = useMemo(() => {
    const map = new Map(options.map((option) => [option.value, option]))
    tags.forEach((tag) => {
      if (tag != null && tag !== '' && !map.has(tag)) {
        map.set(tag, { label: tag, value: tag })
      }
    })
    return Array.from(map.values())
  }, [options, tags])

  const treeData = useMemo(
    () =>
      mergedOptions.map((option) => ({
        title: (
          <div style={{ paddingTop: 1 }}>
            <TagsDisplay tags={[option.label]} />
          </div>
        ),
        value: option.value,
        key: option.value,
        isLeaf: true
      })),
    [mergedOptions]
  )

  const handleChange = (nextTags) => {
    onChange?.(nextTags)
    setSearchValue('')
  }

  const handleAdd = () => {
    const newTag = searchValue.trim()
    if (!newTag || tags.includes(newTag)) return
    onChange?.([...tags, newTag])
    setSearchValue('')
  }

  const onInputKeyDown = (e) => {
    if (e.key !== 'Enter' || e.defaultPrevented) return
    const newTag = searchValue.trim()
    if (!newTag || tags.includes(newTag)) return
    const existsInOptions = mergedOptions.some(
      (option) => option.value === newTag
    )
    if (existsInOptions) return
    e.preventDefault()
    e.stopPropagation()
    onChange?.([...tags, newTag])
    setSearchValue('')
  }

  const tagRender = ({ label, value: tagValue, closable, onClose }) => (
    <Tag
      color='blue'
      closable={closable}
      onClose={onClose}
      style={{ marginInlineEnd: 4 }}
    >
      {typeof label === 'string' ? label : tagValue}
    </Tag>
  )

  return (
    <Space.Compact block>
      <div style={{ position: 'relative', flex: 1, width: '100%' }}>
        <TreeSelect
          multiple
          treeDataSimpleMode={false}
          treeDefaultExpandAll
          showCheckedStrategy={SHOW_CHILD}
          className='object-select-multiple tags-input'
          style={{ width: '100%', opacity: delayedLoading ? 0 : 1 }}
          placeholder={placeholder}
          value={tags}
          onChange={handleChange}
          treeData={treeData}
          showSearch
          treeNodeFilterProp='value'
          searchValue={searchValue}
          onSearch={setSearchValue}
          onInputKeyDown={onInputKeyDown}
          tagRender={tagRender}
          disabled={disabled}
        />
        {delayedLoading && (
          <TreeSelect
            disabled
            loading
            placeholder='Loading...'
            style={{
              position: 'absolute',
              top: 0,
              left: 0,
              right: 0,
              bottom: 0
            }}
          />
        )}
      </div>
      <Button
        onClick={handleAdd}
        icon={<PlusIcon />}
        disabled={disabled || !searchValue.trim()}
      />
    </Space.Compact>
  )
}

TagsInput.propTypes = {
  value: PropTypes.arrayOf(PropTypes.string),
  onChange: PropTypes.func,
  propertyName: PropTypes.string,
  modelType: PropTypes.string,
  disabled: PropTypes.bool,
  placeholder: PropTypes.string
}

export default TagsInput
