import { useRef, useState } from 'react'

import TagSearchBox from '@/components/TagSearchBox'
import Label from '@/components/common/Label'
import TextArea from '@/components/common/TextArea'
import { apiGet } from '@/lib/api'

import type { FC, SyntheticEvent } from 'react'

import type { Tag } from '@/types'

const SEP = /\s/


const getTokenAt = (value: string, pos: number) => {
  let start = pos
  while (start > 0 && !(SEP.test (value[start - 1])))
    --start

  let end = pos
  while (end < value.length && !(SEP.test (value[end])))
    ++end

  return { start, end, token: value.slice (start, end) }
}


const replaceToken = (value: string, start: number, end: number, text: string) => (
    `${ value.slice (0, start) }${ text }${ value.slice (end) }`)


type Props = {
    tags:     string
    setTags:  (tags: string) => void }


export default (({ tags, setTags }: Props) => {
  const ref = useRef<HTMLTextAreaElement> (null)

  const [bounds, setBounds] = useState<{ start: number; end: number }> ({ start: 0, end: 0 })
  const [suggestions, setSuggestions] = useState<Tag[]> ([])
  const [suggestionsVsbl, setSuggestionsVsbl] = useState (false)

  const handleTagSelect = (tag: Tag) => {
    setSuggestionsVsbl (false)
    const textarea = ref.current!
    const newValue = replaceToken (tags, bounds.start, bounds.end, tag.name)
    setTags (newValue)
    requestAnimationFrame (async () => {
      const p = bounds.start + tag.name.length
      textarea.selectionStart = textarea.selectionEnd = p
      textarea.focus ()
      await recompute (p, newValue)
    })
  }

  const recompute = async (pos: number, v: string = tags) => {
    const { start, end, token } = getTokenAt (v, pos)
    setBounds ({ start, end })
    const data = await apiGet<Tag[]> ('/tags/autocomplete', { params: { q: token } })
    setSuggestions (data.filter (t => t.postCount > 0))
    setSuggestionsVsbl (suggestions.length > 0)
  }

  return (
      <div>
	<Label>タグ</Label>
	<TextArea
	    ref={ref}
	    value={tags}
	    onChange={ev => setTags (ev.target.value)}
	    onSelect={async (ev: SyntheticEvent<HTMLTextAreaElement>) => {
	      const pos = (ev.target as HTMLTextAreaElement).selectionStart
	      await recompute (pos)
	    }}/>
	<TagSearchBox suggestions={suggestionsVsbl && suggestions.length
				   ? suggestions
				   : [] as Tag[]}
		      activeIndex={-1}
		      onSelect={handleTagSelect}/>
      </div>)
}) satisfies FC<Props>
