import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
import { AnimatePresence, motion } from 'framer-motion'
import { processPurposePracticeItems } from '@/data/process-purpose-practice'
import { InputArea } from '@/components/practice/InputArea'
import { ProficientInputArea } from '@/components/practice/ProficientInputArea'
import { useAppStore } from '@/stores/useAppStore'
import { normalizeAnswer, announceToScreenReader } from '@/utils/practice'

type CharStatus = 'pending' | 'correct' | 'error'

type PracticeProgress = {
  order: string[]
  currentIndex: number
  completedIds: string[]
  currentInput: string[]
}

const STORAGE_KEY = 'process-purpose-practice-progress'
const NEXT_QUESTION_DELAY = 650
const ANSWER_VISIBLE_DURATION = 3000

function shuffleItems<T>(items: T[]): T[] {
  const result = [...items]
  for (let i = result.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1))
    ;[result[i], result[j]] = [result[j], result[i]]
  }
  return result
}

function createFreshProgress(): PracticeProgress {
  return {
    order: shuffleItems(processPurposePracticeItems.map((item) => item.id)),
    currentIndex: 0,
    completedIds: [],
    currentInput: [],
  }
}

function getStoredProgress(): PracticeProgress {
  try {
    const saved = localStorage.getItem(STORAGE_KEY)
    if (!saved) return createFreshProgress()

    const parsed = JSON.parse(saved) as Partial<PracticeProgress>
    const validIds = new Set(processPurposePracticeItems.map((item) => item.id))
    const order = Array.isArray(parsed.order)
      ? parsed.order.filter((id): id is string => validIds.has(String(id)))
      : []
    const missingIds = processPurposePracticeItems
      .map((item) => item.id)
      .filter((id) => !order.includes(id))
    const completedIds = Array.isArray(parsed.completedIds)
      ? parsed.completedIds.filter((id): id is string => validIds.has(String(id)))
      : []
    const mergedOrder = [...order, ...shuffleItems(missingIds)]

    if (mergedOrder.length === 0) return createFreshProgress()

    return {
      order: mergedOrder,
      currentIndex: Math.min(
        Math.max(Number(parsed.currentIndex) || 0, 0),
        mergedOrder.length - 1
      ),
      completedIds,
      currentInput: Array.isArray(parsed.currentInput)
        ? parsed.currentInput.map((char) => String(char || ''))
        : [],
    }
  } catch (error) {
    console.error('加载子过程主要作用练习进度失败:', error)
    return createFreshProgress()
  }
}

export default function ProcessPurposePracticePage() {
  const [progress, setProgress] = useState<PracticeProgress>(() =>
    getStoredProgress()
  )
  const [userInput, setUserInput] = useState<string[]>([])
  const [charStatuses, setCharStatuses] = useState<CharStatus[]>([])
  const [isComposing, setIsComposing] = useState(false)
  const [inputLocked, setInputLocked] = useState(false)
  const [lastErrorTimestamp, setLastErrorTimestamp] = useState<number | null>(
    null
  )
  const [showAnswer, setShowAnswer] = useState(false)
  const [correctFeedback, setCorrectFeedback] = useState(false)
  const [proficientInput, setProficientInput] = useState('')
  const [proficientHasError, setProficientHasError] = useState(false)
  const isComposingRef = useRef(false)
  const latestInputRef = useRef<string[]>([])
  const answerTimerRef = useRef<number | null>(null)
  const deckScrollRef = useRef<HTMLDivElement | null>(null)
  const currentScrollLimitRef = useRef(0)
  const { practiceMode } = useAppStore()

  const itemMap = useMemo(
    () => new Map(processPurposePracticeItems.map((item) => [item.id, item])),
    []
  )
  const currentItem = itemMap.get(progress.order[progress.currentIndex])
  const totalCount = progress.order.length
  const completedCount = progress.completedIds.length
  const isFinished = completedCount >= totalCount
  const deckCards = progress.order
    .map((id, index) => ({ item: itemMap.get(id), index }))
    .filter((card): card is { item: NonNullable<typeof currentItem>; index: number } => Boolean(card.item))

  const focusFirstEmptyInput = useCallback(() => {
    setTimeout(() => {
      const inputs = document.querySelectorAll('.practice-input-area input')
      const firstEmptyInput = Array.from(inputs).find(
        (input) => !(input as HTMLInputElement).value
      ) as HTMLInputElement | undefined
      ;(firstEmptyInput || (inputs[0] as HTMLInputElement | undefined))?.focus()
    }, 100)
  }, [])

  const hideAnswer = useCallback(() => {
    if (answerTimerRef.current) {
      window.clearTimeout(answerTimerRef.current)
      answerTimerRef.current = null
    }
    setShowAnswer(false)
    setInputLocked(false)
    announceToScreenReader('答案已隐藏')
    focusFirstEmptyInput()
  }, [focusFirstEmptyInput])

  const showCurrentAnswer = useCallback(() => {
    if (!currentItem || isFinished) return
    if (answerTimerRef.current) {
      window.clearTimeout(answerTimerRef.current)
    }
    if (practiceMode === 'proficient') {
      setProficientInput('')
      setProficientHasError(false)
    }
    setShowAnswer(true)
    setInputLocked(true)
    announceToScreenReader('答案已显示')
    answerTimerRef.current = window.setTimeout(() => {
      hideAnswer()
    }, ANSWER_VISIBLE_DURATION)
  }, [currentItem, hideAnswer, isFinished, practiceMode])

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

    const nextInput = new Array(currentItem.name.length).fill('')
    progress.currentInput.slice(0, currentItem.name.length).forEach((char, index) => {
      nextInput[index] = char
    })
    latestInputRef.current = nextInput
    setUserInput(nextInput)
    setCharStatuses(new Array(currentItem.name.length).fill('pending'))
    setLastErrorTimestamp(null)
    setShowAnswer(false)
    setCorrectFeedback(false)
    setInputLocked(false)
    setProficientInput('')
    setProficientHasError(false)
  }, [currentItem?.id])

  useEffect(() => {
    latestInputRef.current = userInput
  }, [userInput])

  useEffect(() => {
    try {
      localStorage.setItem(
        STORAGE_KEY,
        JSON.stringify({
          ...progress,
          currentInput: userInput,
        })
      )
    } catch (error) {
      console.error('保存子过程主要作用练习进度失败:', error)
    }
  }, [progress, userInput])

  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.ctrlKey && e.key.toLowerCase() === 'h') {
        e.preventDefault()
        if (!showAnswer) showCurrentAnswer()
      } else if (e.key === 'Escape' && !inputLocked) {
        if (practiceMode === 'proficient') {
          setProficientInput('')
          setProficientHasError(false)
        } else {
          setUserInput(new Array(userInput.length).fill(''))
          setCharStatuses(new Array(userInput.length).fill('pending'))
        }
      }
    }

    const handleKeyUp = (e: KeyboardEvent) => {
      if ((e.key === 'Control' || e.key.toLowerCase() === 'h') && showAnswer) {
        hideAnswer()
      }
    }

    window.addEventListener('keydown', handleKeyDown)
    window.addEventListener('keyup', handleKeyUp)
    return () => {
      window.removeEventListener('keydown', handleKeyDown)
      window.removeEventListener('keyup', handleKeyUp)
    }
  }, [hideAnswer, inputLocked, practiceMode, showAnswer, showCurrentAnswer, userInput.length])

  useEffect(() => {
    return () => {
      if (answerTimerRef.current) window.clearTimeout(answerTimerRef.current)
    }
  }, [])

  useLayoutEffect(() => {
    const deck = deckScrollRef.current
    if (!deck) return

    requestAnimationFrame(() => {
      const currentCard = deck.querySelector('[data-current-question="true"]') as HTMLElement | null
      if (!currentCard) return

      const deckTop = deck.getBoundingClientRect().top
      const cardTop = currentCard.getBoundingClientRect().top
      const targetTop = Math.max(deck.scrollTop + cardTop - deckTop - 90, 0)
      currentScrollLimitRef.current = targetTop
      deck.scrollTo({ top: targetTop, behavior: 'smooth' })
    })
  }, [progress.currentIndex])

  const moveToNextQuestion = useCallback(() => {
    setShowAnswer(false)
    setCorrectFeedback(false)
    setInputLocked(false)
    setProgress((prev) => ({
      ...prev,
      currentIndex: Math.min(prev.currentIndex + 1, prev.order.length - 1),
      currentInput: [],
    }))
    focusFirstEmptyInput()
  }, [focusFirstEmptyInput])

  const validateInput = useCallback(
    (input: string[]) => {
      if (!currentItem || inputLocked || isFinished) return

      const answer = currentItem.name
      const normalizeChar = (char: string) => normalizeAnswer(char) || char
      const newCharStatuses = input.map((char, index) => {
        if (!char) return 'pending' as CharStatus
        return normalizeChar(char) === normalizeChar(answer[index] || '')
          ? ('correct' as CharStatus)
          : ('error' as CharStatus)
      })
      setCharStatuses(newCharStatuses)

      const isComplete = input.every(Boolean) && input.length === answer.length
      const isCorrect =
        isComplete && normalizeAnswer(input.join('')) === normalizeAnswer(answer)

      if (isCorrect) {
        setCorrectFeedback(true)
        setInputLocked(true)
        setProgress((prev) => ({
          ...prev,
          completedIds: prev.completedIds.includes(currentItem.id)
            ? prev.completedIds
            : [...prev.completedIds, currentItem.id],
          currentInput: input,
        }))
        announceToScreenReader('回答正确')

        window.setTimeout(() => {
          const isLastQuestion =
            progress.currentIndex >= progress.order.length - 1
          if (isLastQuestion) {
            setInputLocked(false)
            setCorrectFeedback(false)
          } else {
            moveToNextQuestion()
          }
        }, NEXT_QUESTION_DELAY)
      } else if (isComplete) {
        setLastErrorTimestamp(Date.now())
      }
    },
    [currentItem, inputLocked, isFinished, moveToNextQuestion, progress.currentIndex, progress.order.length]
  )

  const handleInputChange = useCallback(
    (newInput: string[]) => {
      latestInputRef.current = newInput
      setUserInput(newInput)
      if (isComposingRef.current) return
      validateInput(newInput)
    },
    [validateInput]
  )

  const handleCompositionStart = useCallback((_index?: number) => {
    isComposingRef.current = true
    setIsComposing(true)
  }, [])

  const submitProficientAnswer = useCallback(
    (rawValue: string) => {
      if (!currentItem || inputLocked || isFinished) return

      const normalizedValue = normalizeAnswer(rawValue)
      if (!normalizedValue) return

      if (normalizedValue !== normalizeAnswer(currentItem.name)) {
        setProficientHasError(true)
        return
      }

      setCorrectFeedback(true)
      setInputLocked(true)
      setProficientInput('')
      setProficientHasError(false)
      setProgress((prev) => ({
        ...prev,
        completedIds: prev.completedIds.includes(currentItem.id)
          ? prev.completedIds
          : [...prev.completedIds, currentItem.id],
        currentInput: [],
      }))
      announceToScreenReader('回答正确')

      window.setTimeout(() => {
        const isLastQuestion = progress.currentIndex >= progress.order.length - 1
        if (isLastQuestion) {
          setInputLocked(false)
          setCorrectFeedback(false)
        } else {
          moveToNextQuestion()
        }
      }, NEXT_QUESTION_DELAY)
    },
    [currentItem, inputLocked, isFinished, moveToNextQuestion, progress.currentIndex, progress.order.length]
  )

  const handleCompositionEnd = useCallback(
    (indexOrValue: number | string, maybeValue?: string) => {
      isComposingRef.current = false
      setIsComposing(false)

      if (practiceMode === 'proficient') {
        const nextValue =
          typeof indexOrValue === 'string' ? indexOrValue : maybeValue ?? ''
        setProficientInput(nextValue)
        setProficientHasError(false)
        return
      }

      const index = indexOrValue as number
      const value = maybeValue ?? ''
      requestAnimationFrame(() => {
        const newInput = [...latestInputRef.current]
        const chars = value.split('')
        if (chars.length > 0) {
          for (let i = 0; i < chars.length && index + i < newInput.length; i++) {
            newInput[index + i] = chars[i]
          }
        } else {
          newInput[index] = ''
        }
        handleInputChange(newInput)
      })
    },
    [handleInputChange, practiceMode]
  )

  const handlePaste = useCallback(
    (e: React.ClipboardEvent) => {
      e.preventDefault()
      if (!currentItem || inputLocked) return
      const nextInput = new Array(currentItem.name.length).fill('')
      e.clipboardData
        .getData('text')
        .split('')
        .slice(0, currentItem.name.length)
        .forEach((char, index) => {
          nextInput[index] = char
        })
      handleInputChange(nextInput)
    },
    [currentItem, handleInputChange, inputLocked]
  )

  useEffect(() => {
    if (practiceMode !== 'proficient' || isComposing || inputLocked || isFinished) return
    if (!normalizeAnswer(proficientInput)) return

    const timer = window.setTimeout(() => {
      submitProficientAnswer(proficientInput)
    }, 800)

    return () => window.clearTimeout(timer)
  }, [
    isComposing,
    inputLocked,
    isFinished,
    practiceMode,
    proficientInput,
    submitProficientAnswer,
  ])

  useEffect(() => {
    setProficientInput('')
    setProficientHasError(false)
    setShowAnswer(false)
    setInputLocked(false)
  }, [practiceMode])

  const handleRestart = useCallback(() => {
    const fresh = createFreshProgress()
    localStorage.setItem(STORAGE_KEY, JSON.stringify(fresh))
    setProgress(fresh)
    setShowAnswer(false)
    setCorrectFeedback(false)
    setInputLocked(false)
    setProficientInput('')
    setProficientHasError(false)
    focusFirstEmptyInput()
  }, [focusFirstEmptyInput])

  const handleDeckScroll = useCallback((event: React.UIEvent<HTMLDivElement>) => {
    const deck = event.currentTarget
    const maxScrollTop = currentScrollLimitRef.current
    if (deck.scrollTop > maxScrollTop + 2) {
      deck.scrollTop = maxScrollTop
    }
  }, [])

  if (!currentItem) return null

  return (
    <div className="flex h-[calc(100vh-3rem)] min-h-[620px] flex-col overflow-hidden bg-gray-50 dark:bg-gray-900">
      <div className="z-20 shrink-0 border-b border-gray-200 bg-white/90 shadow-sm backdrop-blur dark:border-gray-700 dark:bg-gray-800/90">
        <div className="mx-auto max-w-5xl px-4 py-4">
          <div className="mb-3 flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
            <div>
              <h1 className="text-xl font-bold text-gray-900 dark:text-gray-100">
                子过程主要作用专项练习
              </h1>
              <p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
                根据主要作用写出对应的过程名称
              </p>
            </div>
            <div className="flex items-center gap-3">
              <div className="text-sm text-gray-600 dark:text-gray-400">
                进度：{completedCount} / {totalCount}
              </div>
              <button
                type="button"
                onClick={handleRestart}
                className="rounded-lg px-3 py-1.5 text-sm text-gray-600 transition-colors hover:bg-gray-100 hover:text-gray-900 dark:text-gray-300 dark:hover:bg-gray-700 dark:hover:text-gray-100"
              >
                重新练习
              </button>
            </div>
          </div>
          <div className="h-2 w-full rounded-full bg-gray-200 dark:bg-gray-700">
            <motion.div
              className="h-2 rounded-full bg-blue-500"
              initial={{ width: 0 }}
              animate={{ width: `${(completedCount / totalCount) * 100}%` }}
              transition={{ duration: 0.3 }}
            />
          </div>
        </div>
      </div>

      <main className="mx-auto flex min-h-0 w-full max-w-5xl flex-1 flex-col gap-3 overflow-hidden px-4 pb-4 pt-3">
        <div
          ref={deckScrollRef}
          onScroll={handleDeckScroll}
          className="min-h-0 flex-1 space-y-3 overflow-y-auto overscroll-contain pr-1 scroll-smooth"
        >
          <AnimatePresence initial={false}>
            {deckCards.map(({ item, index }) => {
              const isCurrent = index === progress.currentIndex
              const isAnsweredHistory = index < progress.currentIndex
              const isFuture = index > progress.currentIndex

              return (
                <motion.section
                  key={item.id}
                  initial={{ opacity: 0, y: 24, scale: 0.98 }}
                  animate={{
                    opacity: isCurrent ? 1 : isFuture ? 0.42 : 0.62,
                    y: 0,
                    scale: isCurrent ? 1 : 0.975,
                  }}
                  exit={{ opacity: 0, y: -16, scale: 0.98 }}
                  transition={{ duration: 0.22, ease: 'easeOut' }}
                  data-current-question={isCurrent ? 'true' : undefined}
                  className={`rounded-2xl border bg-white p-6 transition-all duration-200 dark:bg-gray-800 ${
                    isCurrent && correctFeedback
                      ? 'border-green-300 shadow-lg shadow-green-100/70 ring-2 ring-green-100 dark:border-green-700 dark:shadow-none dark:ring-green-900/40'
                      : isCurrent
                        ? 'border-blue-200 shadow-xl shadow-blue-100/80 ring-2 ring-blue-100 dark:border-blue-800 dark:shadow-none dark:ring-blue-900/30'
                        : isFuture
                          ? 'pointer-events-none border-dashed border-gray-200 bg-white/55 shadow-sm blur-[0.4px] dark:border-gray-700 dark:bg-gray-800/45'
                          : 'border-gray-100 shadow-sm dark:border-gray-800'
                  }`}
                >
                  <div className="mb-4 flex min-h-9 flex-wrap items-center justify-between gap-3">
                    <span
                      className={`rounded-full px-3 py-1 text-sm font-medium ${
                        isCurrent
                          ? 'bg-blue-50 text-blue-700 dark:bg-blue-900/30 dark:text-blue-200'
                          : 'bg-gray-100 text-gray-500 dark:bg-gray-700/70 dark:text-gray-300'
                      }`}
                    >
                      第 {index + 1} / {totalCount} 题
                    </span>

                    <AnimatePresence mode="wait">
                      {isCurrent && showAnswer ? (
                        <motion.span
                          key="current-answer"
                          initial={{ opacity: 0, y: -4 }}
                          animate={{ opacity: 1, y: 0 }}
                          exit={{ opacity: 0, y: -4 }}
                          className="rounded-xl border border-indigo-300 bg-indigo-50 px-3 py-1 text-base font-semibold leading-6 text-indigo-800 shadow-sm shadow-indigo-100 dark:border-indigo-500/70 dark:bg-indigo-950/50 dark:text-indigo-100 dark:shadow-none"
                        >
                          {item.name}
                        </motion.span>
                      ) : isAnsweredHistory ? (
                        <motion.span
                          key="history-answer"
                          initial={{ opacity: 0 }}
                          animate={{ opacity: 1 }}
                          exit={{ opacity: 0 }}
                          className="rounded-full bg-green-50 px-3 py-1 text-sm font-medium text-green-700 dark:bg-green-900/30 dark:text-green-200"
                        >
                          {item.name}
                        </motion.span>
                      ) : null}
                    </AnimatePresence>
                  </div>

                  <p
                    className={`leading-9 text-gray-900 dark:text-gray-100 ${
                      isCurrent ? 'text-xl' : isFuture ? 'line-clamp-2 select-none text-base text-gray-500 blur-[1.5px] dark:text-gray-400' : 'text-base'
                    }`}
                  >
                    {item.purpose}
                  </p>
                </motion.section>
              )
            })}
          </AnimatePresence>
        </div>


        {isFinished && (
          <section className="shrink-0 rounded-2xl border border-gray-200 bg-white p-10 text-center shadow-sm dark:border-gray-700 dark:bg-gray-800">
            <div className="text-2xl font-bold text-gray-900 dark:text-gray-100">
              已完成本轮练习
            </div>
            <p className="mt-3 text-gray-600 dark:text-gray-300">
              继续巩固 49 个项目管理过程。
            </p>
            <button
              type="button"
              onClick={handleRestart}
              className="mt-6 rounded-xl bg-blue-600 px-5 py-2.5 font-medium text-white transition-colors hover:bg-blue-700"
            >
              再练一轮
            </button>
          </section>
        )}
      </main>

      {!isFinished && (
        <div className="z-10 shrink-0 border-t border-gray-200 bg-white/70 pb-8 backdrop-blur-md dark:border-gray-700 dark:bg-gray-800/70">
          <div className="mx-auto max-w-5xl px-6">
            <div className="border-b border-gray-200/50 py-3 dark:border-gray-700/50">
              <div className="flex items-center justify-center gap-3">
                {practiceMode === 'proficient' ? (
                  <ProficientInputArea
                    value={proficientInput}
                    isComposing={isComposing}
                    inputLocked={inputLocked}
                    hasError={proficientHasError}
                    onChange={(value) => {
                      setProficientInput(value)
                      setProficientHasError(false)
                    }}
                    onCompositionStart={() => handleCompositionStart()}
                    onCompositionEnd={(value) => handleCompositionEnd(value)}
                    showLockedHint={false}
                  />
                ) : (
                  <InputArea
                    userInput={userInput}
                    charStatuses={charStatuses}
                    isComposing={isComposing}
                    inputLocked={inputLocked}
                    lastErrorTimestamp={lastErrorTimestamp}
                    onInputChange={handleInputChange}
                    onCompositionStart={handleCompositionStart}
                    onCompositionEnd={(index, value) => handleCompositionEnd(index, value)}
                    onPaste={handlePaste}
                    showLockedHint={false}
                  />
                )}

                <button
                  onClick={showCurrentAnswer}
                  disabled={inputLocked}
                  className="p-2 text-gray-500 transition-colors hover:text-blue-600 disabled:cursor-not-allowed disabled:opacity-50 dark:text-gray-400 dark:hover:text-blue-400"
                  title="查看答案（Ctrl+H）"
                  type="button"
                >
                  <svg className="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
                  </svg>
                </button>
              </div>

            </div>

            <div className="px-4 py-3 text-sm text-gray-500 dark:text-gray-400">
              Ctrl + H 查看答案
            </div>
          </div>
        </div>
      )}

      <div
        id="aria-live-region"
        className="sr-only"
        aria-live="polite"
        aria-atomic="true"
      />
    </div>
  )
}
