import { useEffect, useState, useRef, useCallback } from 'react'
import './Feed.css'
import { useAuth } from '../context/AuthContext'
import { getFeedPairs, saveProgress, getUserProgress, getStats } from '../api/directus'
import ProgressRing from '../components/ProgressRing'
import PairSentenceCard from '../components/PairSentenceCard'
import PairYesNoCard    from '../components/PairYesNoCard'
import PairWordCard     from '../components/PairWordCard'
import MilestoneOverlay from '../components/MilestoneOverlay'
import SessionSummary   from '../components/SessionSummary'
import useCountUp from '../hooks/useCountUp'
import { levelForEp } from '../utils/leveling'
import { playCorrect, playMilestone } from '../utils/sound'
import { streakState } from '../utils/streak'
import { cancelStreakReminder } from '../utils/streakReminder'

// Points per answer_type
const POINTS = { text: 2, yes_no: 2, word: 3, question: 3 }
const PAGE_SIZE = 20
const STREAK_MILESTONES = [3, 7, 14, 30, 50, 100, 200, 365]

function buildCard(pair) {
  return {
    type: pair.answer_type,
    meta: { pairId: pair.id, points: pair.difficulty_level || POINTS[pair.answer_type] || 2, cardType: pair.answer_type },
    card: pair,
  }
}

export default function Feed() {
  const { user, token } = useAuth()
  const [cards, setCards]   = useState([])
  const [done,  setDone]    = useState(new Set())
  const [loading, setLoading] = useState(true)
  const [empty, setEmpty]   = useState(false)
  const [loadingMore, setLoadingMore] = useState(false)
  const [exhausted, setExhausted] = useState(false)
  const [totalEp, setTotalEp] = useState(null)
  const [daily, setDaily] = useState(null) // { ep, daily_goal_ep } – wenn /auth/stats verfügbar
  const [combo, setCombo] = useState(0)            // richtige Antworten in Folge (diese Session)
  const [milestones, setMilestones] = useState([]) // Queue: Level-Up / Streak / Tagesziel
  const [topCat, setTopCat] = useState(null)       // stärkste Kategorie für die Session-Summary
  const [reloadKey, setReloadKey] = useState(0)    // erneutes Laden nach der Summary
  const [practiced, setPracticed] = useState(false)        // heute in dieser Session geübt?
  const [streakDismissed, setStreakDismissed] = useState(false)

  // Session-Zähler (lokal, für die Abschluss-Summary) + zuletzt bekannter Fortschritt,
  // um Level-Up/Streak-Up im saveProgress-Response zu erkennen.
  const session  = useRef({ cards: 0, correct: 0, ep: 0 })
  const progress = useRef({ level: 0, streak: 0 })

  // Sanft hochzählender EP-Wert fürs Badge (statt stummem Umspringen).
  const displayEp = useCountUp(totalEp ?? 0)

  // Refs für den Nachlade-Pfad: Re-Entrancy-Schutz + immer aktuelle Kartenliste
  // (Closure im IntersectionObserver wäre sonst veraltet).
  const loadingMoreRef = useRef(false)
  const exhaustedRef = useRef(false)
  const cardsRef = useRef(cards)
  cardsRef.current = cards
  const sentinelRef = useRef(null)

  // Target language from user profile, fall back to 'de'
  const lang = user?.language_target_short || 'de'

  useEffect(() => {
    getFeedPairs(token, lang, PAGE_SIZE)
      .then(pairs => {
        const built = pairs.map(buildCard)
        setCards(built)
        setEmpty(built.length === 0)
        if (built.length < PAGE_SIZE) { exhaustedRef.current = true; setExhausted(true) }
      })
      .catch(err => { console.error('Feed load error', err); setEmpty(true) })
      .finally(() => setLoading(false))
  }, [token, lang, reloadKey])

  useEffect(() => {
    getUserProgress(token)
      .then(p => {
        setTotalEp(p.total_ep)
        // Level aus EP über die (mit dem Backend identische) Kurve ableiten, damit Level-Up
        // unabhängig vom Backend-Deploy korrekt erkannt wird.
        progress.current = { level: levelForEp(p.total_ep), streak: p.streak_days ?? 0 }
      })
      .catch(() => {})
    // Tagesziel-Fortschritt + stärkste Kategorie – degradiert lautlos, falls /auth/stats fehlt
    getStats(token)
      .then(s => {
        if (s?.today) setDaily(s.today)
        if (s?.categories?.length) setTopCat(s.categories[0])
      })
      .catch(() => {})
  }, [token])

  // Weitere Karten nachladen: schon geladene (inkl. erledigte) Pair-IDs ausschließen.
  // Leere Antwort → Server hat keine weiteren Karten → erschöpft.
  const loadMore = useCallback(async () => {
    if (loadingMoreRef.current || exhaustedRef.current) return
    loadingMoreRef.current = true
    setLoadingMore(true)
    try {
      const known = new Set(cardsRef.current.map(c => c.meta.pairId))
      const pairs = await getFeedPairs(token, lang, PAGE_SIZE, [...known])
      const fresh = pairs.filter(p => !known.has(p.id)).map(buildCard)
      if (fresh.length) setCards(prev => [...prev, ...fresh])
      if (!fresh.length || pairs.length < PAGE_SIZE) { exhaustedRef.current = true; setExhausted(true) }
    } catch (err) {
      console.error('Feed loadMore error', err)
    } finally {
      loadingMoreRef.current = false
      setLoadingMore(false)
    }
  }, [token, lang])

  // Infinite Scroll: lädt nach, sobald der Sentinel in die Nähe des Sichtbereichs kommt.
  // Großzügiger rootMargin, weil scroll-snap-mandatory einen winzigen End-Sentinel
  // sonst schwer erreichbar macht.
  useEffect(() => {
    if (loading || exhausted) return
    const el = sentinelRef.current
    if (!el) return
    const io = new IntersectionObserver(
      (entries) => { if (entries[0].isIntersecting) loadMore() },
      { root: el.closest('.feed'), rootMargin: '300px' },
    )
    io.observe(el)
    return () => io.disconnect()
  }, [loading, exhausted, loadMore])

  function handleComplete(item, result) {
    setDone(prev => new Set([...prev, item.meta.pairId]))
    const correct = result === 'correct'
    const earned = correct ? item.meta.points : 0

    // Heute geübt → Serie gesichert: Nudge weg + geplante Erinnerung abbrechen.
    if (!practiced) { setPracticed(true); cancelStreakReminder() }

    // Session-Zähler + Combo + Sound
    session.current.cards += 1
    if (correct) {
      session.current.correct += 1
      session.current.ep += earned
      setCombo(c => c + 1)
      playCorrect()
    } else {
      setCombo(0)
    }

    // Optimistische Tagesziel-Erkennung als Fallback, falls die API kein
    // goal_just_reached liefert (älteres Backend).
    const dayBefore = daily?.ep ?? null
    const goalEp = daily?.daily_goal_ep ?? null
    const optimisticGoal = earned > 0 && dayBefore != null && goalEp != null &&
      dayBefore < goalEp && dayBefore + earned >= goalEp

    saveProgress({ pairId: item.meta.pairId, correct, points: earned, userToken: token })
      .then(res => {
        if (res?.total_ep != null) setTotalEp(res.total_ep)
        if (res?.daily_ep != null && res?.daily_goal_ep != null) {
          setDaily(d => ({ ...(d || {}), ep: res.daily_ep, daily_goal_ep: res.daily_goal_ep }))
        }

        const queued = []
        // Level aus dem aktuellen EP-Stand ableiten (deploy-unabhängig, identische Kurve);
        // prev = zuletzt bekannter Level vor dieser Karte.
        const newLevel  = res?.total_ep != null ? levelForEp(res.total_ep) : progress.current.level
        const prevLevel = progress.current.level
        if (newLevel > prevLevel) queued.push({ kind: 'level', value: newLevel })

        const newStreak = res?.streak_days ?? progress.current.streak
        const streakUp  = res?.streak_increased ?? (newStreak > progress.current.streak)
        if (streakUp && STREAK_MILESTONES.includes(newStreak)) queued.push({ kind: 'streak', value: newStreak })

        if (res?.goal_just_reached ?? optimisticGoal) {
          queued.push({ kind: 'goal', value: res?.daily_goal_ep ?? goalEp })
        }

        // Neu freigeschaltete Erfolge zuletzt feiern
        for (const a of (res?.unlocked_achievements || [])) {
          queued.push({ kind: 'achievement', key: a.key, label: a.label, icon: a.icon })
        }

        progress.current = { level: newLevel, streak: newStreak }
        if (queued.length) { setMilestones(q => [...q, ...queued]); playMilestone() }
      })
      .catch(err => console.error('saveProgress error', err))

    // Tagesziel optimistisch hochzählen (wird vom Server-Response ggf. überschrieben)
    if (earned > 0) setDaily(d => d ? { ...d, ep: (d.ep || 0) + earned } : d)
  }

  // Nach der Summary erneut nach Karten suchen (Server schließt erledigte Pairs aus).
  const handleReload = () => {
    setDone(new Set())
    setExhausted(false); exhaustedRef.current = false
    session.current = { cards: 0, correct: 0, ep: 0 }
    setCombo(0)
    setLoading(true)
    setReloadKey(k => k + 1)
  }

  const visible = cards.filter(c => !done.has(c.meta.pairId))

  if (loading) {
    return (
      <div className="feed page-enter">
        <div className="feed-empty">Lade Karten…</div>
      </div>
    )
  }

  if (empty) {
    return (
      <div className="feed page-enter">
        <div className="feed-empty">Noch keine Inhalte verfügbar.</div>
      </div>
    )
  }

  const goalPct = daily && daily.daily_goal_ep ? (daily.ep || 0) / daily.daily_goal_ep : 0

  // Loss-Aversion-Nudge: Serie läuft heute ab (oder ist gerissen) und heute noch nicht geübt.
  const streak = streakState(user?.last_practice_at, user?.streak_days || 0)
  const showStreakNudge = !practiced && !streakDismissed &&
    (streak.state === 'at_risk' || streak.state === 'broken')

  return (
    <div className="feed page-enter">
      {showStreakNudge && (
        <div className="streak-nudge" role="status">
          <span>
            {streak.state === 'at_risk'
              ? `🔥 ${streak.streakDays}-Tage-Serie — nur noch ${streak.hoursLeft} Std heute!`
              : '🌱 Starte heute deine Serie neu'}
          </span>
          <button className="streak-nudge-x" onClick={() => setStreakDismissed(true)} aria-label="Schließen">×</button>
        </div>
      )}
      {combo >= 3 && (
        <div key={combo} className="combo-pill" aria-hidden="true">🔥 {combo} in Folge</div>
      )}
      {milestones.length > 0 && (
        <MilestoneOverlay
          milestone={milestones[0]}
          onClose={() => setMilestones(q => q.slice(1))}
        />
      )}
      {totalEp != null && (
        <div className="ep-badge">
          <ProgressRing
            value={daily ? goalPct : 1}
            size={26} stroke={4}
            color={goalPct >= 1 ? 'var(--success)' : 'var(--gold)'}
          >
            <span style={{ fontSize: 11 }}>{goalPct >= 1 ? '✓' : '⭐'}</span>
          </ProgressRing>
          <span className="ep-value">
            {displayEp}<small>EP{daily ? ` · ${daily.ep || 0}/${daily.daily_goal_ep} heute` : ''}</small>
          </span>
        </div>
      )}
      {visible.map((item) => {
        const cardWithMeta = { ...item.card, meta: item.meta }
        const handler = (result) => handleComplete(item, result)
        return (
          <div key={item.meta.pairId} className="feed-slot">
            {item.type === 'text'    && <PairSentenceCard card={cardWithMeta} onComplete={handler} />}
            {item.type === 'yes_no'  && <PairYesNoCard    card={cardWithMeta} onComplete={handler} />}
            {item.type === 'word'    && <PairWordCard      card={cardWithMeta} onComplete={handler} />}
            {item.type === 'question'&& <PairWordCard      card={cardWithMeta} onComplete={handler} />}
          </div>
        )
      })}

      {/* Nachlade-Bereich */}
      {!exhausted && <div ref={sentinelRef} className="feed-sentinel" aria-hidden="true" />}
      {loadingMore && <div className="feed-empty">Lade weitere Karten…</div>}
      {exhausted && (
        visible.length === 0
          ? <SessionSummary
              cards={session.current.cards}
              ep={session.current.ep}
              correct={session.current.correct}
              streak={progress.current.streak}
              topCategory={topCat}
              onReload={handleReload}
            />
          : <div className="feed-empty">Super! Alle Karten abgeschlossen. 🎉</div>
      )}
    </div>
  )
}
