import * as React from 'react'
import { cn } from './utils'

export interface TagSuggestion {
  value: string
  label?: string
  color?: string
  category?: string
}

export interface TagInputProps {
  /** Current tags */
  tags?: string[]
  /** Static suggestions or async fetcher */
  suggestions?: TagSuggestion[] | ((query: string) => TagSuggestion[] | Promise<TagSuggestion[]>)
  /** Maximum number of tags */
  maxTags?: number
  /** Allow duplicate tag values */
  allowDuplicates?: boolean
  /** Input placeholder */
  placeholder?: string
  /** Delimiter character for splitting pasted text */
  delimiter?: string
  /** Additional class name */
  className?: string
  /** Callback when tags change */
  onChange?: (tags: string[]) => void
}

function TagInput({
  tags: tagsProp = [],
  suggestions,
  maxTags,
  allowDuplicates = false,
  placeholder = 'Add tag...',
  delimiter = ',',
  className,
  onChange,
}: TagInputProps) {
  const [tags, setTags] = React.useState<string[]>(tagsProp)
  const [inputValue, setInputValue] = React.useState('')
  const [filteredSuggestions, setFilteredSuggestions] = React.useState<TagSuggestion[]>([])
  const [highlightedIndex, setHighlightedIndex] = React.useState(-1)
  const [isDropdownOpen, setIsDropdownOpen] = React.useState(false)
  const [isLoading, setIsLoading] = React.useState(false)
  const inputRef = React.useRef<HTMLInputElement>(null)
  const rootRef = React.useRef<HTMLDivElement>(null)
  const debounceRef = React.useRef<ReturnType<typeof setTimeout> | null>(null)

  React.useEffect(() => {
    setTags(tagsProp)
  }, [tagsProp])

  // Close dropdown on outside click
  React.useEffect(() => {
    function handleClick(e: MouseEvent) {
      if (rootRef.current && !rootRef.current.contains(e.target as Node)) {
        closeDropdown()
      }
    }
    document.addEventListener('mousedown', handleClick)
    return () => document.removeEventListener('mousedown', handleClick)
  }, [])

  React.useEffect(() => {
    return () => {
      if (debounceRef.current) clearTimeout(debounceRef.current)
    }
  }, [])

  const isMaxReached = maxTags !== undefined && tags.length >= maxTags

  function updateTags(newTags: string[]) {
    setTags(newTags)
    onChange?.(newTags)
  }

  function addTag(tag: string): boolean {
    const trimmed = tag.trim()
    if (trimmed === '') return false
    if (isMaxReached) return false
    if (!allowDuplicates && tags.includes(trimmed)) return false
    updateTags([...tags, trimmed])
    return true
  }

  function removeTag(tag: string) {
    updateTags(tags.filter(t => t !== tag))
  }

  function removeLastTag() {
    if (tags.length > 0) {
      updateTags(tags.slice(0, -1))
    }
  }

  function closeDropdown() {
    setIsDropdownOpen(false)
    setHighlightedIndex(-1)
  }

  function openDropdown() {
    setIsDropdownOpen(true)
  }

  function findSuggestion(tag: string): TagSuggestion | undefined {
    if (!suggestions || typeof suggestions === 'function') return undefined
    return suggestions.find(s => s.value === tag)
  }

  function filterExisting(list: TagSuggestion[]): TagSuggestion[] {
    if (allowDuplicates) return list
    return list.filter(s => !tags.includes(s.value))
  }

  async function fetchSuggestions(query: string) {
    if (!suggestions) {
      closeDropdown()
      return
    }
    if (typeof suggestions === 'function') {
      setIsLoading(true)
      try {
        const result = suggestions(query)
        const resolved = result instanceof Promise ? await result : result
        const filtered = filterExisting(resolved)
        setFilteredSuggestions(filtered)
        if (filtered.length > 0) openDropdown()
        else closeDropdown()
      } catch {
        closeDropdown()
      } finally {
        setIsLoading(false)
      }
    } else {
      const lowerQuery = query.toLowerCase()
      const filtered = filterExisting(
        suggestions.filter(s => {
          const text = (s.label ?? s.value).toLowerCase()
          return text.includes(lowerQuery)
        })
      )
      setFilteredSuggestions(filtered)
      if (filtered.length > 0) openDropdown()
      else closeDropdown()
    }
  }

  function handleInputChange(e: React.ChangeEvent<HTMLInputElement>) {
    const val = e.target.value
    setInputValue(val)
    const query = val.trim()
    if (query === '' || !suggestions) {
      closeDropdown()
      return
    }
    if (debounceRef.current) clearTimeout(debounceRef.current)
    debounceRef.current = setTimeout(() => {
      void fetchSuggestions(query)
    }, 150)
  }

  function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
    switch (e.key) {
      case 'Enter': {
        e.preventDefault()
        if (isDropdownOpen && highlightedIndex >= 0) {
          const suggestion = filteredSuggestions[highlightedIndex]
          if (suggestion) {
            addTag(suggestion.value)
            setInputValue('')
            closeDropdown()
          }
        } else if (inputValue.trim() !== '') {
          addTag(inputValue)
          setInputValue('')
          closeDropdown()
        }
        break
      }
      case 'Backspace': {
        if (inputValue === '' && tags.length > 0) {
          removeLastTag()
        }
        break
      }
      case 'Escape': {
        if (isDropdownOpen) {
          e.preventDefault()
          closeDropdown()
        }
        break
      }
      case 'ArrowDown': {
        if (isDropdownOpen) {
          e.preventDefault()
          setHighlightedIndex(prev => (prev < filteredSuggestions.length - 1 ? prev + 1 : 0))
        }
        break
      }
      case 'ArrowUp': {
        if (isDropdownOpen) {
          e.preventDefault()
          setHighlightedIndex(prev => (prev > 0 ? prev - 1 : filteredSuggestions.length - 1))
        }
        break
      }
      default: {
        if (e.key === delimiter && inputValue.trim() !== '') {
          e.preventDefault()
          addTag(inputValue)
          setInputValue('')
          closeDropdown()
        }
      }
    }
  }

  function handlePaste(e: React.ClipboardEvent<HTMLInputElement>) {
    const text = e.clipboardData?.getData('text')
    if (!text) return
    if (text.includes(delimiter)) {
      e.preventDefault()
      const parts = text.split(delimiter)
      for (const part of parts) {
        addTag(part)
      }
    }
  }

  function handleSuggestionClick(suggestion: TagSuggestion) {
    addTag(suggestion.value)
    setInputValue('')
    closeDropdown()
    inputRef.current?.focus()
  }

  return (
    <div ref={rootRef} data-slot="tag-input" className={cn('relative w-full', className)}>
      {/* Tags container styled like an input */}
      <div
        className={cn(
          'border-border-glass bg-glass flex min-h-[2.5rem] w-full flex-wrap items-center gap-1.5 rounded-xl border px-3 py-1.5 backdrop-blur-[20px]',
          'transition-all duration-[250ms]',
          'focus-within:border-primary focus-within:shadow-glow'
        )}
        onClick={() => inputRef.current?.focus()}
      >
        {/* Tag chips */}
        {tags.map((tag, i) => {
          const suggestion = findSuggestion(tag)
          return (
            <span
              key={`${tag}-${i}`}
              data-tag={tag}
              className={cn(
                'inline-flex items-center gap-1 rounded-lg px-2 py-0.5 text-[12px] font-medium',
                'bg-glass-hover text-foreground-secondary border border-border-glass',
                'transition-all duration-200'
              )}
              style={
                suggestion?.color
                  ? { borderColor: suggestion.color, color: suggestion.color }
                  : undefined
              }
            >
              <span>{suggestion?.label ?? tag}</span>
              <button
                type="button"
                aria-label={`Remove ${tag}`}
                className="ml-0.5 inline-flex h-4 w-4 items-center justify-center rounded-sm text-foreground-muted transition-colors hover:text-foreground"
                onClick={e => {
                  e.stopPropagation()
                  removeTag(tag)
                }}
              >
                <svg width="10" height="10" viewBox="0 0 12 12" fill="none">
                  <path
                    d="M9 3L3 9M3 3L9 9"
                    stroke="currentColor"
                    strokeWidth="1.5"
                    strokeLinecap="round"
                  />
                </svg>
              </button>
            </span>
          )
        })}

        {/* Input */}
        <input
          ref={inputRef}
          type="text"
          role="combobox"
          aria-expanded={isDropdownOpen}
          aria-autocomplete="list"
          autoComplete="off"
          placeholder={isMaxReached ? 'Max tags reached' : placeholder}
          disabled={isMaxReached}
          className={cn(
            'min-w-[80px] flex-1 border-none bg-transparent py-1 text-[13px] text-foreground outline-none',
            'placeholder:text-foreground-muted',
            'disabled:cursor-not-allowed disabled:opacity-50'
          )}
          value={inputValue}
          onChange={handleInputChange}
          onKeyDown={handleKeyDown}
          onPaste={handlePaste}
          onFocus={() => {
            if (inputValue.trim().length > 0 && suggestions) {
              void fetchSuggestions(inputValue.trim())
            }
          }}
        />
      </div>

      {/* Suggestions dropdown */}
      {isDropdownOpen && (
        <div
          role="listbox"
          className={cn(
            'absolute left-0 z-50 mt-1 max-h-48 w-full overflow-y-auto rounded-xl border border-border-glass bg-glass shadow-lg backdrop-blur-[20px]',
            'animate-in fade-in-0 zoom-in-95 slide-in-from-top-1'
          )}
        >
          {isLoading ? (
            <div className="flex items-center gap-2 px-3 py-2 text-[12px] text-foreground-muted">
              <svg className="animate-spin" width="14" height="14" viewBox="0 0 16 16">
                <circle
                  cx="8"
                  cy="8"
                  r="6"
                  stroke="currentColor"
                  strokeWidth="2"
                  fill="none"
                  strokeDasharray="30 10"
                />
              </svg>
              Loading...
            </div>
          ) : (
            filteredSuggestions.map((suggestion, i) => (
              <div
                key={suggestion.value}
                role="option"
                aria-selected={i === highlightedIndex}
                className={cn(
                  'flex cursor-pointer items-center gap-2 px-3 py-2 text-[13px] transition-colors',
                  'text-foreground-secondary',
                  i === highlightedIndex && 'bg-glass-hover text-foreground',
                  i !== highlightedIndex && 'hover:bg-glass-hover hover:text-foreground'
                )}
                onClick={() => handleSuggestionClick(suggestion)}
                onMouseEnter={() => setHighlightedIndex(i)}
              >
                {suggestion.color && (
                  <span
                    className="h-2.5 w-2.5 shrink-0 rounded-full"
                    style={{ backgroundColor: suggestion.color }}
                  />
                )}
                <span>{suggestion.label ?? suggestion.value}</span>
                {suggestion.category && (
                  <span className="ml-auto text-[11px] text-foreground-muted">
                    {suggestion.category}
                  </span>
                )}
              </div>
            ))
          )}
        </div>
      )}
    </div>
  )
}

export { TagInput }
