import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
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 {
    fetchMaterial,
    updateMaterial,
} from '@/lib/materials'
import { materialsKeys } from '@/lib/queryKeys'
import { inputClass } from '@/lib/utils'
import { useValidationErrors } from '@/lib/useValidationErrors'

import type { FC } from 'react'

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


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

  const [exportPath, setExportPath] = useState ('')
  const [file, setFile] = useState<File | null> (null)
  const [filePreview, setFilePreview] = useState ('')
  const [tag, setTag] = useState ('')
  const [url, setURL] = useState ('')
  const { baseErrors, fieldErrors, clearValidationErrors, applyValidationError } =
      useValidationErrors<MaterialFormField> ()

  const { data: material, isError, isLoading } = useQuery ({
      queryKey: materialsKeys.show (id ?? ''),
      queryFn:  () => fetchMaterial (id ?? ''),
      enabled:  id != null})
  const materialTitle = material
      ? material.tag?.name ?? `素材 #${ material.id }`
      : ''

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

    setTag (material.tag?.name ?? '')
    setURL (material.url ?? '')
    setExportPath (material.exportPaths.legacyDrive ?? '')
    if (material.file && material.contentType)
      {
	setFilePreview (material.file)
	setFile (null)
      }
  }, [material])

  const invalidateMaterialQueries = async () => {
    await qc.invalidateQueries ({ queryKey: materialsKeys.root })
  }

  const updateMutation = useMutation ({
      mutationFn: async () => {
	const formData = new FormData
	if (tag.trim ())
	  formData.append ('tag', tag)
	if (file)
	  formData.append ('file', file)
	if (url.trim ())
	  formData.append ('url', url)
	formData.append ('export_paths[legacy_drive]', exportPath)

	return await updateMaterial (id ?? '', formData)
      },
      onSuccess: async data => {
	qc.setQueryData (materialsKeys.show (id ?? ''), data)
	await invalidateMaterialQueries ()
	toast ({ title: '更新成功!' })
      },
      onError: error => {
	applyValidationError (error)
	toast ({ title: '更新失敗……', description: '入力を見直してください．' })
      }})

  const handleSubmit = () => {
    clearValidationErrors ()
    updateMutation.mutate ()
  }

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

	{isLoading ? 'Loading...' : isError ? (
	    <p className="text-red-600 dark:text-red-300">
	      素材の取得に失敗しました．
	    </p>) : material == null ? (
	    <p className="text-stone-700 dark:text-stone-300">
	      素材が見つかりませんでした．
	    </p>) : (
	    <>
	      <PageTitle>
		{material.tag
		 ? (
		    <TagLink
			tag={material.tag}
			withWiki={false}
			withCount={false}/>)
		 : materialTitle}
	      </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>
		{material.tag && (
		    <Tab name="Wiki">
		      <WikiBody
			  title={material.tag.name}
			  body={material.wikiPageBody ?? undefined}/>
		    </Tab>)}

		<Tab name="編輯">
		  <div className="max-w-wl space-y-4 pt-2">
		    <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 nextFile = e.target.files?.[0]
				  setFile (nextFile ?? null)
				  setFilePreview (
				      nextFile ? URL.createObjectURL (nextFile) : '')
				}}/>
			    {(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>

		    <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>

		    <FormField label="ZIP 出力パス" messages={fieldErrors.exportPaths}>
		      {({ describedBy, invalid }) => (
			  <input
			      type="text"
			      value={exportPath}
			      onChange={e => setExportPath (e.target.value)}
			      placeholder="伊地知ニジカ/表情/泣き.png"
			      aria-describedby={describedBy}
			      aria-invalid={invalid}
			      className={inputClass (invalid)}/>)}
		    </FormField>

		    <div className="flex flex-wrap gap-2">
		      <Button
			  onClick={handleSubmit}
			  className="rounded bg-blue-600 px-4 py-2 text-white
				     disabled:bg-gray-400"
			  disabled={updateMutation.isPending}>
			更新
		      </Button>
		    </div>
		  </div>
		</Tab>
	      </TabGroup>
	    </>)}
      </MainArea>)
}

export default MaterialDetailPage
