import { useEffect, useMemo, useState } from 'react'

import PostOriginalCreatedTimeField from '@/components/PostOriginalCreatedTimeField'
import FieldError from '@/components/common/FieldError'
import PostDurationField from '@/components/posts/PostDurationField'
import PostTagsField from '@/components/posts/PostTagsField'
import PostTextField from '@/components/posts/PostTextField'
import { Button } from '@/components/ui/button'
import { toast } from '@/components/ui/use-toast'
import { isApiError } from '@/lib/api'
import useDialogue from '@/lib/dialogues/useDialogue'
import { updatePost } from '@/lib/posts'
import { msToTime } from '@/lib/utils'
import { useValidationErrors } from '@/lib/useValidationErrors'

import type { FC, FormEvent } from 'react'

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

type PostFormField =
    'parentPostIds' | 'tags' | 'videoMs'
    | 'originalCreatedAt' | 'originalCreatedFrom' | 'originalCreatedBefore'

const groupedMessages = (...values: (string[] | undefined)[]): string[] =>
    [...new Set (values.flatMap (value => value ?? []))]

const videoMsToDurationValue = (videoMs: number | null): string =>
    videoMs == null ? '' : String (videoMs / 1_000)


const tagsToStr = (tags: TagWithSections[]): string => {
  const result: Omit<TagWithSections, 'children'>[] = []

  const walk = (tag: TagWithSections) => {
    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 }${ t.sections
		     .map (s => `[${ msToTime (s.beginMs) }-${ s.endMs == null ? '' : msToTime (s.endMs) }]`)
		     .join ('') }`)))].join (' ')
}


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


const PostEditForm: FC<Props> = ({ post, onSave }) => {
  const [disabled, setDisabled] = useState (false)
  const [duration, setDuration] = useState<string> (videoMsToDurationValue (post.videoMs))
  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 videoFlg =
      useMemo (() => tags.split (/\s+/).some (tag => tag.replace (/\[.*\]$/, '') === '動画'),
	       [tags])

  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,
		videoMs:                data.videoMs,
		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,
			  duration: videoFlg ? duration : null,
			  originalCreatedFrom, originalCreatedBefore },
			{ baseVersionNo: post.versionNo, merge: true })
	  return
	}

      if (action === 'overwrite')
	{
	  await update ({ id: post.id, title, tags, parentPostIds,
			  duration: videoFlg ? duration : null,
			  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,
		      duration: videoFlg ? duration : null,
		      originalCreatedFrom, originalCreatedBefore },
		    { baseVersionNo: post.versionNo })
    }
    finally
    {
      setDisabled (false)
    }
  }

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

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

	<PostTextField
	    label="タイトル"
	    value={title ?? ''}
	    disabled={disabled}
	    onChange={setTitle}/>

	<PostTextField
	    label="親投稿"
	    value={parentPostIds}
	    disabled={disabled}
	    errors={fieldErrors.parentPostIds}
	    onChange={setParentPostIds}/>

	<PostTagsField
	    disabled={disabled}
	    tags={tags}
	    setTags={setTags}
	    errors={fieldErrors.tags}/>

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

	{videoFlg && (
	    <PostDurationField
		value={duration}
		disabled={disabled}
		errors={fieldErrors.videoMs}
		onChange={setDuration}/>)}

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


export default PostEditForm
