import { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { motion, AnimatePresence, MotionConfig } from 'motion/react'
import { IconShare2 } from '@tabler/icons-react'
import Grid from './components/Grid'
import CluePanel from './components/CluePanel'
import ActiveClue from './components/ActiveClue'
import Header from './components/Header'
import InfoBar from './components/InfoBar'
import ZoomStrip from './components/ZoomStrip'
import Welcome from './components/Welcome'
import OnboardingModal from './components/OnboardingModal'
import PuzzleRating from './components/PuzzleRating'
import Leaderboard from './components/Leaderboard'
import EditorPanel from './components/EditorPanel'
import AdminPanel from './components/AdminPanel'
import InstancePages from './components/InstancePages'
import ReceiptNotification from './components/ReceiptNotification'
import SolveHeatmap from './components/SolveHeatmap'
import PartyPage from './components/PartyPage'
import DailyPreviewRenderer from './components/DailyPreviewRenderer'
import CommunityToday from './components/CommunityToday'
import { useKeyboard } from './hooks/useKeyboard'
import { useNarrator } from './hooks/useNarrator'
import { useSounds } from './hooks/useSounds'
import { useStore } from './store'
import { generateShareText } from './share'
import * as api from './api'
import * as offlineStore from './offline'

export default function App() {
  const puzzle = useStore((s) => s.puzzle)
  const loading = useStore((s) => s.loading)
  const error = useStore((s) => s.error)
  const solved = useStore((s) => s.solved)
  const inputMode = useStore((s) => s.inputMode)
  const setInputMode = useStore((s) => s.setInputMode)
  const fetchSession = useStore((s) => s.fetchSession)
  const loadByShortId = useStore((s) => s.loadByShortId)
  const checkPendingReceipts = useStore((s) => s.checkPendingReceipts)
  const showOnboarding = useStore((s) => s.showOnboarding)
  const showEditor = useStore((s) => s.showEditor)
  const showAdmin = useStore((s) => s.showAdmin)
  const gameMode = useStore((s) => s.gameMode)
  const marathonCount = useStore((s) => s.marathonCount)
  const marathonFailed = useStore((s) => s.marathonFailed)
  const timeLimit = useStore((s) => s.timeLimit)
  const elapsed = useStore((s) => s.elapsed)
  const puzzleId = useStore((s) => s.puzzleId)
  const solveRank = useStore((s) => s.solveRank)
  const solvePercentile = useStore((s) => s.solvePercentile)
  const solveTotalSolvers = useStore((s) => s.solveTotalSolvers)
  const entries = useStore((s) => s.entries)
  const cellStatus = useStore((s) => s.cellStatus)
  const cellEverWrong = useStore((s) => s.cellEverWrong)
  const hintsUsed = useStore((s) => s.hintsUsed)
  const shortId = useStore((s) => s.shortId)
  const dailyDate = useStore((s) => s.dailyDate)
  const dailyPreset = useStore((s) => s.dailyPreset)
  const dailyChallenge = useStore((s) => s.dailyChallenge)
  const challengeCompleted = useStore((s) => s.challengeCompleted)
  const showHeatmap = useStore((s) => s.showHeatmap)
  const mpMode = useStore((s) => s.mpMode)
  const mpConnected = useStore((s) => s.mpConnected)
  const { t, i18n } = useTranslation()
  const [isOffline, setIsOffline] = useState(!navigator.onLine)
  const [instancePage, setInstancePage] = useState<'about' | 'health' | null>(() =>
    window.location.pathname === '/about' ? 'about' : null
  )
  const [shareCopied, setShareCopied] = useState(false)
  useKeyboard()
  useNarrator()
  useSounds()

  // offline status
  useEffect(() => offlineStore.onOnlineChange(online => setIsOffline(!online)), [])

  // route detection
  useEffect(() => {
    const path = window.location.pathname
    if (path === '/about') { window.history.replaceState(null, '', '/about'); return }
    fetch('/api/admin/config').then(r => r.json()).then(cfg => {
      const adminPath = cfg.path || '/admin'
      if (path === adminPath) {
        useStore.getState().setShowAdmin(true)
        window.history.replaceState(null, '', '/')
      }
    }).catch(() => {})
  }, [])

  // autosave progress to IndexedDB
  const saveTimer = useRef<ReturnType<typeof setTimeout> | null>(null)
  useEffect(() => {
    if (!puzzleId || !puzzle || solved) return
    if (saveTimer.current) clearTimeout(saveTimer.current)
    saveTimer.current = setTimeout(() => {
      offlineStore.saveProgress(puzzleId, entries).catch(() => {})
    }, 1000)
    return () => { if (saveTimer.current) clearTimeout(saveTimer.current) }
  }, [entries, puzzleId, puzzle, solved])

  useEffect(() => {
    fetchSession()
    checkPendingReceipts()

    const path = window.location.pathname
    const match = path.match(/^\/p\/([a-zA-Z0-9]+)$/)
    if (match) {
      loadByShortId(match[1])
      window.history.replaceState(null, '', '/')
    }
    const dailyMatch = path.match(/^\/daily\/([a-z]+)$/)
    if (dailyMatch) {
      const today = new Date().toISOString().split('T')[0]
      useStore.getState().loadDaily(today, dailyMatch[1])
      window.history.replaceState(null, '', '/')
    }
  }, [])

  // page title
  useEffect(() => {
    if (mpMode === 'party' && mpConnected) document.title = 'Party - cruciverb'
    else if (puzzle) document.title = 'Puzzle - cruciverb'
    else document.title = 'cruciverb'
  }, [puzzle, mpMode, mpConnected])

  // html lang attribute
  useEffect(() => {
    document.documentElement.lang = i18n.language
  }, [i18n.language])

  if (window.location.pathname === '/internal/generate-daily-image') {
    return <DailyPreviewRenderer />
  }

  if (window.location.pathname === '/community/today') {
    return <CommunityToday />
  }

  if (instancePage) {
    return <InstancePages page={instancePage} onBack={() => { setInstancePage(null); window.history.replaceState(null, '', '/') }} />
  }

  if (showEditor) {
    return <EditorPanel onClose={() => useStore.getState().setShowEditor(false)} />
  }

  if (showAdmin) {
    return <AdminPanel onClose={() => useStore.getState().setShowAdmin(false)} />
  }

  if (mpMode === 'party' && mpConnected) {
    return (
      <MotionConfig reducedMotion="user">
      <div className="app">
        <a href="#main-content" className="skip-link">{t('app.skipToContent')}</a>
        <Header />
        <div id="main-content"><PartyPage /></div>
        <ReceiptNotification />
      </div>
      </MotionConfig>
    )
  }

  return (
    <MotionConfig reducedMotion="user">
    <div className="app">
      <a href="#main-content" className="skip-link">{t('app.skipToContent')}</a>
      <Header />

      <AnimatePresence>
        {isOffline && (
          <motion.div className="offline-banner" key="offline" role="status"
            initial={{ opacity: 0, y: -20 }} animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -20 }} transition={{ duration: 0.2 }}>
            {t('app.offline')}
          </motion.div>
        )}
      </AnimatePresence>

      <AnimatePresence>
        {loading && (
          <motion.div className="loading-overlay" key="loading"
            initial={{ opacity: 0 }} animate={{ opacity: 1 }}
            exit={{ opacity: 0 }} transition={{ duration: 0.2 }}>
            <motion.div className="loading-card" role="status" aria-live="polite"
              initial={{ opacity: 0, scale: 0.95, y: 10 }}
              animate={{ opacity: 1, scale: 1, y: 0 }}
              exit={{ opacity: 0, scale: 0.95, y: 10 }}
              transition={{ duration: 0.2 }}>
              <div className="spinner" />
              <p>{t('app.generating')}</p>
            </motion.div>
          </motion.div>
        )}
      </AnimatePresence>

      <AnimatePresence>
        {error && (
          <motion.div className="main-content" key="error"
            initial={{ opacity: 0, y: -10 }} animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -10 }} transition={{ duration: 0.15 }}>
            <div className="error-banner" role="alert">{error}</div>
          </motion.div>
        )}
      </AnimatePresence>

      <AnimatePresence>
        {showOnboarding && <OnboardingModal key="onboarding" />}
      </AnimatePresence>

      {puzzle ? (
        <div className="main-content" id="main-content">
          <AnimatePresence>
            {solved && gameMode === 'speedrun' && elapsed >= timeLimit && (
              <motion.div className="solved-banner" key="timeup" role="status"
                style={{ background: 'var(--color-error)' }}
                initial={{ opacity: 0, y: -10 }} animate={{ opacity: 1, y: 0 }}
                exit={{ opacity: 0, y: -10 }} transition={{ duration: 0.2 }}>
                {t('game.timeUp')}
              </motion.div>
            )}
          </AnimatePresence>
          <AnimatePresence>
            {solved && !(gameMode === 'speedrun' && elapsed >= timeLimit) && gameMode !== 'marathon' && (
              <motion.div key="solved" style={{ textAlign: 'center' }}
                initial={{ opacity: 0 }} animate={{ opacity: 1 }}
                exit={{ opacity: 0 }} transition={{ duration: 0.2 }}>
                <div className="solved-banner" role="status">
                  {t('game.solved')}
                  {solvePercentile !== null && solveTotalSolvers !== null && solveTotalSolvers > 1 && (
                    <span className="solved-rank">
                      {t('game.fasterThan', { percent: Math.round(solvePercentile), rank: solveRank, total: solveTotalSolvers })}
                    </span>
                  )}
                </div>
                <button className="btn btn-sm share-result-btn" onClick={() => {
                  if (!puzzle) return
                  const text = generateShareText(puzzle, entries, cellStatus, elapsed, hintsUsed, shortId, dailyPreset, dailyDate, cellEverWrong)
                  navigator.clipboard.writeText(text).then(() => {
                    setShareCopied(true)
                    setTimeout(() => setShareCopied(false), 2000)
                  })
                }}>
                  <IconShare2 size={14} style={{ verticalAlign: -2 }} />
                  {' '}{shareCopied ? t('common.copied') : t('game.shareResult')}
                </button>
                <span className="solved-hint">{t('game.solvedHint')}</span>
                {dailyChallenge && challengeCompleted && (
                  <div className="challenge-result challenge-success">
                    {t('challenge.completed', { challenge: t(`challenge.types.${dailyChallenge}`) })}
                  </div>
                )}
                {dailyChallenge && !challengeCompleted && (
                  <div className="challenge-result challenge-failed">
                    {t('challenge.notMet', { challenge: t(`challenge.types.${dailyChallenge}`) })}
                  </div>
                )}
                <div className="post-solve-actions">
                  <button className="btn btn-sm btn-ghost" onClick={() => useStore.setState({ showHeatmap: !showHeatmap })}>
                    {showHeatmap ? t('heatmap.hide') : t('heatmap.show')}
                  </button>
                </div>
                <AnimatePresence>
                  {showHeatmap && (
                    <motion.div key="heatmap"
                      initial={{ opacity: 0, height: 0 }} animate={{ opacity: 1, height: 'auto' }}
                      exit={{ opacity: 0, height: 0 }} transition={{ duration: 0.2 }}>
                      <SolveHeatmap />
                    </motion.div>
                  )}
                </AnimatePresence>
                <PuzzleRating />
                <Leaderboard />
              </motion.div>
            )}
          </AnimatePresence>
          <AnimatePresence>
            {solved && gameMode === 'marathon' && (
              <motion.div className="solved-banner" key="marathon-done" role="status"
                initial={{ opacity: 0, y: -10 }} animate={{ opacity: 1, y: 0 }}
                exit={{ opacity: 0, y: -10 }} transition={{ duration: 0.2 }}>
                {t('game.marathonComplete', { count: marathonCount })}
              </motion.div>
            )}
          </AnimatePresence>
          <AnimatePresence>
            {marathonFailed && (
              <motion.div className="solved-banner" key="marathon-fail" role="status"
                style={{ background: 'var(--color-error)' }}
                initial={{ opacity: 0, y: -10 }} animate={{ opacity: 1, y: 0 }}
                exit={{ opacity: 0, y: -10 }} transition={{ duration: 0.2 }}>
                {t('game.marathonOver', { count: marathonCount })}
              </motion.div>
            )}
          </AnimatePresence>
          <AnimatePresence>
            {dailyChallenge && !solved && (
              <motion.div className="challenge-banner" key="challenge"
                initial={{ opacity: 0, y: -10 }} animate={{ opacity: 1, y: 0 }}
                exit={{ opacity: 0, y: -10 }} transition={{ duration: 0.15 }}>
                <span className="challenge-banner-title">{t('challenge.todaysChallenge')}: {t(`challenge.types.${dailyChallenge}`)}</span>
                <span className="challenge-banner-desc">{t(`challenge.descriptions.${dailyChallenge}`)}</span>
              </motion.div>
            )}
          </AnimatePresence>
          <ActiveClue />
          <div className="game-area">
            <div className="grid-column">
              <h2 className="visually-hidden">{t('app.crosswordGrid')}</h2>
              <Grid />
              <div className="game-input-mode" role="group" aria-label={t('settings.display.inputMode')}>
                {(['auto', 'keyboard', 'stylus'] as const).map(m => (
                  <button key={m} type="button"
                    className={`btn btn-sm ${inputMode === m ? 'btn-primary' : ''}`}
                    aria-pressed={inputMode === m}
                    onClick={() => { setInputMode(m); api.fetchSettings().then(s => api.saveSettings({ ...s, inputMode: m })) }}>
                    {m === 'auto' ? t('settings.display.inputAuto') : m === 'keyboard' ? t('settings.display.inputKeyboard') : t('settings.display.inputStylus')}
                  </button>
                ))}
              </div>
              {inputMode !== 'keyboard' && <ZoomStrip onDone={() => {}} />}
              <h2 className="visually-hidden">{t('app.gameControls')}</h2>
              <InfoBar />
            </div>
            <h2 className="visually-hidden">{t('app.clues')}</h2>
            <CluePanel />
          </div>
        </div>
      ) : (
        <div id="main-content"><Welcome /></div>
      )}

      <ReceiptNotification />
    </div>
    </MotionConfig>
  )
}
