// TODO: TagSearch と共通化する．

import { useRef, useState } from 'react'

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

import type { ComponentPropsWithoutRef, 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 = Omit<ComponentPropsWithoutRef<'textarea'>, 'value' | 'onChange' | 'onBlur'> & {
    tags:     string
    setTags:  (tags: string) => void
    errors?:  string[] }


const PostFormTagsArea: FC<Props> = ({ tags, setTags, errors, ...rest }) => {
  const ref = useRef<HTMLTextAreaElement> (null)

  const [bounds, setBounds] = useState<{ start: number; end: number }> ({ start: 0, end: 0 })
  const [focused, setFocused] = useState (false)
  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 + 1
      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)
    if (!(token.trim ()))
      {
	setSuggestionsVsbl (false)
	return
      }

    setBounds ({ start, end })

    const data = await apiGet<Tag[]> ('/tags/autocomplete', { params: {
	q: token,
	nico: '0' } })
    const nextSuggestions = data.filter (t => t.postCount > 0)
    setSuggestions (nextSuggestions)
    setSuggestionsVsbl (nextSuggestions.length > 0)
  }

  return (
      <FormField className="relative w-full" label="タグ" messages={errors}>
	{({ describedBy, invalid }) => (
	    <>
	      <TextArea
		  {...rest}
		  ref={ref}
		  value={tags}
		  aria-describedby={describedBy}
		  invalid={invalid}
		  onChange={ev => setTags (ev.target.value)}
		  onSelect={async (ev: SyntheticEvent<HTMLTextAreaElement>) => {
		    const pos = (ev.target as HTMLTextAreaElement).selectionStart
		    await recompute (pos)
		  }}
		  onFocus={() => setFocused (true)}
		  onBlur={() => {
		    setFocused (false)
		    setSuggestionsVsbl (false)
		  }}/>
	      {focused && (
		  <TagSearchBox
		      suggestions={suggestionsVsbl && suggestions.length > 0
				   ? suggestions
				   : [] as Tag[]}
		      activeIndex={-1}
		      onSelect={handleTagSelect}/>)}
	    </>)}
      </FormField>)
}

export default PostFormTagsArea
