import { useEffect, useState } from 'react'

import PostFormTagsArea from '@/components/PostFormTagsArea'
import PostOriginalCreatedTimeField from '@/components/PostOriginalCreatedTimeField'
import FieldError from '@/components/common/FieldError'
import FormField from '@/components/common/FormField'
import { useDialogue } from '@/components/dialogues/DialogueProvider'
import { Button } from '@/components/ui/button'
import { toast } from '@/components/ui/use-toast'
import { isApiError } from '@/lib/api'
import { updatePost } from '@/lib/posts'
import { inputClass } from '@/lib/utils'
import { useValidationErrors } from '@/lib/useValidationErrors'

import type { FC, FormEvent } from 'react'

import type { Post, Tag } from '@/types'

type PostFormField =
    'parentPostIds' | 'tags' | 'originalCreatedAt'


const tagsToStr = (tags: Tag[]): string => {
  const result: Tag[] = []

  const walk = (tag: Tag) => {
    const { children, ...rest } = tag
    result.push (rest)
    children?.forEach (walk)
  }

  tags.filter (t => t.category !== 'nico').forEach (walk)

  return [...(new Set (result.map (t => t.name)))].join (' ')
}


type Props = { post:    Post
	       onSave:  (newPost: Post) => void }


const PostEditForm: FC<Props> = ({ post, onSave }) => {
  const [disabled, setDisabled] = useState (false)
  const { baseErrors, fieldErrors, clearValidationErrors, applyValidationError } =
      useValidationErrors<PostFormField> ()
  const [originalCreatedBefore, setOriginalCreatedBefore] =
      useState<string | null> (post.originalCreatedBefore)
  const [originalCreatedFrom, setOriginalCreatedFrom] =
      useState<string | null> (post.originalCreatedFrom)
  const [parentPostIds, setParentPostIds] =
      useState ((post.parentPosts ?? []).map (p => p.id).join (' '))
  const [tags, setTags] = useState<string> ('')
  const [title, setTitle] = useState (post.title)

  const dialogue = useDialogue ()

  const update = async (...args: Parameters<typeof updatePost>) => {
    clearValidationErrors ()

    try
    {
      const data = await updatePost (...args)
      onSave ({ ...post,
		versionNo:              data.versionNo,
		title:                  data.title,
		tags:                   data.tags,
		parentPosts:            data.parentPosts,
		childPosts:             data.childPosts,
		siblingPosts:           data.siblingPosts,
		originalCreatedFrom:    data.originalCreatedFrom,
		originalCreatedBefore:  data.originalCreatedBefore } as Post)
      toast ({ description: '更新しました．' })
    }
    catch (e)
    {
      const response = isApiError<{ mergeable?: boolean }> (e) ? e.response : undefined

      if (response?.status !== 409)
	{
	  if (applyValidationError (e))
	    {
	      toast ({ description: '更新はできなかったよ……' })
	      return
	    }

	  toast ({ title: '失敗……', description: '入力を確認してください．' })

	  return
	}

      const action = await dialogue.choice ({
	  title: '競合が発生しました．',
	  description: (
	      <div>
		<p>ほかの耕作員が先に更新してゐます．</p>
		<p>現在の変更をどう扱ひますか?</p>
	      </div>),
	  choices: [...(response?.data?.mergeable ? [{ value: 'merge', label: '差分をマージ' }] : []),
		    { value: 'overwrite', label: '強制上書き', variant: 'danger' }] })

      if (action === 'merge')
	{
	  // TODO: 差分 UI
	  await update ({ id: post.id, title, tags, parentPostIds,
			  originalCreatedFrom, originalCreatedBefore },
			{ baseVersionNo: post.versionNo, merge: true })
	  return
	}

      if (action === 'overwrite')
	{
	  await update ({ id: post.id, title, tags, parentPostIds,
			  originalCreatedFrom, originalCreatedBefore },
			{ baseVersionNo: post.versionNo, force: true })
	  return
	}
    }
  }

  const handleSubmit = async (e: FormEvent) => {
    e.preventDefault ()

    setDisabled (true)
    try
    {
      await update ({ id: post.id, title, tags, parentPostIds,
		      originalCreatedFrom, originalCreatedBefore },
		    { baseVersionNo: post.versionNo })
    }
    finally
    {
      setDisabled (false)
    }
  }

  useEffect (() => {
    setTags(tagsToStr (post.tags))
  }, [post])

  return (
      <form onSubmit={handleSubmit} className="max-w-xl pt-2 space-y-4">
	<FieldError messages={baseErrors}/>

	{/* タイトル */}
	<FormField label="タイトル">
	  {({ invalid }) => (
	      <input
		  type="text"
		  disabled={disabled}
		  className={inputClass (invalid)}
		  value={title ?? ''}
		  onChange={ev => setTitle (ev.target.value)}/>)}
	</FormField>

	{/* 親投稿 */}
	<FormField label="親投稿" messages={fieldErrors.parentPostIds}>
	  {({ describedBy, invalid }) => (
	      <input
		  type="text"
		  disabled={disabled}
		  value={parentPostIds}
		  onChange={e => setParentPostIds (e.target.value)}
		  aria-describedby={describedBy}
		  aria-invalid={invalid}
		  className={inputClass (invalid)}/>)}
	</FormField>

	{/* タグ */}
	<PostFormTagsArea
	    disabled={disabled}
	    tags={tags}
	    setTags={setTags}
	    errors={fieldErrors.tags}/>

	{/* オリジナルの作成日時 */}
	<PostOriginalCreatedTimeField
	    disabled={disabled}
	    originalCreatedFrom={originalCreatedFrom}
	    setOriginalCreatedFrom={setOriginalCreatedFrom}
	    originalCreatedBefore={originalCreatedBefore}
	    setOriginalCreatedBefore={setOriginalCreatedBefore}
	    errors={fieldErrors.originalCreatedAt}/>

	{/* 送信 */}
	<Button type="submit" disabled={disabled}>
	  更新
	</Button>
      </form>)
}

export default PostEditForm
