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

import FieldError from '@/components/common/FieldError'
import PageTitle from '@/components/common/PageTitle'
import PrefetchLink from '@/components/PrefetchLink'
import MainArea from '@/components/layout/MainArea'
import PostImportStatusBadge from '@/components/posts/import/PostImportStatusBadge'
import { displayPostImportStatus } from '@/components/posts/import/postImportRowStatus'
import { Button } from '@/components/ui/button'
import { toast } from '@/components/ui/use-toast'
import { SITE_TITLE } from '@/config'
import { apiPost } from '@/lib/api'
import { canEditContent } from '@/lib/users'
import { clearPostImportSourceDraft,
	 initialisePreviewRows,
	 loadPostImportSession,
	 mergeImportResults,
	 mergeValidatedImportRows,
	 resultSummaryCounts,
	 retryImportRow,
	 savePostImportSession,
	 type PostImportResultRow,
	 type PostImportRow,
	 type PostImportSession } from '@/lib/postImportSession'
import Forbidden from '@/pages/Forbidden'

import type { FC } from 'react'

import type { User } from '@/types'

type Props = { user: User | null }

const rowMessages = (row: PostImportRow): string[] =>
    Object.values (row.importErrors ?? { }).flat ()


const PostImportResultPage: FC<Props> = ({ user }) => {
    const editable = canEditContent (user)
    const navigate = useNavigate ()
    const { sessionId } = useParams ()

    const [session, setSession] = useState<PostImportSession | null> (null)
    const [missing, setMissing] = useState (false)
    const [loadingRow, setLoadingRow] = useState<number | null> (null)

    useEffect (() => {
	if (sessionId == null)
	  return

	const loaded = loadPostImportSession (sessionId, message =>
	    toast ({ title: '取込状態を復元できませんでした', description: message }))
	setSession (loaded)
	setMissing (loaded == null)
    }, [sessionId])

    useEffect (() => {
	if (sessionId == null || session == null)
	  return

	savePostImportSession (sessionId, session, message =>
	    toast ({ title: '取込状態を保存できませんでした', description: message }))
    }, [session, sessionId])

    const counts = useMemo (
	() => resultSummaryCounts (session?.rows ?? []),
	[session])

    const retry = async (sourceRow: number) => {
	if (session == null || sessionId == null)
	  return

	setLoadingRow (sourceRow)
	try
	{
	    const pendingRows = retryImportRow (session.rows, sourceRow)
	    setSession ({ ...session, rows: pendingRows })
	    const validated = await apiPost<{ rows: PostImportRow[] }> ('/posts/import/validate', {
		rows: pendingRows
		    .filter (row => row.importStatus !== 'created')
		    .map (row => ({
			sourceRow: row.sourceRow,
			url: row.url,
			attributes: row.attributes,
			provenance: row.provenance,
			tagSources: row.tagSources,
			metadataUrl: row.metadataUrl })),
		changed_row: -1 })
	    const validatedRows = mergeValidatedImportRows (
		pendingRows,
		initialisePreviewRows (validated.rows))
	    const nextSession = {
		...session,
		rows: validatedRows,
		repairMode: 'failed' as const }
	    setSession (nextSession)
	    const target = validatedRows.find (_1 => _1.sourceRow === sourceRow)
	    if (target == null)
	      return
	    if (Object.keys (target.validationErrors ?? { }).length > 0)
	      {
		const saved = savePostImportSession (sessionId, nextSession, message =>
		    toast ({ title: '取込状態を保存できませんでした', description: message }))
		if (saved)
		  navigate (`/posts/import/${ sessionId }/review?edit=${ sourceRow }`)
		return
	      }

	    const result = await apiPost<{
		created: number
		skipped: number
		failed:  number
		rows:    PostImportResultRow[] }> ('/posts/import', {
		    rows: [{
			sourceRow: target.sourceRow,
			url: target.url,
			attributes: target.attributes,
			provenance: target.provenance,
			tagSources: target.tagSources,
			metadataUrl: target.metadataUrl }] })
	    const mergedRows = mergeImportResults (nextSession.rows, result.rows)
	    const recoverableRows = result.rows.filter (row =>
		row.status === 'failed'
		&& row.recoverable
		&& Object.keys (row.errors ?? { }).length > 0)
	    const nextRows = mergedRows.map (row => {
		const recoverable = recoverableRows.find (_1 => _1.sourceRow === row.sourceRow)
		if (recoverable == null)
		  return row
		return {
		    ...row,
		    importStatus: 'pending',
		    validationErrors: recoverable.errors ?? { },
		    importErrors: undefined }
	    })
	    const recoverableTarget = nextRows.find (_1 => _1.sourceRow === sourceRow)
	    const resultSession = {
		...nextSession,
		rows: nextRows,
		repairMode: recoverableTarget == null ? 'all' as const : 'failed' as const }
	    setSession (resultSession)
	    if (recoverableTarget != null
		&& Object.keys (recoverableTarget.validationErrors).length > 0)
	      {
		const saved = savePostImportSession (sessionId, resultSession, message =>
		    toast ({ title: '取込状態を保存できませんでした', description: message }))
		if (saved)
		  navigate (`/posts/import/${ sessionId }/review?edit=${ sourceRow }`)
		return
	      }
	}
	catch
	{
	    setSession (session)
	    toast ({ title: '再試行に失敗しました' })
	}
	finally
	{
	    setLoadingRow (null)
	}
    }

    const openRepair = (sourceRow: number) => {
	if (session == null || sessionId == null)
	  return

	const nextSession = { ...session, repairMode: 'failed' as const }
	setSession (nextSession)
	const saved = savePostImportSession (sessionId, nextSession, message =>
	    toast ({ title: '取込状態を保存できませんでした', description: message }))
	if (!(saved))
	  return
	navigate (`/posts/import/${ sessionId }/review?edit=${ sourceRow }`)
    }

    if (!(editable))
      return <Forbidden/>

    if (missing || sessionId == null || session == null)
      {
	return (
	    <MainArea>
	      <div className="mx-auto max-w-4xl space-y-4 p-4">
		<PageTitle>投稿インポート結果</PageTitle>
		<FieldError messages={['取込状態が見つかりません．']}/>
		<Button type="button" onClick={() => navigate ('/posts/import')}>
		  URL リスト入力へ戻る
		</Button>
	      </div>
	    </MainArea>)
      }

    return (
	<MainArea>
	  <Helmet>
	    <title>{`投稿インポート結果 | ${ SITE_TITLE }`}</title>
	  </Helmet>

	  <div className="mx-auto max-w-5xl space-y-4 p-4">
	    <PageTitle>登録結果</PageTitle>

	    <div className="text-sm text-neutral-700 dark:text-neutral-200">
	      登録成功 {counts.created}件　スキップ {counts.skipped}件　失敗 {counts.failed}件
	    </div>

	    <div className="space-y-3">
	      {session.rows.map (row => {
		  const displayStatus = displayPostImportStatus (row)
		  const canEdit = row.importStatus === 'failed'
		  const canRetry =
		      row.importStatus === 'failed'
		      && Object.keys (row.validationErrors).length === 0
		  return (
		      <div
			  key={row.sourceRow}
			  className="rounded-lg border p-4 transition-shadow hover:shadow-sm">
		    <div className="flex flex-col gap-3 md:flex-row md:items-start
				    md:justify-between">
		      <div className="space-y-2">
			<div className="flex flex-wrap items-center gap-2">
			  <span className="text-sm font-medium">行 {row.sourceRow}</span>
			  {displayStatus != null && <PostImportStatusBadge value={displayStatus}/>}
			</div>
			<div className="text-sm text-neutral-700 dark:text-neutral-200">
			  {String (row.attributes.title ?? '') || row.url}
			</div>
			<div className="text-xs text-neutral-500 dark:text-neutral-400">
			  {row.url}
			</div>
			<FieldError messages={rowMessages (row)}/>
		      </div>

		      <div className="flex flex-col gap-2 sm:flex-row">
			{(row.createdPostId != null || row.existingPostId != null) && (
			    <Button type="button" variant="outline" asChild>
			      <PrefetchLink
				  to={`/posts/${ row.createdPostId ?? row.existingPostId }`}>
				投稿を開く
			      </PrefetchLink>
			    </Button>)}
			{canEdit && (
			    <Button
				type="button"
				variant="outline"
				onClick={() => openRepair (row.sourceRow)}>
			      編輯
			    </Button>)}
			{canRetry && (
			    <>
			      <Button
				  type="button"
				  onClick={() => retry (row.sourceRow)}
				  disabled={loadingRow === row.sourceRow}>
				再試行
			      </Button>
			    </>)}
		      </div>
		    </div>
		  </div>)})}
	    </div>

	    <div className="flex flex-col gap-2 sm:flex-row">
	      <Button
		  type="button"
		  variant="outline"
		  onClick={() => {
		      const nextSession = { ...session, repairMode: 'all' as const }
		      setSession (nextSession)
		      const saved = savePostImportSession (sessionId, nextSession, message =>
			  toast ({ title: '取込状態を保存できませんでした', description: message }))
		      if (!(saved))
			return
		      navigate (`/posts/import/${ sessionId }/review`)
		  }}>
		確認画面へ戻る
	      </Button>
	      <Button
		  type="button"
		  variant="outline"
		  onClick={() => {
		      clearPostImportSourceDraft (message =>
			  toast ({ title: '入力内容を削除できませんでした', description: message }))
		      navigate ('/posts/import')
		  }}>
		新しい URL リストを入力
	      </Button>
	    </div>
	  </div>
	</MainArea>)
}

export default PostImportResultPage
