import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Link } from 'react-router-dom'

interface PublicQuestion {
  id: string
  firstName: string
  question: string
  answer: string
  topic?: string
  submittedAt?: string
  answeredAt?: string
  upvotes?: number
  pinned?: boolean
}

interface DecoratedQuestion extends PublicQuestion {
  _topic: string
  _tags: string[]
  _helpful: number
  _semanticScore: number
}

type SortMode = 'relevance' | 'newest' | 'oldest' | 'helpful'

interface EngagementCounts {
  shares: number
  related: number
  expands: number
}

type EngagementMap = Record<string, EngagementCounts>

const QA_PAGE_SIZE = 8
const ENGAGEMENT_STORAGE_KEY = 'qa-engagement-v1'
const MAX_TAGS_PER_QUESTION = 4
const MAX_TAGS_VISIBLE = 10

const STOP_WORDS = new Set([
  'about', 'after', 'again', 'also', 'always', 'among', 'appears', 'around', 'been', 'before', 'being', 'between', 'both', 'could',
  'does', 'each', 'every', 'from', 'have', 'into', 'just', 'like', 'many', 'more', 'most', 'much', 'must', 'other', 'over', 'same',
  'some', 'such', 'than', 'that', 'their', 'there', 'these', 'they', 'this', 'those', 'through', 'very', 'what', 'when', 'where',
  'which', 'while', 'will', 'with', 'would', 'your', 'you', 'the', 'and', 'for', 'are', 'but', 'not', 'too', 'can', 'how', 'why',
  'who', 'was', 'were', 'into', 'upon', 'then', 'them', 'ours', 'ourselves', 'himself', 'herself', 'because', 'therefore', 'really',
  'simply', 'right', 'still', 'even', 'today', 'episode', 'episodes', 'verse', 'verses',
])

const TAG_LEXICON = [
  'bible', 'scripture', 'faith', 'grace', 'hope', 'salvation', 'gospel', 'mercy', 'obedience', 'leadership', 'elders', 'church',
  'doctrine', 'discipleship', 'prayer', 'family', 'politics', 'translation', 'reading', 'study', 'titus', 'christian',
]

const SYNONYM_MAP: Record<string, string[]> = {
  faith: ['belief', 'trust'],
  trust: ['faith', 'belief'],
  grace: ['mercy', 'favor'],
  mercy: ['grace', 'compassion'],
  hope: ['expectation', 'future'],
  love: ['charity', 'care'],
  sin: ['wrong', 'evil'],
  prayer: ['pray'],
  pray: ['prayer'],
  bible: ['scripture', 'word'],
  scripture: ['bible', 'word'],
  church: ['fellowship', 'body'],
  leadership: ['elder', 'pastor'],
  politics: ['government', 'public'],
  family: ['home', 'household'],
  translation: ['version'],
  salvation: ['saved', 'redeemed'],
}

function escapeRegExp(value: string) {
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}

function normalizeToken(token: string) {
  return token.toLowerCase().replace(/[^a-z0-9]/g, '').trim()
}

function tokenize(text: string) {
  return text
    .toLowerCase()
    .replace(/[^a-z0-9\s]/g, ' ')
    .split(/\s+/)
    .map(normalizeToken)
    .filter(token => token.length > 2)
}

function expandQueryTokens(tokens: string[]) {
  const expanded = new Set<string>()
  for (const token of tokens) {
    expanded.add(token)
    const synonyms = SYNONYM_MAP[token] ?? []
    for (const synonym of synonyms) expanded.add(synonym)
  }
  return expanded
}

function renderHighlightedText(text: string, query: string) {
  const trimmed = query.trim()
  if (!trimmed) return text

  const regex = new RegExp(`(${escapeRegExp(trimmed)})`, 'ig')
  const parts = text.split(regex)

  return parts.map((part, index) => {
    if (part.toLowerCase() === trimmed.toLowerCase()) {
      return <mark key={`mark-${index}`} className="qa-highlight">{part}</mark>
    }
    return <span key={`plain-${index}`}>{part}</span>
  })
}

function renderTextWithLinks(text: string, highlightQuery = '') {

  const BOOK_ID_MAP: Record<string, string> = {
    gen: 'GEN', genesis: 'GEN', exo: 'EXO', exodus: 'EXO', lev: 'LEV', leviticus: 'LEV',
    num: 'NUM', numbers: 'NUM', deut: 'DEU', deuteronomy: 'DEU', josh: 'JOS', joshua: 'JOS',
    judg: 'JDG', judges: 'JDG', ruth: 'RUT', '1 sam': 'SA1', '2 sam': 'SA2',
    '1 kgs': 'KI1', '1 kings': 'KI1', '2 kgs': 'KI2', '2 kings': 'KI2',
    '1 chr': 'CH1', '1 chron': 'CH1', '1 chronicles': 'CH1', '2 chr': 'CH2', '2 chron': 'CH2', '2 chronicles': 'CH2',
    ezra: 'EZR', neh: 'NEH', nehemiah: 'NEH', esth: 'EST', esther: 'EST',
    job: 'JOB', ps: 'PSA', psalms: 'PSA', psalm: 'PSA', prov: 'PRO', proverbs: 'PRO',
    eccl: 'ECC', ecclesiastes: 'ECC', song: 'SNG', 'song of sol': 'SNG', 'song of solomon': 'SNG',
    isa: 'ISA', isaiah: 'ISA', jer: 'JER', jeremiah: 'JER', lam: 'LAM', lamentations: 'LAM',
    ezek: 'EZK', ezekiel: 'EZK', dan: 'DAN', daniel: 'DAN', hos: 'HOS', hosea: 'HOS',
    joel: 'JOL', amos: 'AMO', obad: 'OBA', obadiah: 'OBA', jonah: 'JNA', mic: 'MIC', micah: 'MIC',
    nah: 'NAH', nahum: 'NAH', hab: 'HAB', habakkuk: 'HAB', zeph: 'ZEP', zephaniah: 'ZEP',
    hag: 'HAG', haggai: 'HAG', zech: 'ZEC', zechariah: 'ZEC', mal: 'MAL', malachi: 'MAL',
    matt: 'MAT', matthew: 'MAT', mark: 'MRK', luke: 'LUK', john: 'JHN', acts: 'ACT',
    rom: 'ROM', romans: 'ROM', '1 cor': 'CO1', '1 corinthians': 'CO1', '2 cor': 'CO2', '2 corinthians': 'CO2',
    gal: 'GAL', galatians: 'GAL', eph: 'EPH', ephesians: 'EPH', phil: 'PHP', philippians: 'PHP',
    col: 'COL', colossians: 'COL', '1 thess': 'TH1', '1 thessalonians': 'TH1', '2 thess': 'TH2', '2 thessalonians': 'TH2',
    '1 tim': 'TI1', '1 timothy': 'TI1', '2 tim': 'TI2', '2 timothy': 'TI2',
    titus: 'TIT', philem: 'PHM', philemon: 'PHM', heb: 'HEB', hebrews: 'HEB',
    jas: 'JAM', james: 'JAM', '1 pet': 'PE1', '1 peter': 'PE1', '2 pet': 'PE2', '2 peter': 'PE2',
    '1 john': 'JO1', '2 john': 'JO2', '3 john': 'JO3', jude: 'JDE', rev: 'REV', revelation: 'REV',
  }

  function parseScriptureRef(refText: string): { bookId: string; chapter: number; verseStart: number; verseEnd: number } | null {
    const match = refText.match(/^(.*?)\s+(\d+):(\d+)(?:-(\d+))?$/)
    if (!match) return null
    const [, bookRaw, chapterStr, verseStartStr, verseEndStr] = match
    const bookKey = bookRaw.toLowerCase().replace(/\.\s*/g, ' ').trim()
    const bookId = BOOK_ID_MAP[bookKey]
    if (!bookId) return null
    return {
      bookId,
      chapter: parseInt(chapterStr, 10),
      verseStart: parseInt(verseStartStr, 10),
      verseEnd: verseEndStr ? parseInt(verseEndStr, 10) : parseInt(verseStartStr, 10),
    }
  }

  // Split the text by both scripture refs and URLs
  const combined = /(https?:\/\/[^\s]+)|\b((?:(?:1|2|3)\s)?(?:Gen(?:esis)?|Exo(?:dus)?|Lev(?:iticus)?|Num(?:bers)?|Deut(?:eronomy)?|Josh(?:ua)?|Judg(?:es)?|Ruth|1\s?Sam|2\s?Sam|1\s?Kgs?|2\s?Kgs?|1\s?Chr(?:on)?|2\s?Chr(?:on)?|Ezra|Neh(?:emiah)?|Esth(?:er)?|Job|Ps(?:alms?)?|Prov(?:erbs)?|Eccl(?:esiastes)?|Song(?:\s?of\s?Sol(?:omon)?)?|Isa(?:iah)?|Jer(?:emiah)?|Lam(?:entations)?|Ezek(?:iel)?|Dan(?:iel)?|Hos(?:ea)?|Joel|Amos|Obad(?:iah)?|Jonah|Mic(?:ah)?|Nah(?:um)?|Hab(?:akkuk)?|Zeph(?:aniah)?|Hag(?:gai)?|Zech(?:ariah)?|Mal(?:achi)?|Matt(?:hew)?|Mark|Luke|John|Acts|Rom(?:ans)?|1\s?Cor(?:inthians)?|2\s?Cor(?:inthians)?|Gal(?:atians)?|Eph(?:esians)?|Phil(?:ippians)?|Col(?:ossians)?|1\s?Thess|2\s?Thess|1\s?Tim(?:othy)?|2\s?Tim(?:othy)?|Titus|Philem(?:on)?|Heb(?:rews)?|Jas(?:mes)?|1\s?Pet(?:er)?|2\s?Pet(?:er)?|1\s?John|2\s?John|3\s?John|Jude|Rev(?:elation)?)\.?\s+\d+:\d+(?:-\d+)?)\b/gi

  const parts: string[] = []
  let lastIndex = 0
  let m: RegExpExecArray | null

  // reset lastIndex for combined
  combined.lastIndex = 0
  while ((m = combined.exec(text)) !== null) {
    if (m.index > lastIndex) parts.push(text.slice(lastIndex, m.index))
    parts.push(m[0])
    lastIndex = m.index + m[0].length
  }
  if (lastIndex < text.length) parts.push(text.slice(lastIndex))

  return parts.map((part, index) => {
    if (/^https?:\/\//i.test(part)) {
      const safeHref = part.replace(/[),.;!?]+$/g, '')
      const trailing = part.slice(safeHref.length)
      return (
        <span key={`link-${index}`}>
          <a href={safeHref} target="_blank" rel="noopener noreferrer">{safeHref}</a>
          {trailing}
        </span>
      )
    }

    const parsed = parseScriptureRef(part.replace(/\.$/, ''))
    if (parsed) {
      return <ScriptureTooltip key={`scripture-${index}`} refText={part} bookId={parsed.bookId} chapter={parsed.chapter} verseStart={parsed.verseStart} verseEnd={parsed.verseEnd} />
    }

    return <span key={`text-${index}`}>{renderHighlightedText(part, highlightQuery)}</span>
  })
}

interface VerseData {
  verse: number
  value: string
}

// Maps helloao API book IDs → bible.com book codes
const BIBLE_COM_BOOK_IDS: Record<string, string> = {
  GEN: 'GEN', EXO: 'EXO', LEV: 'LEV', NUM: 'NUM', DEU: 'DEU', JOS: 'JOS', JDG: 'JDG', RUT: 'RUT',
  SA1: '1SA', SA2: '2SA', KI1: '1KI', KI2: '2KI', CH1: '1CH', CH2: '2CH',
  EZR: 'EZR', NEH: 'NEH', EST: 'EST', JOB: 'JOB', PSA: 'PSA', PRO: 'PRO', ECC: 'ECC', SNG: 'SNG',
  ISA: 'ISA', JER: 'JER', LAM: 'LAM', EZK: 'EZK', DAN: 'DAN', HOS: 'HOS', JOL: 'JOL', AMO: 'AMO',
  OBA: 'OBA', JNA: 'JON', MIC: 'MIC', NAH: 'NAH', HAB: 'HAB', ZEP: 'ZEP', HAG: 'HAG', ZEC: 'ZEC', MAL: 'MAL',
  MAT: 'MAT', MRK: 'MRK', LUK: 'LUK', JHN: 'JHN', ACT: 'ACT', ROM: 'ROM',
  CO1: '1CO', CO2: '2CO', GAL: 'GAL', EPH: 'EPH', PHP: 'PHP', COL: 'COL',
  TH1: '1TH', TH2: '2TH', TI1: '1TI', TI2: '2TI', TIT: 'TIT', PHM: 'PHM', HEB: 'HEB',
  JAM: 'JAS', PE1: '1PE', PE2: '2PE', JO1: '1JN', JO2: '2JN', JO3: '3JN', JDE: 'JUD', REV: 'REV',
}

function ScriptureTooltip({ refText, bookId, chapter, verseStart, verseEnd }: {
  refText: string
  bookId: string
  chapter: number
  verseStart: number
  verseEnd: number
}) {
  const [open, setOpen] = useState(false)
  const [verses, setVerses] = useState<VerseData[]>([])
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState(false)
  const wrapRef = useRef<HTMLSpanElement>(null)

  useEffect(() => {
    if (!open) return
    if (verses.length > 0) return
    setLoading(true)
    setError(false)
    fetch(`https://bible.helloao.org/api/BSB/${bookId}/${chapter}.json`)
      .then(r => {
        if (!r.ok) throw new Error('Not found')
        return r.json() as Promise<{ chapter: { content: Array<{ type: string; number?: number; content?: Array<{ text?: string; poem?: number } | string> }> } }>
      })
      .then(data => {
        const content = data?.chapter?.content ?? []
        const found: VerseData[] = []
        for (const item of content) {
          if (item.type === 'verse' && item.number != null && item.number >= verseStart && item.number <= verseEnd) {
            const text = (item.content ?? [])
              .map(c => (typeof c === 'string' ? c : (c.text ?? '')))
              .join('')
              .trim()
            if (text) found.push({ verse: item.number, value: text })
          }
        }
        setVerses(found)
        setLoading(false)
      })
      .catch(() => {
        setError(true)
        setLoading(false)
      })
  }, [open, bookId, chapter, verseStart, verseEnd, verses.length])

  useEffect(() => {
    if (!open) return
    function handleClickOutside(e: MouseEvent) {
      if (wrapRef.current && !wrapRef.current.contains(e.target as Node)) {
        setOpen(false)
      }
    }
    document.addEventListener('mousedown', handleClickOutside)
    return () => document.removeEventListener('mousedown', handleClickOutside)
  }, [open])

  return (
    <span ref={wrapRef} className="scripture-ref-wrap">
      <button
        type="button"
        className="scripture-ref"
        onClick={() => setOpen(o => !o)}
        aria-expanded={open}
        title={`View ${refText}`}
      >
        {refText}
      </button>
      {open && (
        <span className="scripture-popup" role="tooltip">
          <span className="scripture-popup-header">
            <strong>{refText}</strong>
            <button type="button" className="scripture-popup-close" onClick={() => setOpen(false)} aria-label="Close">✕</button>
          </span>
          {loading && <span className="scripture-popup-body">Loading…</span>}
          {error && <span className="scripture-popup-body scripture-popup-error">Could not load verse.</span>}
          {!loading && !error && verses.length === 0 && <span className="scripture-popup-body">Verse not found.</span>}
          {!loading && !error && verses.map(v => (
            <span key={v.verse} className="scripture-popup-body">
              {verseStart !== verseEnd && <sup>{v.verse} </sup>}{v.value}
            </span>
          ))}
          <span className="scripture-popup-footer">
            <a
              href={`https://www.bible.com/bible/3034/${BIBLE_COM_BOOK_IDS[bookId] ?? bookId}.${chapter}.BSB`}
              target="_blank"
              rel="noopener noreferrer"
              className="scripture-popup-link"
            >
              Read on Bible.com ↗
            </a>
            <span className="scripture-popup-attribution">Berean Standard Bible</span>
          </span>
        </span>
      )}
    </span>
  )
}

// ─── old renderTextWithLinks removed, replaced above ───

function readEngagementFromStorage(): EngagementMap {
  try {
    const raw = window.localStorage.getItem(ENGAGEMENT_STORAGE_KEY)
    if (!raw) return {}
    const parsed = JSON.parse(raw)
    return typeof parsed === 'object' && parsed ? (parsed as EngagementMap) : {}
  } catch {
    return {}
  }
}

function truncateAnswer(text: string, limit = 260) {
  if (text.length <= limit) return text
  const sliced = text.slice(0, limit)
  const safeCut = sliced.lastIndexOf(' ')
  return `${sliced.slice(0, safeCut > 120 ? safeCut : limit).trim()}...`
}

function semanticScoreForQuestion(question: PublicQuestion, tags: string[], query: string) {
  const normalized = query.trim().toLowerCase()
  if (!normalized) return 0

  const queryTokens = tokenize(normalized)
  if (queryTokens.length === 0) return 0

  const expanded = expandQueryTokens(queryTokens)
  const questionTokens = new Set(tokenize(`${question.question} ${question.answer} ${question.topic ?? ''} ${tags.join(' ')}`))

  let score = 0
  for (const token of expanded) {
    if (questionTokens.has(token)) score += 2
  }

  const lowerQuestion = question.question.toLowerCase()
  const lowerAnswer = question.answer.toLowerCase()
  const lowerTopic = (question.topic ?? '').toLowerCase()

  if (lowerQuestion.includes(normalized)) score += 6
  if (lowerAnswer.includes(normalized)) score += 3
  if (lowerTopic.includes(normalized)) score += 2

  for (const token of queryTokens) {
    if (token.length < 4) continue
    if (lowerQuestion.includes(token)) score += 1
    if (lowerAnswer.includes(token)) score += 0.5
  }

  return score
}

function tokenizeForRelated(text: string) {
  return tokenize(text).filter(token => token.length > 3 && !STOP_WORDS.has(token))
}

function extractAutoTags(question: PublicQuestion) {
  const source = `${question.question} ${question.answer}`.toLowerCase()
  const topicToken = normalizeToken(question.topic ?? '')

  const lexiconHits = TAG_LEXICON
    .filter(tag => source.includes(tag))
    .filter(tag => tag !== topicToken)

  if (lexiconHits.length > 0) {
    return Array.from(new Set(lexiconHits)).slice(0, MAX_TAGS_PER_QUESTION)
  }

  const questionOnlyTokens = tokenize(question.question)
    .filter(token => token.length >= 5 && !STOP_WORDS.has(token) && token !== topicToken)
    .slice(0, MAX_TAGS_PER_QUESTION)

  return Array.from(new Set(questionOnlyTokens))
}

export default function QASection() {
  const [questions, setQuestions] = useState<PublicQuestion[]>([])
  const [searchQuery, setSearchQuery] = useState('')
  const [selectedTopic, setSelectedTopic] = useState<string | null>(null)
  const [selectedTag, setSelectedTag] = useState<string | null>(null)
  const [focusedQuestionId, setFocusedQuestionId] = useState<string | null>(null)
  const [sortMode, setSortMode] = useState<SortMode>('relevance')
  const [compactMode, setCompactMode] = useState(false)
  const [page, setPage] = useState(0)
  const [engagement, setEngagement] = useState<EngagementMap>({})
  const [expandedCompactAnswers, setExpandedCompactAnswers] = useState<Record<string, boolean>>({})
  const [copiedQuestionId, setCopiedQuestionId] = useState<string | null>(null)
  const [loading, setLoading] = useState(true)
  const [votedIds, setVotedIds] = useState<Set<string>>(() => {
    try {
      const raw = localStorage.getItem('qa-voted-ids-v1')
      return new Set(raw ? JSON.parse(raw) as string[] : [])
    } catch { return new Set() }
  })
  const resultsTopRef = useRef<HTMLDivElement | null>(null)

  useEffect(() => {
    fetch('/api/questions')
      .then(r => (r.ok ? r.json() : Promise.reject(new Error('Failed to load questions'))))
      .then(data => {
        setQuestions((data as { questions: PublicQuestion[] }).questions ?? [])
        setLoading(false)
      })
      .catch(() => {
        setLoading(false)
      })
  }, [])

  async function handleUpvote(id: string) {
    if (votedIds.has(id)) return
    try {
      const res = await fetch(`/api/questions/${encodeURIComponent(id)}/upvote`, { method: 'POST' })
      if (!res.ok) return
      const data = await res.json() as { upvotes: number }
      setQuestions(prev => prev.map(q => q.id === id ? { ...q, upvotes: data.upvotes } : q))
      setVotedIds(prev => {
        const next = new Set(prev)
        next.add(id)
        localStorage.setItem('qa-voted-ids-v1', JSON.stringify([...next]))
        return next
      })
    } catch { /* ignore */ }
  }

  useEffect(() => {
    setEngagement(readEngagementFromStorage())
  }, [])

  useEffect(() => {
    window.localStorage.setItem(ENGAGEMENT_STORAGE_KEY, JSON.stringify(engagement))
  }, [engagement])

  const incrementEngagement = useCallback((id: string, metric: keyof EngagementCounts) => {
    setEngagement(prev => {
      const current = prev[id] ?? { shares: 0, related: 0, expands: 0 }
      return {
        ...prev,
        [id]: {
          ...current,
          [metric]: current[metric] + 1,
        },
      }
    })
  }, [])

  const normalizedSearch = searchQuery.trim().toLowerCase()

  const questionSet = useMemo(() => {
    return questions.map(question => {
      const topic = question.topic?.trim() || 'General'
      const tags = extractAutoTags(question)
      const counts = engagement[question.id] ?? { shares: 0, related: 0, expands: 0 }
      const helpful = counts.shares * 3 + counts.related * 2 + counts.expands
      const semantic = semanticScoreForQuestion(question, tags, normalizedSearch)
      return {
        ...question,
        _topic: topic,
        _tags: tags,
        _helpful: helpful,
        _semanticScore: semantic,
      }
    })
  }, [engagement, normalizedSearch, questions])

  useEffect(() => {
    const syncFromHash = () => {
      const hash = window.location.hash
      if (!hash.startsWith('#qa-')) return
      const id = decodeURIComponent(hash.slice(4))
      const matched = questionSet.find(item => item.id === id)
      if (!matched) return

      setFocusedQuestionId(id)
      setSelectedTopic(matched._topic)

      window.setTimeout(() => {
        const element = document.getElementById(`qa-${id}`)
        element?.scrollIntoView({ behavior: 'smooth', block: 'center' })
      }, 80)
    }

    syncFromHash()
    window.addEventListener('hashchange', syncFromHash)
    return () => window.removeEventListener('hashchange', syncFromHash)
  }, [questionSet])

  const topicCounts = useMemo(() => {
    const counts = new Map<string, number>()
    for (const question of questionSet) {
      counts.set(question._topic, (counts.get(question._topic) ?? 0) + 1)
    }
    return Array.from(counts.entries())
      .map(([topic, count]) => ({ topic, count }))
      .sort((a, b) => b.count - a.count || a.topic.localeCompare(b.topic))
  }, [questionSet])

  const tagCounts = useMemo(() => {
    const counts = new Map<string, number>()
    for (const question of questionSet) {
      for (const tag of question._tags) {
        counts.set(tag, (counts.get(tag) ?? 0) + 1)
      }
    }
    return Array.from(counts.entries())
      .map(([tag, count]) => ({ tag, count }))
      .sort((a, b) => b.count - a.count || a.tag.localeCompare(b.tag))
      .slice(0, MAX_TAGS_VISIBLE)
  }, [questionSet])

  const filteredQuestions = useMemo(() => {
    const output = questionSet.filter(question => {
      const matchesTopic = !selectedTopic || question._topic === selectedTopic
      const matchesTag = !selectedTag || question._tags.includes(selectedTag)
      const matchesSearch = !normalizedSearch || question._semanticScore > 0
      return matchesTopic && matchesTag && matchesSearch
    })

    const timestamp = (question: PublicQuestion) => {
      const value = question.answeredAt ?? question.submittedAt
      return value ? new Date(value).getTime() : 0
    }

    output.sort((a, b) => {
      if (sortMode === 'newest') return timestamp(b) - timestamp(a)
      if (sortMode === 'oldest') return timestamp(a) - timestamp(b)
      if (sortMode === 'helpful') {
        if (b._helpful !== a._helpful) return b._helpful - a._helpful
        return timestamp(b) - timestamp(a)
      }
      if (b._semanticScore !== a._semanticScore) return b._semanticScore - a._semanticScore
      return timestamp(b) - timestamp(a)
    })

    return output
  }, [normalizedSearch, questionSet, selectedTag, selectedTopic, sortMode])

  const totalPages = Math.max(1, Math.ceil(filteredQuestions.length / QA_PAGE_SIZE))
  const safePage = Math.min(page, totalPages - 1)
  const pageStart = safePage * QA_PAGE_SIZE
  const pageEnd = Math.min(pageStart + QA_PAGE_SIZE, filteredQuestions.length)
  const pagedQuestions = filteredQuestions.slice(pageStart, pageEnd)

  useEffect(() => {
    setPage(0)
  }, [normalizedSearch, selectedTag, selectedTopic, sortMode])

  useEffect(() => {
    if (page > totalPages - 1) setPage(totalPages - 1)
  }, [page, totalPages])

  useEffect(() => {
    if (!focusedQuestionId) return
    const timer = window.setTimeout(() => setFocusedQuestionId(null), 2200)
    return () => window.clearTimeout(timer)
  }, [focusedQuestionId])

  useEffect(() => {
    if (!focusedQuestionId) return
    const index = filteredQuestions.findIndex(item => item.id === focusedQuestionId)
    if (index === -1) return
    const nextPage = Math.floor(index / QA_PAGE_SIZE)
    if (nextPage !== safePage) setPage(nextPage)
  }, [filteredQuestions, focusedQuestionId, safePage])

  const goToPage = (nextPage: number) => {
    setPage(nextPage)
    window.setTimeout(() => {
      resultsTopRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' })
    }, 40)
  }

  const handleSearch = (value: string) => {
    setSearchQuery(value)
  }

  const handleTopic = (topic: string | null) => {
    setSelectedTopic(topic)
  }

  const handleTag = (tag: string | null) => {
    setSelectedTag(tag)
  }

  const clearFilters = () => {
    setSelectedTopic(null)
    setSelectedTag(null)
    setSearchQuery('')
    setPage(0)
  }

  const buildQuoteCanvas = (question: DecoratedQuestion): HTMLCanvasElement => {
    const SIZE = 1080
    const PADDING = 72
    const canvas = document.createElement('canvas')
    canvas.width = SIZE
    canvas.height = SIZE
    const ctx = canvas.getContext('2d')!

    ctx.fillStyle = '#1a1208'
    ctx.fillRect(0, 0, SIZE, SIZE)

    ctx.fillStyle = '#c9a84c'
    ctx.fillRect(0, 0, SIZE, 6)

    function wrapText(text: string, maxWidth: number, font: string): string[] {
      ctx.font = font
      const words = text.split(' ')
      const lines: string[] = []
      let line = ''
      for (const word of words) {
        const test = line ? `${line} ${word}` : word
        if (ctx.measureText(test).width > maxWidth && line) { lines.push(line); line = word }
        else { line = test }
      }
      if (line) lines.push(line)
      return lines
    }

    let y = PADDING + 40

    ctx.fillStyle = '#c9a84c'
    ctx.font = 'bold 26px Georgia, serif'
    ctx.textAlign = 'center'
    ctx.fillText('✦  Verse by Verse with Nate  ✦', SIZE / 2, y)
    y += 50

    ctx.fillStyle = '#c9a84c'
    ctx.fillRect(PADDING, y, SIZE - PADDING * 2, 1)
    y += 36

    ctx.fillStyle = '#c9a84c'
    ctx.font = 'bold 22px Georgia, serif'
    ctx.textAlign = 'left'
    ctx.fillText('Q', PADDING, y)
    y += 8

    const qFont = 'italic 32px Georgia, serif'
    const qLines = wrapText(`"${question.question}"`, SIZE - PADDING * 2 - 20, qFont)
    ctx.fillStyle = '#f5ead2'
    ctx.font = qFont
    ctx.textAlign = 'left'
    const qLineH = 44
    for (const line of qLines.slice(0, 5)) { y += qLineH; ctx.fillText(line, PADDING + 20, y) }
    y += 40

    ctx.fillStyle = '#3a2d14'
    ctx.fillRect(PADDING, y, SIZE - PADDING * 2, 1)
    y += 36

    ctx.fillStyle = '#c9a84c'
    ctx.font = 'bold 22px Georgia, serif'
    ctx.textAlign = 'left'
    ctx.fillText('A', PADDING, y)
    y += 8

    const aFont = '28px Georgia, serif'
    const maxAHeight = SIZE - y - PADDING - 60
    const aLineH = 40
    const maxALines = Math.floor(maxAHeight / aLineH)
    const rawAnswer = question.answer.replace(/\*\*/g, '').replace(/\*/g, '').replace(/<[^>]+>/g, '')
    const aLines = wrapText(rawAnswer, SIZE - PADDING * 2 - 20, aFont).slice(0, maxALines)
    if (aLines.length > 0 && rawAnswer.split(' ').length > aLines.join(' ').split(' ').length) {
      aLines[aLines.length - 1] = aLines[aLines.length - 1].replace(/\s*\w+$/, '…')
    }
    ctx.fillStyle = '#e8d9b5'
    ctx.font = aFont
    for (const line of aLines) { y += aLineH; ctx.fillText(line, PADDING + 20, y) }

    ctx.fillStyle = '#8a7040'
    ctx.font = '20px Georgia, serif'
    ctx.textAlign = 'center'
    ctx.fillText(window.location.host + '/questions', SIZE / 2, SIZE - PADDING + 8)

    return canvas
  }

  const saveAsImage = (question: DecoratedQuestion) => {
    const canvas = buildQuoteCanvas(question)
    const link = document.createElement('a')
    link.download = `vbvn-qa-${question.id.slice(0, 8)}.png`
    link.href = canvas.toDataURL('image/png')
    link.click()
  }

  const shareImageTo = async (question: DecoratedQuestion, platform: 'x' | 'facebook') => {
    const canvas = buildQuoteCanvas(question)
    const shareUrl = getSocialUrl(question.id)
    const shareText = `Q: ${question.question.slice(0, 120)}${question.question.length > 120 ? '…' : ''}`

    // Try Web Share API with file (works on mobile — iOS Safari, Android Chrome)
    if (typeof navigator.share === 'function') {
      try {
        const blob = await new Promise<Blob>((resolve, reject) =>
          canvas.toBlob(b => b ? resolve(b) : reject(new Error('Canvas toBlob failed')), 'image/png')
        )
        const file = new File([blob], `vbvn-qa-${question.id.slice(0, 8)}.png`, { type: 'image/png' })
        if (navigator.canShare?.({ files: [file] })) {
          await navigator.share({ files: [file], text: shareText, url: shareUrl })
          incrementEngagement(question.id, 'shares')
          return
        }
      } catch (err) {
        if ((err as DOMException)?.name === 'AbortError') return
      }
    }

    // Desktop fallback: download the image, then open the compose window
    const link = document.createElement('a')
    link.download = `vbvn-qa-${question.id.slice(0, 8)}.png`
    link.href = canvas.toDataURL('image/png')
    link.click()

    if (platform === 'x') {
      const tweetUrl = `https://twitter.com/intent/tweet?text=${encodeURIComponent(shareText)}&url=${encodeURIComponent(shareUrl)}`
      window.open(tweetUrl, '_blank', 'width=600,height=400,noopener')
    } else {
      const fbUrl = `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(shareUrl)}`
      window.open(fbUrl, '_blank', 'width=600,height=400,noopener')
    }
    incrementEngagement(question.id, 'shares')
  }

  const getShareUrl = (id: string) =>
    `${window.location.origin}/questions#qa-${encodeURIComponent(id)}`

  const getSocialUrl = (id: string) =>
    `${window.location.origin}/questions/share/${encodeURIComponent(id)}`

  const shareQuestion = async (id: string) => {
    const shareUrl = getShareUrl(id)
    try {
      await navigator.clipboard.writeText(shareUrl)
      incrementEngagement(id, 'shares')
      setCopiedQuestionId(id)
      window.history.replaceState(null, '', `/questions#qa-${encodeURIComponent(id)}`)
      window.setTimeout(() => setCopiedQuestionId(curr => (curr === id ? null : curr)), 1800)
    } catch {
      window.prompt('Copy this link:', shareUrl)
    }
  }


  const formatDate = (value?: string) => {
    if (!value) return null
    const parsed = new Date(value)
    if (Number.isNaN(parsed.getTime())) return null
    return parsed.toLocaleDateString()
  }

  const toggleCompactAnswer = (id: string) => {
    const next = !expandedCompactAnswers[id]
    setExpandedCompactAnswers(prev => ({ ...prev, [id]: next }))
    if (next) incrementEngagement(id, 'expands')
  }

  const getRelatedQuestions = (current: DecoratedQuestion) => {
    const currentTokens = new Set(tokenizeForRelated(`${current.question} ${current.answer}`))

    return questionSet
      .filter(candidate => candidate.id !== current.id)
      .map(candidate => {
        const candidateTokens = tokenizeForRelated(`${candidate.question} ${candidate.answer}`)
        const overlap = candidateTokens.filter(token => currentTokens.has(token)).length
        const sameTopic = current._topic === candidate._topic
        const score = overlap + (sameTopic ? 5 : 0)
        return { candidate, score }
      })
      .filter(item => item.score > 0)
      .sort((a, b) => b.score - a.score)
      .slice(0, 3)
      .map(item => item.candidate)
  }

  const topSuggestedTopic = topicCounts[0]?.topic ?? null
  const mostHelpfulQuestion = [...questionSet].sort((a, b) => b._helpful - a._helpful)[0] ?? null

  return (
    <section id="qa" className="section-qa" aria-label="Questions and answers">
      <div className="section-inner">
        <div className="qa-submit-cta">
          <p className="qa-submit-cta-text">Have a question about Scripture or the podcast?</p>
          <Link to="/contact" className="btn-primary">Submit a Question →</Link>
        </div>

        {loading ? (
          <div className="qa-skeleton-list" aria-busy="true" aria-label="Loading questions">
            {[0, 1, 2].map(i => (
              <div key={i} className="qa-skeleton-card">
                <div className="qa-skeleton-line qa-skeleton-line--short" />
                <div className="qa-skeleton-line" />
                <div className="qa-skeleton-line qa-skeleton-line--medium" />
              </div>
            ))}
          </div>
        ) : questions.length === 0 ? (
          <div className="qa-no-results">
            <p>No questions have been answered yet. <a href="/contact">Submit yours below!</a></p>
          </div>
        ) : (
          <>
            <div className="qa-toolbar">
              <button
                type="button"
                className={`qa-view-toggle${compactMode ? ' qa-view-toggle--active' : ''}`}
                onClick={() => setCompactMode(value => !value)}
              >
                {compactMode ? 'Compact view: On' : 'Compact view: Off'}
              </button>
            </div>

            <div className="qa-filters">
              <div className="qa-topics">
                <button
                  type="button"
                  className={`qa-topic-btn${selectedTopic === null ? ' qa-topic-btn--active' : ''}`}
                  onClick={() => handleTopic(null)}
                >
                  All
                </button>
                {topicCounts.map(({ topic }) => (
                  <button
                    type="button"
                    key={topic}
                    className={`qa-topic-btn${selectedTopic === topic ? ' qa-topic-btn--active' : ''}`}
                    onClick={() => handleTopic(topic)}
                  >
                    {topic}
                  </button>
                ))}
              </div>
              <div className="qa-tags">
                <button
                  type="button"
                  className={`qa-tag-btn${selectedTag === null ? ' qa-tag-btn--active' : ''}`}
                  onClick={() => handleTag(null)}
                >
                  All tags
                </button>
                {tagCounts.map(({ tag }) => (
                  <button
                    type="button"
                    key={tag}
                    className={`qa-tag-btn${selectedTag === tag ? ' qa-tag-btn--active' : ''}`}
                    onClick={() => handleTag(tag)}
                  >
                    #{tag}
                  </button>
                ))}
              </div>
              <div className="qa-search">
                <label>
                  <span className="visually-hidden">Search Questions</span>
                  <input
                    type="text"
                    placeholder="Search naturally (example: how to stay consistent in Bible reading)"
                    value={searchQuery}
                    onChange={e => handleSearch(e.target.value)}
                  />
                </label>
              </div>
            </div>

            {(searchQuery || selectedTopic || selectedTag) && (
              <div className="qa-filter-chips" aria-label="Active filters">
                {searchQuery && (
                  <button type="button" className="qa-chip" onClick={() => setSearchQuery('')}>
                    Search: {searchQuery} x
                  </button>
                )}
                {selectedTopic && (
                  <button type="button" className="qa-chip" onClick={() => setSelectedTopic(null)}>
                    Topic: {selectedTopic} x
                  </button>
                )}
                {selectedTag && (
                  <button type="button" className="qa-chip" onClick={() => setSelectedTag(null)}>
                    Tag: #{selectedTag} x
                  </button>
                )}
                <button type="button" className="qa-chip qa-chip--clear" onClick={clearFilters}>Clear all</button>
              </div>
            )}

            {filteredQuestions.length === 0 ? (
              <div className="qa-no-results qa-no-results--smart">
                <p>No matches for this search yet.</p>
                {topSuggestedTopic && (
                  <p>
                    Try browsing <button type="button" className="qa-inline-link" onClick={() => { clearFilters(); setSelectedTopic(topSuggestedTopic) }}>{topSuggestedTopic}</button> questions.
                  </p>
                )}
                {mostHelpfulQuestion && (
                  <p>
                    Or jump to <button type="button" className="qa-inline-link" onClick={() => {
                      clearFilters()
                      setFocusedQuestionId(mostHelpfulQuestion.id)
                      window.history.replaceState(null, '', `/questions#qa-${encodeURIComponent(mostHelpfulQuestion.id)}`)
                    }}>{mostHelpfulQuestion.question}</button>.
                  </p>
                )}
                <p><a href="/contact">Submit your question</a> and Nate may add it here.</p>
              </div>
            ) : (
              <>
                <div className="qa-meta-row">
                  <p className="qa-result-count">
                    {filteredQuestions.length} result{filteredQuestions.length === 1 ? '' : 's'}
                    {normalizedSearch && <span> for "{normalizedSearch}"</span>}
                    {filteredQuestions.length > 0 && <span> · Showing {pageStart + 1}-{pageEnd}</span>}
                  </p>
                  <label className="qa-sort-select" htmlFor="qa-sort-mode">
                    Sort
                    <select id="qa-sort-mode" value={sortMode} onChange={e => setSortMode(e.target.value as SortMode)}>
                      <option value="relevance">Best match</option>
                      <option value="helpful">Most helpful</option>
                      <option value="newest">Newest first</option>
                      <option value="oldest">Oldest first</option>
                    </select>
                  </label>
                </div>

                <div className="qa-layout">
                  <aside className="qa-sidebar" aria-label="Question topics">
                    <p className="qa-sidebar-label">Browse by topic</p>
                    <button
                      type="button"
                      className={`qa-topic-btn qa-topic-btn--full${selectedTopic === null ? ' qa-topic-btn--active' : ''}`}
                      onClick={() => handleTopic(null)}
                    >
                      <span>All topics</span>
                      <span>{questions.length}</span>
                    </button>
                    {topicCounts.map(({ topic, count }) => (
                      <button
                        type="button"
                        key={topic}
                        className={`qa-topic-btn qa-topic-btn--full${selectedTopic === topic ? ' qa-topic-btn--active' : ''}`}
                        onClick={() => handleTopic(topic)}
                      >
                        <span>{topic}</span>
                        <span>{count}</span>
                      </button>
                    ))}
                  </aside>

                  <div className={`qa-cards${compactMode ? ' qa-cards--compact' : ''}`}>
                    <div ref={resultsTopRef} aria-hidden="true" />
                    {pagedQuestions.map(question => {
                      const relatedQuestions = getRelatedQuestions(question)
                      const isFocused = focusedQuestionId === question.id
                      const isExpandedInCompact = expandedCompactAnswers[question.id] === true
                      const linkCopied = copiedQuestionId === question.id
                      const answerText = compactMode && !isExpandedInCompact
                        ? truncateAnswer(question.answer)
                        : question.answer

                      return (
                        <article key={question.id} id={`qa-${question.id}`} className={`qa-card-scene${isFocused ? ' qa-card-scene--focused' : ''}${question.pinned ? ' qa-card-scene--pinned' : ''}`}>
                          <div className="qa-question-header">
                            <span className="qa-face-label">Q</span>
                            <span className="qa-question-main">
                              <span className="qa-question-text">
                                {question.pinned && (
                                  <span className="qa-pinned-badge" aria-label="Pinned question">📌 </span>
                                )}
                                {renderHighlightedText(question.question, normalizedSearch)}
                              </span>
                              <span className="qa-question-meta">
                                <span className="qa-topic-pill">{question._topic}</span>
                                {formatDate(question.answeredAt ?? question.submittedAt) && (
                                  <span>{formatDate(question.answeredAt ?? question.submittedAt)}</span>
                                )}
                                {question._helpful > 0 && <span>{question._helpful} helpful</span>}
                                {(question.upvotes ?? 0) > 0 && (
                                  <span>👍 {question.upvotes}</span>
                                )}
                              </span>
                            </span>
                          </div>

                          <div className="qa-card-actions">
                            <button
                              type="button"
                              aria-label={votedIds.has(question.id) ? 'Already upvoted' : 'Upvote this question'}
                              className={`qa-upvote-btn${votedIds.has(question.id) ? ' qa-upvote-btn--voted' : ''}`}
                              onClick={() => handleUpvote(question.id)}
                              disabled={votedIds.has(question.id)}
                            >
                              <svg viewBox="0 0 16 16" aria-hidden="true" fill="currentColor" width="13" height="13"><path d="M8 2L2 9h4v5h4V9h4z"/></svg>
                              <span>{votedIds.has(question.id) ? 'Upvoted' : 'Upvote'}{(question.upvotes ?? 0) > 0 ? ` · ${question.upvotes}` : ''}</span>
                            </button>
                            <button
                              type="button"
                              aria-label="Share on X"
                              className="qa-social-btn qa-social-btn--x"
                              onClick={() => shareImageTo(question, 'x')}
                            >
                              <svg viewBox="0 0 24 24" aria-hidden="true" fill="currentColor" width="14" height="14"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.746l7.73-8.835L1.254 2.25H8.08l4.253 5.622zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
                              <span>Share</span>
                            </button>
                            <button
                              type="button"
                              aria-label="Share on Facebook"
                              className="qa-social-btn qa-social-btn--fb"
                              onClick={() => shareImageTo(question, 'facebook')}
                            >
                              <svg viewBox="0 0 24 24" aria-hidden="true" fill="currentColor" width="14" height="14"><path d="M24 12.073C24 5.404 18.627 0 12 0S0 5.404 0 12.073C0 18.1 4.388 23.094 10.125 24v-8.437H7.078v-3.49h3.047V9.41c0-3.025 1.792-4.697 4.533-4.697 1.312 0 2.686.236 2.686.236v2.97h-1.513c-1.491 0-1.956.93-1.956 1.884v2.25h3.328l-.532 3.49h-2.796V24C19.612 23.094 24 18.1 24 12.073z"/></svg>
                              <span>Share</span>
                            </button>
                            <button type="button" className="qa-share-btn" onClick={() => shareQuestion(question.id)}>
                              {linkCopied ? '✓ Copied' : 'Copy link'}
                            </button>
                            <button type="button" className="qa-share-btn" onClick={() => saveAsImage(question)}>
                              Save image
                            </button>
                          </div>
                          {linkCopied && (
                            <p className="qa-share-feedback" role="status" aria-live="polite">
                              Direct link copied to clipboard.
                            </p>
                          )}

                          <div id={`qa-answer-${question.id}`} className="qa-card-face qa-card-back">
                            <span className="qa-face-label">A</span>
                            <div className="qa-answer-text">
                              {renderTextWithLinks(answerText, normalizedSearch)}
                              {compactMode && question.answer.length > 260 && (
                                <button
                                  type="button"
                                  className="qa-read-more-btn"
                                  onClick={() => toggleCompactAnswer(question.id)}
                                >
                                  {isExpandedInCompact ? 'Show less' : 'Read full answer'}
                                </button>
                              )}
                            </div>
                            {relatedQuestions.length > 0 && (
                              <div className="qa-related-wrap">
                                <p className="qa-related-label">Related questions</p>
                                <div className="qa-related-list">
                                  {relatedQuestions.map(related => (
                                    <button
                                      key={related.id}
                                      type="button"
                                      className="qa-related-btn"
                                      onClick={() => {
                                        incrementEngagement(question.id, 'related')
                                        setSelectedTopic(related._topic)
                                        setSelectedTag(null)
                                        setSearchQuery('')
                                        setFocusedQuestionId(related.id)
                                        window.history.replaceState(null, '', `/questions#qa-${encodeURIComponent(related.id)}`)
                                        window.setTimeout(() => {
                                          const element = document.getElementById(`qa-${related.id}`)
                                          element?.scrollIntoView({ behavior: 'smooth', block: 'center' })
                                        }, 30)
                                      }}
                                    >
                                      {related.question}
                                    </button>
                                  ))}
                                </div>
                              </div>
                            )}
                            <p className="qa-answer-byline">Answered by Nate</p>
                          </div>
                        </article>
                      )
                    })}

                    {totalPages > 1 && (
                      <div className="qa-pagination">
                        <button
                          type="button"
                          className="qa-page-btn"
                          onClick={() => goToPage(Math.max(0, safePage - 1))}
                          disabled={safePage === 0}
                          aria-label="Previous questions page"
                        >
                          Prev
                        </button>
                        <span className="qa-page-info">Page {safePage + 1} / {totalPages}</span>
                        <button
                          type="button"
                          className="qa-page-btn"
                          onClick={() => goToPage(Math.min(totalPages - 1, safePage + 1))}
                          disabled={safePage >= totalPages - 1}
                          aria-label="Next questions page"
                        >
                          Next
                        </button>
                      </div>
                    )}
                  </div>
                </div>
              </>
            )}
          </>
        )}
      </div>
    </section>
  )
}
