import { useQuery } from '@tanstack/react-query'
import { motion } from 'framer-motion'
import { useEffect, useMemo, useState } from 'react'
import { Helmet } from 'react-helmet-async'
import { useLocation, useNavigate } from 'react-router-dom'

import PrefetchLink from '@/components/PrefetchLink'
import SortHeader from '@/components/SortHeader'
import TagLink from '@/components/TagLink'
import DateTimeField from '@/components/common/DateTimeField'
import FormField from '@/components/common/FormField'
import PageTitle from '@/components/common/PageTitle'
import Pagination from '@/components/common/Pagination'
import TagInput from '@/components/common/TagInput'
import MainArea from '@/components/layout/MainArea'
import { SITE_TITLE } from '@/config'
import { clientAnimationTransition,
	 clientScrollBehaviour } from '@/lib/clientAnimation'
import { fetchPosts } from '@/lib/posts'
import { postsKeys } from '@/lib/queryKeys'
import {
    DEFAULT_POST_LIST_LIMIT,
    DEFAULT_POST_LIST_ORDER,
    getClientListLimit,
    getClientListOrder,
    LIST_LIMIT_OPTIONS,
    setClientListSettings,
} from '@/lib/settings'
import { useClientBehaviourSettings } from '@/lib/useClientBehaviourSettings'
import { useKeyboardShortcuts } from '@/lib/useKeyboardShortcuts'
import { dateString, inputClass, originalCreatedAtString } from '@/lib/utils'

import type { FC, FormEvent } from 'react'

import type { FetchPostsOrder,
	      FetchPostsOrderField,
	      FetchPostsParams } from '@/types'


const setIf = (qs: URLSearchParams, k: string, v: string | null) => {
  const t = v?.trim ()
  if (t)
    qs.set (k, t)
}

const parseLimit = (value: string | null): 20 | 50 | 100 | null => {
  const n = Number (value)
  return n === 20 || n === 50 || n === 100 ? n : null
}

const parseOrder = (value: string | null): FetchPostsOrder | null => {
  switch (value)
  {
  case 'title:asc':
  case 'title:desc':
  case 'url:asc':
  case 'url:desc':
  case 'original_created_at:asc':
  case 'original_created_at:desc':
  case 'created_at:asc':
  case 'created_at:desc':
  case 'updated_at:asc':
  case 'updated_at:desc':
    return value

  default:
    return null
  }
}


const PostSearchPage: FC = () => {
  const behaviourSettings = useClientBehaviourSettings ()
  const animationMode = behaviourSettings.animation ?? 'normal'
  const thumbnailTransition = clientAnimationTransition (
      animationMode,
      { normal: { type: 'spring', stiffness: 500, damping: 40, mass: .5 },
	reduced: { duration: .08, ease: 'linear' as const } },
  )
  const location = useLocation ()

  const navigate = useNavigate ()

  const query = useMemo (() => new URLSearchParams (location.search),
			 [location.search])

  const page = Number (query.get ('page') ?? 1)
  const limit = (
      parseLimit (query.get ('limit'))
      ?? getClientListLimit ('postSearch')
      ?? DEFAULT_POST_LIST_LIMIT
  )

  const qURL = query.get ('url') ?? ''
  const qTitle = query.get ('title') ?? ''
  const qTags = query.get ('tags') ?? ''
  const qMatch: 'all' | 'any' = query.get ('match') === 'any' ? 'any' : 'all'
  const qOriginalCreatedFrom = query.get ('original_created_from') ?? ''
  const qOriginalCreatedTo = query.get ('original_created_to') ?? ''
  const qCreatedFrom = query.get ('created_from') ?? ''
  const qCreatedTo = query.get ('created_to') ?? ''
  const qUpdatedFrom = query.get ('updated_from') ?? ''
  const qUpdatedTo = query.get ('updated_to') ?? ''
  const order = (
      parseOrder (query.get ('order'))
      ?? getClientListOrder<FetchPostsOrder> ('postSearch')
      ?? DEFAULT_POST_LIST_ORDER
  )

  const [createdFrom, setCreatedFrom] = useState<string | null> (null)
  const [createdTo, setCreatedTo] = useState<string | null> (null)
  const [matchType, setMatchType] = useState<'all' | 'any'> ('all')
  const [originalCreatedFrom, setOriginalCreatedFrom] = useState<string | null> (null)
  const [originalCreatedTo, setOriginalCreatedTo] = useState<string | null> (null)
  const [tagsStr, setTagsStr] = useState ('')
  const [title, setTitle] = useState ('')
  const [updatedFrom, setUpdatedFrom] = useState<string | null> (null)
  const [updatedTo, setUpdatedTo] = useState<string | null> (null)
  const [url, setURL] = useState ('')

  const keys: FetchPostsParams = {
      tags: qTags, match: qMatch, page, limit,
      url: qURL,
      title: qTitle,
      originalCreatedFrom: qOriginalCreatedFrom,
      originalCreatedTo: qOriginalCreatedTo,
      createdFrom: qCreatedFrom,
      createdTo: qCreatedTo,
      updatedFrom: qUpdatedFrom,
      updatedTo: qUpdatedTo,
      order }
  const { data, isLoading: loading } = useQuery ({
      queryKey: postsKeys.index (keys),
      queryFn: () => fetchPosts (keys) })
  const results = data?.posts ?? []
  const totalPages = data ? Math.ceil (data.count / limit) : 0

  useEffect (() => {
    setClientListSettings ('postSearch', { limit, order })
  }, [limit, order])

  useEffect (() => {
    setURL (qURL ?? '')
    setTitle (qTitle ?? '')
    setTagsStr (qTags ?? '')
    setMatchType (qMatch ?? 'all')
    setOriginalCreatedFrom (qOriginalCreatedFrom)
    setOriginalCreatedTo (qOriginalCreatedTo)
    setCreatedFrom (qCreatedFrom)
    setCreatedTo (qCreatedTo)
    setUpdatedFrom (qUpdatedFrom)
    setUpdatedTo (qUpdatedTo)

    document.querySelector ('table')?.scrollIntoView ({
	behavior: clientScrollBehaviour (animationMode) })
  }, [animationMode, location.search, qCreatedFrom, qCreatedTo, qMatch,
      qOriginalCreatedFrom, qOriginalCreatedTo, qTags, qTitle, qUpdatedFrom,
      qUpdatedTo, qURL])

  const search = async () => {
    const qs = new URLSearchParams ()
    setIf (qs, 'tags', tagsStr)
    setIf (qs, 'url', url)
    setIf (qs, 'title', title)
    setIf (qs, 'original_created_from', originalCreatedFrom)
    setIf (qs, 'original_created_to', originalCreatedTo)
    setIf (qs, 'created_from', createdFrom)
    setIf (qs, 'created_to', createdTo)
    setIf (qs, 'updated_from', updatedFrom)
    setIf (qs, 'updated_to', updatedTo)
    qs.set ('match', matchType)
    qs.set ('page', '1')
    qs.set ('order', order)
    qs.set ('limit', String (limit))
    navigate (`${ location.pathname }?${ qs.toString () }`)
  }

  const updateListQuery = (next: { limit?: 20 | 50 | 100
				   order?: FetchPostsOrder }) => {
    const qs = new URLSearchParams (location.search)
    qs.set ('limit', String (next.limit ?? limit))
    qs.set ('order', next.order ?? order)
    qs.set ('page', '1')
    navigate (`${ location.pathname }?${ qs.toString () }`)
  }

  const updatePage = (nextPage: number) => {
    const qs = new URLSearchParams (location.search)
    qs.set ('page', String (Math.max (1, nextPage)))
    navigate (`${ location.pathname }?${ qs.toString () }`)
  }

  const handleSearch = (e: FormEvent) => {
    e.preventDefault ()
    search ()
  }

  useKeyboardShortcuts (useMemo (() => ({
      ...(page > 1
	  ? {
	      'pagination.previous': () => {
		  updatePage (page - 1)
	      },
	  }
	  : { }),
      ...(totalPages > 0 && page < totalPages
	  ? {
	      'pagination.next': () => {
		  updatePage (Math.min (page + 1, totalPages))
	      },
	  }
	  : { }),
  }), [page, totalPages, location.search]))

  const defaultDirection = { title: 'asc',
			     url: 'asc',
			     original_created_at: 'desc',
			     created_at: 'desc',
			     updated_at: 'desc' } as const

  return (
      <MainArea>
	<Helmet>
	  <title>広場検索 | {SITE_TITLE}</title>
	</Helmet>

	<div className="max-w-xl">
	  <div className="flex flex-wrap items-center justify-between gap-3">
	    <PageTitle>広場検索</PageTitle>
	    <label className="flex items-center gap-2 text-xs text-muted-foreground">
	      <span>件数</span>
	      <select
		  className="rounded border border-border bg-background px-2 py-1 text-sm
			     text-foreground"
		  value={limit}
		  onChange={ev => updateListQuery ({
		      limit: Number (ev.target.value) as 20 | 50 | 100,
		  })}>
		{LIST_LIMIT_OPTIONS.map (value => (
		    <option key={value} value={value}>
		      {value}
		    </option>))}
	      </select>
	    </label>
	  </div>

	  <form onSubmit={handleSearch} className="space-y-2">
	    {/* タイトル */}
	    <FormField label="タイトル">
	      {({ invalid }) => (
		  <input
		      type="text"
		      data-shortcut-focus-search="true"
		      value={title}
		      onChange={e => setTitle (e.target.value)}
		      className={inputClass (invalid)}/>)}
	    </FormField>

	    {/* URL */}
	    <FormField label="URL">
	      {({ invalid }) => (
		  <input
		      type="text"
		      value={url}
		      onChange={e => setURL (e.target.value)}
		      className={inputClass (invalid)}/>)}
	    </FormField>

	    {/* タグ */}
	    <FormField label="タグ">
	      {() => (
		  <TagInput
		      includeNico={true}
		      value={tagsStr}
		      setValue={setTagsStr}/>)}
	    </FormField>
	    <div>
	      <fieldset className="w-full my-2">
		<label>検索区分：</label>
		<label className="mx-2">
		  <input
		      type="radio"
		      name="match-type"
		      checked={matchType === 'all'}
		      onChange={() => setMatchType ('all')}/>
		  AND
		</label>
		<label className="mx-2">
		  <input
		      type="radio"
		      name="match-type"
		      checked={matchType === 'any'}
		      onChange={() => setMatchType ('any')}/>
		  OR
		</label>
	      </fieldset>
	    </div>

	    {/* オリジナルの投稿日時 */}
	    <FormField label="オリジナルの投稿日時">
	      {() => (
		  <>
		    <DateTimeField
			value={originalCreatedFrom ?? undefined}
			onChange={setOriginalCreatedFrom}/>
		    <span className="mx-1">〜</span>
		    <DateTimeField
			value={originalCreatedTo ?? undefined}
			onChange={setOriginalCreatedTo}/>
		  </>)}
	    </FormField>

	    {/* 投稿日時 */}
	    <FormField label="投稿日時">
	      {() => (
		  <>
		    <DateTimeField
			value={createdFrom ?? undefined}
			onChange={setCreatedFrom}/>
		    <span className="mx-1">〜</span>
		    <DateTimeField
			value={createdTo ?? undefined}
			onChange={setCreatedTo}/>
		  </>)}
	    </FormField>

	    {/* 更新日時 */}
	    <FormField label="更新日時">
	      {() => (
		  <>
		    <DateTimeField
			value={updatedFrom ?? undefined}
			onChange={setUpdatedFrom}/>
		    <span className="mx-1">〜</span>
		    <DateTimeField
			value={updatedTo ?? undefined}
			onChange={setUpdatedTo}/>
		  </>)}
	    </FormField>

	    {/* 検索 */}
	    <div className="py-3">
	      <button
		  type="submit"
		  className="bg-blue-500 text-white px-4 py-2 rounded">
		検索
	      </button>
	    </div>
	  </form>
	</div>

	{loading ? 'Loading...' : (results.length > 0 ? (
	    <div className="mt-4">
	      <div className="overflow-x-auto">
		<table className="w-full min-w-[1200px] table-fixed border-collapse">
		  <colgroup>
		    <col className="w-14"/>
		    <col className="w-72"/>
		    <col className="w-80"/>
		    <col className="w-[24rem]"/>
		    <col className="w-60"/>
		    <col className="w-44"/>
		    <col className="w-44"/>
		  </colgroup>

		  <thead className="border-b-2 border-black dark:border-white">
		    <tr>
		      <th className="p-2 text-left whitespace-nowrap">投稿</th>
		      <th className="p-2 text-left whitespace-nowrap">
			<SortHeader<FetchPostsOrderField>
			    by="title"
			    label="タイトル"
			    currentOrder={order}
			    defaultDirection={defaultDirection}/>
		      </th>
		      <th className="p-2 text-left whitespace-nowrap">
			<SortHeader<FetchPostsOrderField>
			    by="url"
			    label="URL"
			    currentOrder={order}
			    defaultDirection={defaultDirection}/>
		      </th>
		      <th className="p-2 text-left whitespace-nowrap">タグ</th>
		      <th className="p-2 text-left whitespace-nowrap">
			<SortHeader<FetchPostsOrderField>
			    by="original_created_at"
			    label="オリジナルの投稿日時"
			    currentOrder={order}
			    defaultDirection={defaultDirection}/>
		      </th>
		      <th className="p-2 text-left whitespace-nowrap">
			<SortHeader<FetchPostsOrderField>
			    by="created_at"
			    label="投稿日時"
			    currentOrder={order}
			    defaultDirection={defaultDirection}/>
		      </th>
		      <th className="p-2 text-left whitespace-nowrap">
			<SortHeader<FetchPostsOrderField>
			    by="updated_at"
			    label="更新日時"
			    currentOrder={order}
			    defaultDirection={defaultDirection}/>
		      </th>
		    </tr>
		  </thead>
		  <tbody>
		    {results.map (row => (
			<tr
			    key={row.id}
			    className="even:bg-gray-100 dark:even:bg-gray-700">
			  <td className="p-2">
			    <PrefetchLink to={`/posts/${ row.id }`} title={row.title || undefined}>
			      <motion.div
				  layoutId={animationMode === 'off'
				      ? undefined
				      : `page-${ row.id }`}
				  transition={thumbnailTransition}>
				<img src={row.thumbnail || row.thumbnailBase || undefined}
				     alt={row.title || row.url}
				     title={row.title || row.url || undefined}
				     className="w-8"/>
			      </motion.div>
			    </PrefetchLink>
			  </td>
			  <td className="p-2 truncate">
			    <PrefetchLink to={`/posts/${ row.id }`} title={row.title || undefined}>
			      {row.title}
			    </PrefetchLink>
			  </td>
			  <td className="p-2 truncate">
			    <a href={row.url}
			       title={row.url}
			       target="_blank"
			       rel="noopener noreferrer nofollow">
			      {row.url}
			    </a>
			  </td>
			  <td className="p-2">
			    {row.tags.map (t => (
				<span key={t.id} className="mr-2">
				  <TagLink tag={t} withWiki={false} withCount={false}/>
				</span>))}
			  </td>
			  <td className="p-2">
			    {originalCreatedAtString (row.originalCreatedFrom,
						      row.originalCreatedBefore)}
			  </td>
			  <td className="p-2">{dateString (row.createdAt)}</td>
			  <td className="p-2">{dateString (row.updatedAt)}</td>
			</tr>))}
		  </tbody>
		</table>
	      </div>

	      <Pagination page={page} totalPages={totalPages}/>
	    </div>) : '結果ないよ（笑）')}
      </MainArea>)
}

export default PostSearchPage
