import { useCallback, useEffect, useState, useRef } 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' | '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 [thumbnailAutoFlg, setThumbnailAutoFlg] = useState (true)
  const [thumbnailFile, setThumbnailFile] = useState<File | null> (null)
  const [thumbnailLoading, setThumbnailLoading] = useState (false)
  const [thumbnailPreview, setThumbnailPreview] = useState<string> ('')
  const [title, setTitle] = useState ('')
  const [titleAutoFlg, setTitleAutoFlg] = useState (true)
  const [titleLoading, setTitleLoading] = useState (false)
  const [url, setURL] = useState ('')

  const previousURLRef = useRef ('')
  const thumbnailPreviewRef = useRef ('')

  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 (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 handleURLBlur = () => {
    if (!(url) || url === previousURLRef.current)
      return

    if (titleAutoFlg)
      fetchTitle ()
    if (thumbnailAutoFlg)
      fetchThumbnail ()
    previousURLRef.current = url
  }

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

  const fetchThumbnail = useCallback (async () => {
    setThumbnailPreview ('')
    setThumbnailFile (null)
    setThumbnailLoading (true)
    if (thumbnailPreviewRef.current)
      URL.revokeObjectURL (thumbnailPreviewRef.current)
    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' }))
    setThumbnailLoading (false)
  }, [url])

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

  useEffect (() => {
    if (titleAutoFlg && url)
      fetchTitle ()
  }, [fetchTitle, titleAutoFlg, url])

  useEffect (() => {
    if (thumbnailAutoFlg && url)
      fetchThumbnail ()
  }, [fetchThumbnail, thumbnailAutoFlg, url])

  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)}
		       onBlur={handleURLBlur}/>)}
	  </FormField>

	  {/* タイトル */}
	  <FormField
	      checkBox={{
		  label:    '自動',
		  checked:  titleAutoFlg,
		  onChange: ev => setTitleAutoFlg (ev.target.checked)}}
	      label="タイトル"
	      messages={fieldErrors.title}>
	    {({ describedBy, invalid }) => (
		<input type="text"
		       aria-describedby={describedBy}
		       aria-invalid={invalid}
		       className={inputClass (invalid)}
		       value={title}
		       placeholder={titleLoading ? 'Loading...' : ''}
		       onChange={ev => setTitle (ev.target.value)}
		       disabled={titleAutoFlg}/>)}
	  </FormField>

	  {/* サムネール */}
	  <FormField
	      checkBox={{
		  label:    '自動',
		  checked:  thumbnailAutoFlg,
		  onChange: ev => setThumbnailAutoFlg (ev.target.checked)}}
	      label="サムネール"
	      messages={fieldErrors.thumbnail}>
	    {({ describedBy, invalid }) => (
		<>
		  {thumbnailAutoFlg
		   ? (thumbnailLoading
		      ? <p className="text-gray-500 text-sm">Loading...</p>
		      : !(thumbnailPreview) && (
			  <p className="text-gray-500 text-sm">
			    URL から自動取得されます。
			  </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}/>

	  {/* 送信 */}
	  <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
