import { useEffect, useState } from 'react'
import { Helmet } from 'react-helmet-async'
import { useParams } from 'react-router-dom'

import TagLink from '@/components/TagLink'
import WikiBody from '@/components/WikiBody'
import FieldError from '@/components/common/FieldError'
import FormField from '@/components/common/FormField'
import PageTitle from '@/components/common/PageTitle'
import TabGroup, { Tab } from '@/components/common/TabGroup'
import TagInput from '@/components/common/TagInput'
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, apiPut } from '@/lib/api'
import { inputClass } from '@/lib/utils'
import { useValidationErrors } from '@/lib/useValidationErrors'

import type { FC } from 'react'

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

type MaterialWithTag = Material & { tag: Tag }

type MaterialFormField = 'tag' | 'file' | 'url'


const MaterialDetailPage: FC = () => {
  const { id } = useParams ()

  const [file, setFile] = useState<File | null> (null)
  const [filePreview, setFilePreview] = useState ('')
  const [loading, setLoading] = useState (false)
  const [material, setMaterial] = useState<MaterialWithTag | null> (null)
  const [sending, setSending] = useState (false)
  const [tag, setTag] = useState ('')
  const [url, setURL] = useState ('')
  const { baseErrors, fieldErrors, clearValidationErrors, applyValidationError } =
      useValidationErrors<MaterialFormField> ()

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

    const formData = new FormData
    if (tag.trim ())
      formData.append ('tag', tag)
    if (file)
      formData.append ('file', file)
    if (url.trim ())
      formData.append ('url', url)

    try
    {
      setSending (true)
      const data = await apiPut<Material> (`/materials/${ id }`, formData)
      setMaterial (data)
      toast ({ title: '更新成功!' })
    }
    catch (e)
    {
      applyValidationError (e)
      toast ({ title: '更新失敗……', description: '入力を見直してください．' })
    }
    finally
    {
      setSending (false)
    }
  }

  useEffect (() => {
    if (!(id))
      return

    void (async () => {
      try
      {
	setLoading (true)
	const data = await apiGet<MaterialWithTag> (`/materials/${ id }`)
	setMaterial (data)
	setTag (data.tag.name)
	if (data.file && data.contentType)
	  {
	    setFilePreview (data.file)
	    setFile (new File ([await (await fetch (data.file)).blob ()],
			       data.file,
			       { type: data.contentType }))
	  }
	setURL (data.url ?? '')
      }
      finally
      {
	setLoading (false)
      }
    }) ()
  }, [id])

  return (
      <MainArea>
	{material && (
	    <Helmet>
	      <title>{`${ material.tag.name } 素材照会 | ${ SITE_TITLE }`}</title>
	    </Helmet>)}

	{loading ? 'Loading...' : (material && (
	    <>
	      <PageTitle>
		<TagLink
		    tag={material.tag}
		    withWiki={false}
		    withCount={false}/>
	      </PageTitle>

	      {(material.file && material.contentType) && (
		  (/image\/.*/.test (material.contentType) && (
		      <img src={material.file} alt={material.tag.name || undefined}/>))
		  || (/video\/.*/.test (material.contentType) && (
		      <video src={material.file} controls/>))
		  || (/audio\/.*/.test (material.contentType) && (
		      <audio src={material.file} controls/>)))}

	      <TabGroup>
		<Tab name="Wiki">
		  <WikiBody
		      title={material.tag.name}
		      body={material.wikiPageBody ?? undefined}/>
		</Tab>

		<Tab name="編輯">
		  <div className="max-w-wl pt-2 space-y-4">
		    <FieldError messages={baseErrors}/>

		    {/* タグ */}
		    <FormField label="タグ" messages={fieldErrors.tag}>
		      {({ describedBy, invalid }) => (
			  <TagInput
			      describedBy={describedBy}
			      invalid={invalid}
			      value={tag}
			      setValue={setTag}/>)}
		    </FormField>

		    {/* ファイル */}
		    <FormField label="ファイル" messages={fieldErrors.file}>
		      {({ describedBy, invalid }) => (
			  <>
			    <input
				type="file"
				accept="image/*,video/*,audio/*"
				aria-describedby={describedBy}
				aria-invalid={invalid}
				onChange={e => {
				  const f = e.target.files?.[0]
				  setFile (f ?? null)
				  setFilePreview (f ? URL.createObjectURL (f) : '')
				}}/>
			    {(file && filePreview) && (
				(/image\/.*/.test (file.type) && (
				    <img
					src={filePreview}
					alt="preview"
					className="mt-2 max-h-48 rounded border"/>))
				|| (/video\/.*/.test (file.type) && (
				    <video
					src={filePreview}
					controls
					className="mt-2 max-h-48 rounded border"/>))
				|| (/audio\/.*/.test (file.type) && (
				    <audio
					src={filePreview}
					controls
					className="mt-2 max-h-48"/>))
				|| (
				    <p className="text-red-600 dark:text-red-400">
				      その形式のファイルには対応していません．
				    </p>))}
			  </>)}
		    </FormField>

		    {/* 参考 URL */}
		    <FormField label="参考 URL" messages={fieldErrors.url}>
		      {({ describedBy, invalid }) => (
			  <input
			      type="url"
			      value={url}
			      onChange={e => setURL (e.target.value)}
			      aria-describedby={describedBy}
			      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={sending}>
		      更新
		    </Button>
		  </div>
		</Tab>
	      </TabGroup>
	    </>))}
      </MainArea>)
}

export default MaterialDetailPage
