import axios from 'axios'
import toCamel from 'camelcase-keys'
import { useEffect, useRef, useState } from 'react'
import { FaThumbsDown, FaThumbsUp } from 'react-icons/fa'
import { useParams, useSearchParams } from 'react-router-dom'

import ThreadCanvas from '@/components/threads/ThreadCanvas'
import { API_BASE_URL } from '@/config'

import type { ThreadCanvasHandle } from '@/components/threads/ThreadCanvas'
import type { Post, Thread } from '@/types'

const Sort = {
    Newest:   'newest',
    Oldest:   'oldest',
    Likes:    'likes',
    Dislikes: 'dislikes' } as const
type Sort = (typeof Sort)[keyof typeof Sort]


const reformPost = (post: Post): Post => ({
    ...post,
    createdAt:  (new Date (post.createdAt)).toLocaleString ('ja-JP-u-ca-japanese') })


export default () => {
  const { id } = useParams ()

  const canvasRef = useRef<ThreadCanvasHandle> (null)

  const [searchParams, setSearchParams] = useSearchParams ()
  const sort = searchParams.get ('sort') ?? 'created_at'
  const order = searchParams.get ('order') ?? 'desc'
  const sortKey = (() => {
    if (sort === 'score')
      {
	if (order === 'asc')
	  return Sort.Dislikes
	return Sort.Likes
      }
    else
      {
	if (order === 'asc')
	  return Sort.Oldest
	return Sort.Newest
      }
  }) ()

  const [loading, setLoading] = useState (true)
  const [message, setMessage] = useState ('')
  const [name, setName] = useState ('')
  const [password, setPassword] = useState ('')
  const [posts, setPosts] = useState<Post[]> ([])
  const [sending, setSending] = useState (false)
  const [thread, setThread] = useState<Thread | null> (null)
  const [withImage, setWithImage] = useState (false)

  const handleSend = async () => {
    if (!(message) && !(withImage))
      {
	alert ('メッセージ書くか画像描くかどっちかはしろ．')
	return
      }

    setSending (true)
    try
    {
      const form = new FormData
      if (withImage)
	{
	  if (!(canvasRef.current))
	    return
	  const { dataURL } = await canvasRef.current.exportAll ()
	  const blob = await (await fetch (dataURL)).blob ()
	  form.append ('image', new File ([blob], `kekec_${ Date.now () }.png`, { type: 'image/png' }))
	}
      form.append ('name', name)
      form.append ('message', message)
      form.append ('password', password)
      const res = await axios.post (`${ API_BASE_URL }/threads/${ id }/posts`,
				    form,
				    { headers: { 'Content-Type': 'multipart/form-data' } })
      const data: Post = toCamel (res.data as any, { deep: true })
      setPosts(prev => [reformPost (data), ...prev])

      setMessage ('')
      if (withImage)
	{
	  canvasRef.current!.clear ()
	  setWithImage (false)
	}

      localStorage.setItem ('name', name)
      localStorage.setItem ('password', password)
    }
    finally
    {
      setSending (false)
    }
  }

  useEffect (() => {
    const nameRaw = localStorage.getItem ('name')
    nameRaw && setName (nameRaw)

    const passRaw = localStorage.getItem ('password')
    passRaw && setPassword (passRaw)

    const fetchThread = async () => {
      try
      {
	const res = await axios.get (`${ API_BASE_URL }/threads/${ id }`)
	setThread (toCamel (res.data as any, { deep: true }) as Thread)
      }
      catch
      {
	setThread (null)
      }
    }
    fetchThread ()
  }, [])

  useEffect (() => {
    const fetchPosts = async () => {
      try
      {
	const res = await axios.get (`${ API_BASE_URL }/threads/${ id }/posts`,
				     { params: { sort, order } })
	const data = toCamel (res.data as any, { deep: true }) as Post[]
	setPosts (data.map (reformPost))
      }
      catch
      {
	setPosts ([])
      }
      setLoading (false)
    }
    fetchPosts ()
  }, [sortKey])

  useEffect (() => {
    if (!(thread))
      return
    document.title = `${ thread.name } - キケッツチャンネル お絵描き掲示板`
  }, [thread])

  return (
      <>
	<div className="bg-white dark:bg-gray-800 p-3 m-4
			border border-gray-400 rounded-xl
			mb-8">
	  <div className="px-2 py-1 text-bold">
	    {thread?.name}
	  </div>
	  {thread?.description && (
	      <div className="px-4 my-2">
		{thread.description.replaceAll ('\r\n', '\n')
				   .replaceAll ('\r', '\n')
				   .split ('\n')
				   .map ((l, i) => <p key={i}>{l}</p>)}
	      </div>)}
	</div>

	<div className="mb-16">
	  <div>
	    <label>名前：</label>
	    <input type="text"
		   value={name}
		   onChange={ev => setName (ev.target.value)} />
	  </div>
	  <div>
	    <label>削除用パスワード：</label>
	    <input type="password"
		   value={password}
		   onChange={ev => setPassword (ev.target.value)} />
	  </div>
	  <div>
	    <label>メッセージ：</label>
	    <textarea value={message}
		      onChange={ev => setMessage (ev.target.value)} />
	  </div>
	  <div>
	    <label>
	      <input type="checkbox"
		     checked={withImage}
		     onChange={ev => setWithImage (ev.target.checked)} />
	      イラストを投稿に含める
	    </label>
	  </div>
	  <ThreadCanvas ref={canvasRef} visible={withImage} />
	  <div>
	    <button onClick={handleSend} disabled={sending}>
	      {sending ? '送信中...' : '送信'}
	    </button>
	  </div>
	</div>
	{loading ? 'Loading...' : (
	    posts.length > 0
	    ? (
		<>
		  <div>
		    <label>
		      並べ替え：
		    <select value={sortKey} onChange={ev => {
		      switch (ev.target.value)
		      {
		      case Sort.Newest:
			searchParams.set ('sort', 'created_at')
			searchParams.set ('order', 'desc')
			break
		      case Sort.Likes:
			searchParams.set ('sort', 'score')
			searchParams.set ('order', 'desc')
			break
		      case Sort.Dislikes:
			searchParams.set ('sort', 'score')
			searchParams.set ('order', 'asc')
			break
		      case Sort.Oldest:
			searchParams.set ('sort', 'created_at')
			searchParams.set ('order', 'asc')
			break
		      }
		      setSearchParams (searchParams)
		    }}>
		      <option value={Sort.Newest}>投稿が新しい順</option>
		      <option value={Sort.Likes}>評価が高い順</option>
		      <option value={Sort.Dislikes}>評価が低い順</option>
		      <option value={Sort.Oldest}>投稿が古い順</option>
		    </select>
		  </label>
		</div>
		  {posts.map (post => (
		      <div key={post.id}
			   id={post.postNo.toString ()}
			   className="bg-white dark:bg-gray-800 p-3 m-4
				      border border-gray-400 rounded-xl
				      text-center">
			<div className="flex justify-between items-center px-2 py-1">
			  <span>{post.postNo}: {post.name || '名なしさん'}</span>
			  <span>{post.createdAt}</span>
			</div>
			<div className="flex items-center px-4 pt-1 pb-3">
			  <a className="text-blue-600 dark:text-blue-300 mr-1 whitespace-nowrap"
			     href="#"
			     onClick={async ev => {
			       ev.preventDefault ()
			       try
			       {
				 await axios.post (`${ API_BASE_URL }/posts/${ post.id }/bad`)
				 setPosts (prev => prev.map (p => (p.id === post.id
								   ? { ...p, bad: p.bad + 1 }
								   : p)))
			       }
			       catch
			       {
				 ;
			       }
			     }}>
			    <FaThumbsDown className="inline" /> {post.bad}
			  </a>
			  <div className="h-2 bg-blue-600 dark:bg-blue-300"
			       style={{ width: `${ post.good + post.bad === 0
						   ? 50
						   : post.bad * 100 / (post.good + post.bad) }%` }}>
			  </div>
			  <div className="h-2 bg-red-600 dark:bg-red-300"
			       style={{ width: `${ post.good + post.bad === 0
						   ? 50
						   : post.good * 100 / (post.good + post.bad) }%` }}>
			  </div>
			  <a className="text-red-600 dark:text-red-300 ml-1 whitespace-nowrap"
			     href="#"
			     onClick={async ev => {
			       ev.preventDefault ()
			       try
			       {
				 await axios.post (`${ API_BASE_URL }/posts/${ post.id }/good`)
				 setPosts (prev => prev.map (p => (p.id === post.id
								   ? { ...p, good: p.good + 1 }
								   : p)))
			       }
			       catch
			       {
				 ;
			       }
			     }}>
			    {post.good} <FaThumbsUp className="inline" />
			  </a>
			</div>
			{post.deletedAt
			 ? <em className="font-bold">削除されました．</em>
			 : (
			    <>
			      {post.message && (
				  <div className="text-left px-4 my-2">
				    {post.message}
				  </div>)}
			      {post.imageUrl && (
				  <div className="bg-white inline-block border border-black dark:border-white">
				    <img src={`${ API_BASE_URL }${ post.imageUrl }`} />
				  </div>)}
			    </>)}
		      </div>))}
		</>)
	    : 'レスないよ（笑）．')}
      </>)
}
