import { useQuery } from '@tanstack/react-query'
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 MainArea from '@/components/layout/MainArea'
import { SITE_TITLE } from '@/config'
import { CATEGORIES, CATEGORY_NAMES } from '@/consts'
import { clientScrollBehaviour } from '@/lib/clientAnimation'
import { tagsKeys } from '@/lib/queryKeys'
import {
    DEFAULT_POST_LIST_LIMIT,
    DEFAULT_TAG_LIST_ORDER,
    getClientListLimit,
    getClientListOrder,
    LIST_LIMIT_OPTIONS,
    setClientListSettings,
} from '@/lib/settings'
import { useClientBehaviourSettings } from '@/lib/useClientBehaviourSettings'
import { useKeyboardShortcuts } from '@/lib/useKeyboardShortcuts'
import { fetchTags } from '@/lib/tags'
import { dateString, inputClass } from '@/lib/utils'

import type { FC, FormEvent } from 'react'

import type { Category, FetchTagsOrder, FetchTagsOrderField } from '@/types'


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


const boolFromQuery = (value: string | null): boolean =>
    ['1', 'true', 'on', 'yes', ''].includes ((value ?? '').toLowerCase ())


const tagStateLabel = (deprecatedAt: string | null) => deprecatedAt ? '廃止' : ''

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): FetchTagsOrder | null => {
  switch (value)
  {
  case 'name:asc':
  case 'name:desc':
  case 'category:asc':
  case 'category:desc':
  case 'post_count:asc':
  case 'post_count:desc':
  case 'created_at:asc':
  case 'created_at:desc':
  case 'updated_at:asc':
  case 'updated_at:desc':
    return value

  default:
    return null
  }
}


const TagListPage: FC = () => {
  const behaviourSettings = useClientBehaviourSettings ()
  const animationMode = behaviourSettings.animation ?? 'normal'
  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 ('tagList')
      ?? DEFAULT_POST_LIST_LIMIT
  )

  const qName = query.get ('name') ?? ''
  const qCategory = (query.get ('category') || null) as Category | null
  const qPostCountGTE = Number (query.get ('post_count_gte') ?? 1)
  const qPostCountLTERaw = query.get ('post_count_lte')
  const qPostCountLTE = qPostCountLTERaw ? Number (qPostCountLTERaw) : null
  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 qDeprecated = query.has ('deprecated')
      ? boolFromQuery (query.get ('deprecated'))
      : null
  const order = (
      parseOrder (query.get ('order'))
      ?? getClientListOrder<FetchTagsOrder> ('tagList')
      ?? DEFAULT_TAG_LIST_ORDER
  )

  const [name, setName] = useState ('')
  const [category, setCategory] = useState<Category | null> (null)
  const [postCountGTE, setPostCountGTE] = useState (1)
  const [postCountLTE, setPostCountLTE] = useState<number | null> (null)
  const [createdFrom, setCreatedFrom] = useState<string | null> (null)
  const [createdTo, setCreatedTo] = useState<string | null> (null)
  const [updatedFrom, setUpdatedFrom] = useState<string | null> (null)
  const [updatedTo, setUpdatedTo] = useState<string | null> (null)
  const [deprecated, setDeprecated] = useState<boolean | null> (null)

  const keys = {
      page, limit, order,
      post: null,
      name: qName,
      category: qCategory,
      postCountGTE: qPostCountGTE,
      postCountLTE: qPostCountLTE,
      createdFrom: qCreatedFrom,
      createdTo: qCreatedTo,
      updatedFrom: qUpdatedFrom,
      updatedTo: qUpdatedTo,
      deprecated: qDeprecated }
  const { data, isLoading: loading } = useQuery ({
      queryKey: tagsKeys.index (keys),
      queryFn: () => fetchTags (keys) })
  const results = data?.tags ?? []
  const totalPages = data ? Math.ceil (data.count / limit) : 0

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

  useEffect (() => {
    setName (qName)
    setCategory (qCategory)
    setPostCountGTE (qPostCountGTE)
    setPostCountLTE (qPostCountLTE)
    setCreatedFrom (qCreatedFrom)
    setCreatedTo (qCreatedTo)
    setUpdatedFrom (qUpdatedFrom)
    setUpdatedTo (qUpdatedTo)
    setDeprecated (qDeprecated)

    document.querySelector ('table')?.scrollIntoView ({
	behavior: clientScrollBehaviour (animationMode) })
  }, [animationMode, location.search, qCategory, qCreatedFrom, qCreatedTo,
      qName, qPostCountGTE, qPostCountLTE, qUpdatedFrom, qUpdatedTo,
      qDeprecated])

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

    const qs = new URLSearchParams ()
    setIf (qs, 'name', name)
    setIf (qs, 'category', category)
    if (postCountGTE !== 1)
      qs.set ('post_count_gte', String (postCountGTE))
    if (postCountLTE != null)
      qs.set ('post_count_lte', String (postCountLTE))
    setIf (qs, 'created_from', createdFrom)
    setIf (qs, 'created_to', createdTo)
    setIf (qs, 'updated_from', updatedFrom)
    setIf (qs, 'updated_to', updatedTo)
    if (deprecated != null)
      qs.set ('deprecated', deprecated ? '1' : '0')
    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?: FetchTagsOrder }) => {
    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 defaultDirection = { name: 'asc',
			     category: 'asc',
			     post_count: 'desc',
			     created_at: 'desc',
			     updated_at: 'desc' } as const

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

  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
		  value={limit}
		  onChange={ev => updateListQuery ({
		      limit: Number (ev.target.value) as 20 | 50 | 100,
		  })}
		  className="rounded border border-border bg-background px-2 py-1 text-sm
			     text-foreground">
		{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={name}
		      onChange={e => setName (e.target.value)}
		      className={inputClass (invalid)}/>)}
	    </FormField>

	    {/* カテゴリ */}
	    <FormField label="カテゴリ">
	      {({ invalid }) => (
		  <select
		      value={category ?? ''}
		      onChange={e => setCategory((e.target.value || null) as Category | null)}
		      className={inputClass (invalid)}>
		    <option value="">&nbsp;</option>
		    {CATEGORIES.map (cat => (
			<option key={cat} value={cat}>
			  {CATEGORY_NAMES[cat]}
			</option>))}
		  </select>)}
	    </FormField>

	    {/* 広場の投稿数 */}
	    <FormField label="広場の投稿数">
	      {({ invalid }) => (
		  <>
		    <input
			type="number"
			min="0"
			value={postCountGTE < 0 ? 0 : String (postCountGTE)}
			onChange={e => setPostCountGTE (Number (e.target.value || 0))}
			className={inputClass (invalid, 'w-auto')}/>
		    <span className="mx-1">〜</span>
		    <input
			type="number"
			min="0"
			value={postCountLTE == null ? '' : String (postCountLTE)}
			onChange={e => setPostCountLTE (e.target.value
							? Number (e.target.value)
							: null)}
			className={inputClass (invalid, 'w-auto')}/>
		  </>)}
	    </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>

	    <FormField label="状態">
	      {({ invalid }) => (
		  <select
		      value={deprecated == null ? '' : (deprecated ? '1' : '0')}
		      onChange={e => setDeprecated (
			  e.target.value === ''
			  ? null
			  : e.target.value === '1')}
		      className={inputClass (invalid)}>
		    <option value="">&nbsp;</option>
		    <option value="0">有効</option>
		    <option value="1">廃止</option>
		  </select>)}
	    </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-[2000px] table-fixed border-collapse">
		  <colgroup>
		    <col className="w-72"/>
		    <col className="w-16"/>
		    <col className="w-48"/>
		    <col className="w-32"/>
		    <col className="w-72"/>
		    <col className="w-48"/>
		    <col className="w-56"/>
		    <col className="w-56"/>
		    <col className="w-16"/>
		  </colgroup>

		  <thead className="border-b-2 border-black dark:border-white">
		    <tr>
		      <th className="p-2 text-left whitespace-nowrap">
			<SortHeader<FetchTagsOrderField>
			    by="name"
			    label="タグ"
			    currentOrder={order}
			    defaultDirection={defaultDirection}/>
		      </th>
		      <th className="p-2 text-left whitespace-nowrap">
			<SortHeader<FetchTagsOrderField>
			    by="post_count"
			    label="件数"
			    currentOrder={order}
			    defaultDirection={defaultDirection}/>
		      </th>
		      <th className="p-2 text-left whitespace-nowrap">
			<SortHeader<FetchTagsOrderField>
			    by="category"
			    label="カテゴリ"
			    currentOrder={order}
			    defaultDirection={defaultDirection}/>
		      </th>
		      <th className="p-2 text-left whitespace-nowrap">状態</th>
		      <th className="p-2 text-left whitespace-nowrap">別名</th>
		      <th className="p-2 text-left whitespace-nowrap">上位タグ</th>
		      <th className="p-2 text-left whitespace-nowrap">
			<SortHeader<FetchTagsOrderField>
			    by="created_at"
			    label="最初の記載日時"
			    currentOrder={order}
			    defaultDirection={defaultDirection}/>
		      </th>
		      <th className="p-2 text-left whitespace-nowrap">
			<SortHeader<FetchTagsOrderField>
			    by="updated_at"
			    label="更新日時"
			    currentOrder={order}
			    defaultDirection={defaultDirection}/>
		      </th>
		      <th className="p-2 text-left whitespace-nowrap"/>
		    </tr>
		  </thead>

		  <tbody>
		    {results.map (row => (
			<tr key={row.id} className="even:bg-gray-100 dark:even:bg-gray-700">
			  <td className="p-2">
			    <TagLink
				tag={row}
				to={`/tags/${ encodeURIComponent (row.id) }`}
				withCount={false}/>
			  </td>
			  <td className="p-2 text-right">{row.postCount}</td>
			  <td className="p-2">{CATEGORY_NAMES[row.category]}</td>
			  <td className="p-2">{tagStateLabel (row.deprecatedAt)}</td>
			  <td className="p-2">{row.aliases.join (' ')}</td>
			  <td className="p-2">
			    {row.parents.map (t => (
				<span key={t.id} className="mr-2">
				  <TagLink
				      tag={t}
				      withWiki={false}
				      withCount={false}/>
				</span>))}
			  </td>
			  <td className="p-2">{dateString (row.createdAt)}</td>
			  <td className="p-2">{dateString (row.updatedAt)}</td>
			  <td className="p-2">
			    <PrefetchLink to={`/posts/changes?tag=${ row.id }`}>
			      耕作履歴
			    </PrefetchLink>
			  </td>
			</tr>))}
		  </tbody>
		</table>
	      </div>

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

export default TagListPage
