import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Helmet } from 'react-helmet-async'
import { useNavigate } from 'react-router-dom'

import PostFormTagsArea from '@/components/PostFormTagsArea'
import PostOriginalCreatedTimeField from '@/components/PostOriginalCreatedTimeField'
import FieldError from '@/components/common/FieldError'
import Form from '@/components/common/Form'
import FormField from '@/components/common/FormField'
import PageTitle from '@/components/common/PageTitle'
import MainArea from '@/components/layout/MainArea'
import { Button } from '@/components/ui/button'
import { toast } from '@/components/ui/use-toast'
import { SITE_TITLE } from '@/config'
import { apiGet, apiPost } from '@/lib/api'
import { canEditContent } from '@/lib/users'
import { inputClass } from '@/lib/utils'
import { useValidationErrors } from '@/lib/useValidationErrors'
import Forbidden from '@/pages/Forbidden'

import type { FC } from 'react'

import type { User } from '@/types'

type Props = { user: User | null }

type PostFormField =
    'url'
    | 'title'
    | 'tags'
    | 'parentPostIds'
    | 'videoMs'
    | 'originalCreatedAt'
    | 'thumbnail'


const PostNewPage: FC<Props> = ({ user }) => {
  const editable = canEditContent (user)

  const navigate = useNavigate ()

  const { baseErrors, fieldErrors, clearValidationErrors, applyValidationError } =
      useValidationErrors<PostFormField> ()

  const [originalCreatedBefore, setOriginalCreatedBefore] =
      useState<string | null> (null)
  const [originalCreatedFrom, setOriginalCreatedFrom] =
      useState<string | null> (null)
  const [parentPostIds, setParentPostIds] = useState ('')
  const [tags, setTags] = useState ('')
  const [duration, setDuration] = useState ('')
  const [thumbnailFile, setThumbnailFile] = useState<File | null> (null)
  const [thumbnailLoading, setThumbnailLoading] = useState (false)
  const [thumbnailPreview, setThumbnailPreview] = useState<string> ('')
  const [title, setTitle] = useState ('')
  const [titleLoading, setTitleLoading] = useState (false)
  const [url, setURL] = useState ('')

  const thumbnailPreviewRef = useRef ('')
  const videoFlg =
      useMemo (() =>
	  tags.split (/\s+/).some (
	      tag => tag.replace (/\[.*\]$/, '') === '動画'),
	       [tags])

  const handleSubmit = async () => {
    clearValidationErrors ()

    const formData = new FormData
    formData.append ('title', title)
    formData.append ('url', url)
    formData.append ('tags', tags)
    formData.append ('parent_post_ids', parentPostIds)
    if (videoFlg && duration !== '')
      formData.append ('duration', duration)
    if (thumbnailFile)
      formData.append ('thumbnail', thumbnailFile)
    if (originalCreatedFrom)
      formData.append ('original_created_from', originalCreatedFrom)
    if (originalCreatedBefore)
      formData.append ('original_created_before', originalCreatedBefore)

    try
    {
      await apiPost ('/posts', formData,
		     { headers: { 'Content-Type': 'multipart/form-data' } })
      toast ({ title: '投稿成功!' })
      navigate ('/posts')
    }
    catch (e)
    {
      applyValidationError (e)
      toast ({ title: '投稿失敗', description: '入力を確認してください．' })
    }
  }

  const fetchTitle = useCallback (async () => {
    setTitleLoading (true)
    try
    {
      const data = await apiGet<{ title: string }> ('/preview/title',
						    { params: { url } })
      setTitle (data.title || '')
    }
    finally
    {
      setTitleLoading (false)
    }
  }, [url])

  const fetchThumbnail = useCallback (async () => {
    setThumbnailPreview ('')
    setThumbnailFile (null)
    setThumbnailLoading (true)
    if (thumbnailPreviewRef.current)
      URL.revokeObjectURL (thumbnailPreviewRef.current)
    try
    {
      const data = await apiGet<Blob> ('/preview/thumbnail',
				       { params: { url },
					 responseType: 'blob' })
      const imageURL = URL.createObjectURL (data)
      setThumbnailPreview (imageURL)
      setThumbnailFile (new File ([data],
				  'thumbnail.png',
				  { type: data.type || 'image/png' }))
    }
    finally
    {
      setThumbnailLoading (false)
    }
  }, [url])

  useEffect (() => {
    thumbnailPreviewRef.current = thumbnailPreview
  }, [thumbnailPreview])

  if (!(editable))
    return <Forbidden/>

  return (
      <MainArea>
	<Helmet>
	  <title>{`広場に投稿を追加 | ${ SITE_TITLE }`}</title>
	</Helmet>
	<Form>
	  <PageTitle>広場に投稿を追加する</PageTitle>
	  <FieldError messages={baseErrors}/>

	  {/* URL */}
	  <FormField label="URL" messages={fieldErrors.url}>
	    {({ describedBy, invalid }) => (
		<input type="url"
		       placeholder="例：https://www.nicovideo.jp/watch/..."
		       value={url}
		       onChange={e => setURL (e.target.value)}
		       aria-describedby={describedBy}
		       aria-invalid={invalid}
		       className={inputClass (invalid)}/>)}
	  </FormField>

	  {/* タイトル */}
	  <FormField label="タイトル" messages={fieldErrors.title}>
	    {({ describedBy, invalid }) => (
		<div className="space-y-2">
		  <input type="text"
			 aria-describedby={describedBy}
			 aria-invalid={invalid}
			 className={inputClass (invalid)}
			 value={title}
			 placeholder={titleLoading ? 'Loading...' : ''}
			 onChange={ev => setTitle (ev.target.value)}
			 disabled={titleLoading}/>
		  <div className="flex flex-wrap items-center gap-2 text-sm">
		    <span>必要なタイミングで URL から取得できます．</span>
		    <Button
			type="button"
			variant="outline"
			onClick={fetchTitle}
			disabled={!(url) || titleLoading}>
		      取得
		    </Button>
		  </div>
		</div>)}
	  </FormField>

	  {/* サムネール */}
	  <FormField label="サムネール" messages={fieldErrors.thumbnail}>
	    {({ describedBy, invalid }) => (
		<>
		  <div className="mb-2 flex flex-wrap items-center gap-2 text-sm">
		    <span>必要なタイミングで URL から取得できます．</span>
		    <Button
			type="button"
			variant="outline"
			onClick={fetchThumbnail}
			disabled={!(url) || thumbnailLoading}>
		      取得
		    </Button>
		  </div>
		  {thumbnailLoading && (
		      <p className="text-gray-500 text-sm">Loading...</p>)}
		  <input type="file"
			 accept="image/*"
			 aria-describedby={describedBy}
			 aria-invalid={invalid}
			 onChange={e => {
			   const file = e.target.files?.[0]
			   if (file)
			     {
			       setThumbnailFile (file)
			       setThumbnailPreview (URL.createObjectURL (file))
			     }
			 }}/>
		  {thumbnailPreview && (
		      <img src={thumbnailPreview}
			   alt="preview"
			   className="mt-2 max-h-48 rounded border"/>)}
		</>)}
	  </FormField>

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

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

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

	  {/* 動画時間 */}
	  {(videoFlg &&
	      <FormField label="動画時間" messages={fieldErrors.videoMs}>
		{({ invalid }) => (
		    <input
			type="number"
			min="0.001"
			step="0.001"
			value={duration}
			onChange={e => setDuration (e.target.value)}
			aria-invalid={invalid}
			className={inputClass (invalid)}/>)}
	      </FormField>)}

	  {/* 送信 */}
	  <Button onClick={handleSubmit}
		  className="px-4 py-2 bg-blue-600 text-white rounded disabled:bg-gray-400"
		  disabled={titleLoading || thumbnailLoading}>
	    追加
	  </Button>
	</Form>
      </MainArea>)
}

export default PostNewPage
