<script>
  /**
   * /study/topics/[id]/quiz-sessions/[sid] — 문제풀이 결과 (PR-10 신규).
   *
   * 한 세션 = 한 결과 카드. 정답/틀린/모르겠음 3 탭.
   *   - 정답: 본문/사용자 선택 확인만
   *   - 틀린: PR-3 AI 해설 (RAG)
   *   - 모르겠음: 분야(subject+scope) 설명 (PR-9 캐시)
   * 카드별 [학습완료] 토글 (PATCH /study-question-attempts/{aid}/review-mark) — wrong/unsure 만.
   * 헤더 "미확인 N건" → 0이면 "✓ 모두 확인".
   */
  import { onMount } from 'svelte';
  import { page } from '$app/stores';
  import { api } from '$lib/api';
  import { addToast } from '$lib/stores/toast';
  import {
    ArrowLeft, CheckCircle2, XCircle, HelpCircle, Sparkles, BookOpen, AlertCircle, Square, CheckSquare, Flag,
  } from 'lucide-svelte';
  import { renderMathMarkdown, renderMathMarkdownInline } from '$lib/utils/mathMarkdown';
  import Button from '$lib/components/ui/Button.svelte';
  import Card from '$lib/components/ui/Card.svelte';
  import Skeleton from '$lib/components/ui/Skeleton.svelte';
  import ImgAuth from '$lib/components/ImgAuth.svelte';

  let topicId = $derived(Number($page.params.id));
  let sessionId = $derived(Number($page.params.sid));
  let topicName = $state('');

  let detail = $state(null); // {summary, questions, attempts}
  let loading = $state(true);

  // 탭 + per-card state
  let activeTab = $state('wrong'); // 'correct' | 'wrong' | 'unsure'
  let perCard = $state({}); // { [qid]: { open, kind, loading, error, data } }

  // PR-12-A: 카드별 round_count 배지 (틀린/모르겠음 헤더에 표시).
  let relatedCounts = $state({}); // { [qid]: { repeat_round_count, similar_round_count, ... } }

  // 문제 신고(이상 태깅): qid -> true. 검수함(/study/questions-review)에 모임.
  let flagged = $state({});
  let flagBusy = $state({});
  async function flagQuestion(qid) {
    if (flagBusy[qid]) return;
    const next = !flagged[qid];
    flagBusy = { ...flagBusy, [qid]: true };
    try {
      const res = await api(`/study-questions/${qid}`, { method: 'PATCH', body: JSON.stringify({ needs_review: next }) });
      flagged = { ...flagged, [qid]: res?.needs_review ?? next };
      addToast(next ? 'success' : 'info', next ? '이상 문제로 신고했어요 — 검수함에 모았습니다' : '신고를 해제했어요');
    } catch (err) {
      addToast('error', err?.detail || '신고 처리 실패');
    } finally {
      flagBusy = { ...flagBusy, [qid]: false };
    }
  }

  async function loadTopic() {
    try {
      const t = await api(`/study-topics/${topicId}`);
      topicName = t?.topic?.name ?? '';
    } catch {}
  }

  async function load() {
    loading = true;
    try {
      detail = await api(`/study-topics/${topicId}/quiz-sessions/${sessionId}`);
      // 최초 탭: 틀린 → 모르겠음 → 정답 (학습 우선순위 순)
      if ((detail.summary.wrong_count ?? 0) > 0) activeTab = 'wrong';
      else if ((detail.summary.unsure_count ?? 0) > 0) activeTab = 'unsure';
      else activeTab = 'correct';
      // 신고 상태의 영속 source 는 신고함 큐(/study/questions-review) — 세션 결과 payload 엔
      // needs_review 가 없으므로 여기선 세션 내 optimistic 표시만. 새로고침 시 초기화됨.
      // PR-12-A: 카드별 반복 출제/유사 유형 배지 — 1회 bulk 호출.
      void loadRelatedCounts();
    } catch (err) {
      addToast('error', err?.detail || '결과 로드 실패');
      detail = null;
    } finally {
      loading = false;
    }
  }

  async function loadRelatedCounts() {
    if (!detail?.attempts?.length) return;
    const ids = detail.attempts.map((a) => a.question_id);
    try {
      const res = await api(`/study-topics/${topicId}/related-types-bulk`, {
        method: 'POST',
        body: JSON.stringify({ question_ids: ids }),
      });
      relatedCounts = res.items ?? {};
    } catch {
      relatedCounts = {};
    }
  }

  onMount(async () => {
    await loadTopic();
    await load();
  });

  /** 같은 question 의 attempt 와 question 객체를 묶어서 카테고리별로 분류. */
  let categorized = $derived.by(() => {
    if (!detail) return { correct: [], wrong: [], unsure: [] };
    const qById = Object.fromEntries((detail.questions ?? []).map((q) => [q.id, q]));
    const acc = { correct: [], wrong: [], unsure: [] };
    for (const a of detail.attempts ?? []) {
      const q = qById[a.question_id];
      if (!q) continue;
      const item = { attempt: a, q };
      if (a.outcome === 'correct') acc.correct.push(item);
      else if (a.outcome === 'wrong') acc.wrong.push(item);
      else if (a.outcome === 'unsure') acc.unsure.push(item);
    }
    return acc;
  });

  let summary = $derived(detail?.summary);
  let unreviewedCount = $derived(summary?.unreviewed_wrong_unsure_count ?? 0);

  // Phase 4-B v1: 세션 단위 분석 카드 상태
  let aiAnalysis = $derived(detail?.ai_session_analysis ?? null);
  let regeneratingSummary = $state(false);

  async function regenerateSummary() {
    if (regeneratingSummary) return;
    regeneratingSummary = true;
    try {
      const res = await api(`/study-topics/${topicId}/quiz-sessions/${sessionId}/regenerate-summary`, {
        method: 'POST',
      });
      if (!res.enqueued) {
        const msg = ({
          insufficient_attempts: '오답·모르겠음이 5건 미만이라 분석을 생성하지 않습니다',
          already_active: '이미 분석을 생성하고 있습니다. 잠시 후 다시 확인하세요',
          not_done: '세션이 완료되지 않았습니다',
          not_found: '세션을 찾을 수 없습니다',
          race_lost: '다른 호출이 먼저 enqueue 했습니다. 잠시 후 다시 확인하세요',
        }[res.reason] ?? '재생성을 시작할 수 없습니다');
        addToast('warning', msg);
      } else {
        addToast('success', '분석을 다시 생성합니다 (1분 주기 처리)');
        // detail 의 ai_session_analysis 갱신을 위해 가벼운 재로드
        await load();
      }
    } catch (err) {
      addToast('error', err?.detail || '재생성 호출 실패');
    } finally {
      regeneratingSummary = false;
    }
  }

  // Phase 4-A: AI 풀이 캐시 진척 — wrong/unsure attempts 와 question.ai_explanation_status 결합.
  let aiExplProgress = $derived.by(() => {
    if (!detail) return null;
    const qById = Object.fromEntries((detail.questions ?? []).map((q) => [q.id, q]));
    let target = 0, ready = 0, pending = 0, failed = 0, skipped = 0;
    for (const a of detail.attempts ?? []) {
      if (a.outcome !== 'wrong' && a.outcome !== 'unsure') continue;
      target += 1;
      const st = qById[a.question_id]?.ai_explanation_status;
      if (st === 'ready') ready += 1;
      else if (st === 'pending') pending += 1;
      else if (st === 'failed') failed += 1;
      else if (st === 'skipped') skipped += 1;
      // 'none' / 'stale' / undefined 는 카운트 외 (대기 또는 비대상)
    }
    return { target, ready, pending, failed, skipped };
  });

  function setCard(qid, patch) {
    perCard = { ...perCard, [qid]: { ...(perCard[qid] ?? {}), ...patch } };
  }
  function toggleCardOpen(qid) {
    const cur = perCard[qid];
    setCard(qid, { open: !(cur?.open) });
  }

  async function loadAiExplanation(qid, regenerate = false) {
    setCard(qid, { kind: 'ai-expl', loading: true, error: null });
    try {
      const res = await api(`/study-questions/${qid}/ai-explanation`, {
        method: 'POST',
        body: JSON.stringify({ regenerate }),
      });
      setCard(qid, { data: res, error: res.ai_explanation_status === 'failed' ? '풀이 생성 실패. 다시 시도하세요.' : null });
    } catch (err) {
      const msg = err?.status === 409
        ? '다른 호출이 풀이를 생성 중입니다. 잠시 후 다시 시도해주세요.'
        : (err?.detail || 'AI 풀이 생성 실패');
      setCard(qid, { error: msg });
    } finally {
      setCard(qid, { loading: false });
    }
  }

  async function loadSubjectNote(qid, regenerate = false) {
    const item = [...categorized.correct, ...categorized.wrong, ...categorized.unsure].find((it) => it.q.id === qid);
    if (!item) return;
    const subject = item.q.subject || '';
    const scope = item.q.scope || '';
    if (!subject) {
      setCard(qid, { kind: 'subject-note', error: '문제에 과목(subject)이 지정되지 않아 분야 설명을 만들 수 없습니다.' });
      return;
    }
    setCard(qid, { kind: 'subject-note', loading: true, error: null });
    try {
      const res = await api(`/study-topics/${topicId}/subject-notes/generate`, {
        method: 'POST',
        body: JSON.stringify({ subject, scope, regenerate }),
      });
      setCard(qid, { data: res, error: res.status === 'failed' ? '분야 설명 생성 실패. 다시 시도하세요.' : null });
    } catch (err) {
      const msg = err?.status === 409
        ? '다른 호출이 분야 설명을 생성 중입니다. 잠시 후 다시 시도해주세요.'
        : (err?.detail || '분야 설명 생성 실패');
      setCard(qid, { error: msg });
    } finally {
      setCard(qid, { loading: false });
    }
  }

  /** 학습완료 토글. PATCH 후 detail.attempts + summary.unreviewed_count 갱신. */
  async function toggleReviewed(attempt) {
    const next = !attempt.reviewed_at;
    try {
      const res = await api(`/study-question-attempts/${attempt.attempt_id}/review-mark`, {
        method: 'PATCH',
        body: JSON.stringify({ reviewed: next }),
      });
      // 로컬 상태 갱신 (서버 round-trip 없이)
      attempt.reviewed_at = res.reviewed_at;
      // unreviewed_count 재계산
      if (detail && detail.summary) {
        const remaining = (detail.attempts ?? []).filter(
          (a) => (a.outcome === 'wrong' || a.outcome === 'unsure') && !a.reviewed_at
        ).length;
        detail.summary = { ...detail.summary, unreviewed_wrong_unsure_count: remaining };
      }
      // perCard 객체 reactivity 트리거 (attempt mutate 만으로는 안 잡힘)
      perCard = { ...perCard };
    } catch (err) {
      addToast('error', err?.detail || '체크 실패');
    }
  }

  // PR-12-A 후속: 반복 출제 등급 → 라벨 매핑.
  function repeatBadge(grade, roundCount) {
    if (grade === '단골') return { emoji: '🌟', text: '단골' };
    if (grade === '잘 나오는 반복 출제') return { emoji: '🔥', text: '잘 나오는 반복 출제' };
    if (grade === '반복 출제') return { emoji: '🔥', text: '반복 출제' };
    if (grade === '신출') return { emoji: '🆕', text: '신출' };
    if (grade === '빈출') return { emoji: '📜', text: '빈출' };
    if (roundCount >= 2) return { emoji: '🔥', text: '반복 출제' };
    return null;
  }

  function shortText(s, n = 80) {
    if (!s) return '';
    const t = s.replace(/\s+/g, ' ').trim();
    return t.length > n ? t.slice(0, n) + '…' : t;
  }
  function fmtDateTime(s) {
    if (!s) return '';
    return new Date(s).toLocaleString('ko-KR', { dateStyle: 'short', timeStyle: 'short' });
  }
</script>

<svelte:head><title>결과 — {topicName || '주제'}</title></svelte:head>

<div class="p-4 md:p-6 max-w-3xl mx-auto">
  <div class="flex items-center gap-2 text-xs md:text-sm mb-3">
    <a href="/study" class="text-dim hover:text-text">공부</a>
    <span class="text-faint">/</span>
    <a href="/study/topics" class="text-dim hover:text-text">주제</a>
    <span class="text-faint">/</span>
    <a href={`/study/topics/${topicId}`} class="text-dim hover:text-text truncate">{topicName || '...'}</a>
    <span class="text-faint">/</span>
    <span class="text-text font-medium">결과 #{sessionId}</span>
  </div>

  {#if loading}
    <Skeleton h="h-32" rounded="lg" />
  {:else if !detail}
    <Card>
      {#snippet children()}
        <div class="p-6 text-center text-dim">결과를 불러올 수 없습니다.</div>
      {/snippet}
    </Card>
  {:else}
    {@const s = summary}
    {@const accuracy = s.total > 0 ? Math.round((s.correct_count / s.total) * 100) : 0}
    <Card>
      {#snippet children()}
        <div class="p-5 flex flex-col gap-4">
          <div class="flex items-center justify-between flex-wrap gap-2">
            <h1 class="text-base font-semibold text-text">결과</h1>
            <div class="text-xs text-dim flex items-center gap-2">
              {#if unreviewedCount > 0}
                <span class="text-warning bg-warning/10 rounded px-2 py-0.5">미확인 {unreviewedCount}건</span>
              {:else}
                <span class="text-success bg-success/10 rounded px-2 py-0.5">✓ 모두 확인</span>
              {/if}
              <span>완료 {fmtDateTime(s.finished_at)}</span>
            </div>
          </div>
          <div class="text-sm text-dim">
            총 {s.total}문제 ·
            <span class="text-success">정답 {s.correct_count}</span> ·
            <span class="text-error">오답 {s.wrong_count}</span> ·
            <span class="text-warning">모르겠음 {s.unsure_count}</span> ·
            <span class="text-text font-medium">정답률 {accuracy}%</span>
          </div>

          <!-- Phase 2-B: 변화 카운트 (세션 종료 시점 스냅샷) — 값이 있을 때만 -->
          {#if (s.newly_correct_count ?? 0) + (s.relapsed_count ?? 0) + (s.recovered_count ?? 0) > 0}
            <div class="text-[11px] text-dim flex flex-wrap gap-x-3 gap-y-1">
              {#if s.newly_correct_count > 0}
                <span><span class="text-success">🆕 새로 맞힘</span> {s.newly_correct_count}</span>
              {/if}
              {#if s.recovered_count > 0}
                <span><span class="text-success">↗ 회복</span> {s.recovered_count}</span>
              {/if}
              {#if s.relapsed_count > 0}
                <span><span class="text-error">↘ 다시 틀림</span> {s.relapsed_count}</span>
              {/if}
              {#if s.chronic_remaining_count > 0}
                <span><span class="text-warning">⚠ 반복 오답 누적</span> {s.chronic_remaining_count}</span>
              {/if}
            </div>
          {/if}

          {#if Object.keys(s.subject_distribution || {}).length > 0}
            <div class="text-[11px] text-dim">
              과목 분포:
              {#each Object.entries(s.subject_distribution) as [sub, n], i}
                <span>{i > 0 ? ' · ' : ''}{sub || '미분류'} {n}</span>
              {/each}
            </div>
          {/if}

          <!-- Phase 4-A: AI 풀이 캐시 진척 indicator (오답·모르겠음 대상) -->
          {#if aiExplProgress && aiExplProgress.target > 0}
            {@const p = aiExplProgress}
            {@const pct = Math.round((p.ready / p.target) * 100)}
            <div class="text-[11px] text-dim flex items-center gap-2 flex-wrap">
              <Sparkles size={11} class="text-accent" />
              <span>
                AI 풀이 자동 생성:
                <span class="text-text font-medium">{p.ready}/{p.target}</span>
                <span class="text-faint">({pct}%)</span>
              </span>
              {#if p.pending > 0}
                <span class="text-warning">· 생성 중 {p.pending}</span>
              {/if}
              {#if p.failed > 0}
                <span class="text-error">· 실패 {p.failed}</span>
              {/if}
              {#if p.skipped > 0}
                <span class="text-dim">· 자료 부족 {p.skipped}</span>
              {/if}
              {#if p.ready < p.target && p.pending === 0 && p.failed === 0 && p.skipped === 0}
                <span class="text-faint">· 대기열 처리 대기</span>
              {/if}
            </div>
          {/if}

          <!-- Phase 4-B v1: 세션 단위 자유 마크다운 분석 -->
          {#if aiAnalysis}
            <div class="rounded border border-accent/30 bg-accent/5 p-3 text-xs text-text flex flex-col gap-2">
              <div class="flex items-center gap-2 flex-wrap">
                <Sparkles size={12} class="text-accent" />
                <span class="font-medium">세션 요약</span>
                {#if aiAnalysis.confidence === 'low'}
                  <span class="text-[10px] text-warning bg-warning/10 rounded px-1.5 py-0.5">신뢰도 낮음</span>
                {/if}
                {#if aiAnalysis.is_stale}
                  <span class="text-[10px] text-dim">재생성 중</span>
                {/if}
                <button
                  type="button"
                  class="ml-auto text-[10px] text-dim hover:text-text disabled:opacity-50"
                  disabled={regeneratingSummary}
                  onclick={regenerateSummary}
                >
                  {regeneratingSummary ? '...' : '재생성'}
                </button>
              </div>
              {#if aiAnalysis.summary_md}
                <div class="markdown-body math-area leading-relaxed">{@html renderMathMarkdown(aiAnalysis.summary_md)}</div>
              {:else if aiAnalysis.job_status === 'pending' || aiAnalysis.job_status === 'processing'}
                <div class="text-dim">AI 가 세션 분석 중입니다 (1분 주기 처리)</div>
              {:else if aiAnalysis.job_error_code === 'insufficient_attempts'}
                <div class="text-dim">오답·모르겠음이 5건 미만이라 분석을 생성하지 않습니다.</div>
              {:else if aiAnalysis.job_error_code === 'evidence_missing'}
                <div class="text-dim">관련 자료가 부족해 분석을 건너뛰었습니다.</div>
              {:else if aiAnalysis.job_error_code === 'guard_fail'}
                <div class="text-dim">분석 결과가 환각 검증에서 차단됐습니다. <button type="button" class="text-accent hover:underline" onclick={regenerateSummary}>재생성</button></div>
              {:else}
                <div class="text-dim">분석 대기 중입니다.</div>
              {/if}
            </div>
          {/if}

          <!-- Phase 2-B: 바로 할 일 (지금 시점 progress 기반 동적 카운트) — 클릭 시 복습함 해당 탭 -->
          {#if (s.pending_review_count ?? 0) + (s.chronic_count ?? 0) + (s.regressed_count ?? 0) > 0}
            <div class="rounded border border-warning/30 bg-warning/5 p-3 text-xs text-text flex flex-col gap-1.5">
              <div class="font-medium">바로 할 일</div>
              <ul class="flex flex-col gap-0.5">
                {#if s.pending_review_count > 0}
                  <li>
                    <a class="text-error hover:underline" href={`/study/topics/${topicId}/review-queue?tab=pending_review`}>
                      미확인 오답·모르겠음 {s.pending_review_count}건
                    </a>
                  </li>
                {/if}
                {#if s.chronic_count > 0}
                  <li>
                    <a class="text-warning hover:underline" href={`/study/topics/${topicId}/review-queue?tab=chronic`}>
                      반복 오답 {s.chronic_count}건
                    </a>
                  </li>
                {/if}
                {#if s.regressed_count > 0}
                  <li>
                    <a class="text-warning hover:underline" href={`/study/topics/${topicId}/review-queue?tab=regressed`}>
                      퇴행 {s.regressed_count}건
                    </a>
                  </li>
                {/if}
              </ul>
            </div>
          {/if}

          <!-- 탭 -->
          <div class="flex border-b border-default">
            <button
              type="button"
              onclick={() => (activeTab = 'wrong')}
              class="px-4 py-2 text-sm border-b-2 transition-colors flex items-center gap-2
                {activeTab === 'wrong' ? 'border-error text-error font-medium' : 'border-transparent text-dim hover:text-text'}"
            >
              <XCircle size={14} />
              <span>틀린</span>
              <span class="text-[10px] bg-error/10 text-error rounded px-1.5 py-0.5">{s.wrong_count}</span>
            </button>
            <button
              type="button"
              onclick={() => (activeTab = 'unsure')}
              class="px-4 py-2 text-sm border-b-2 transition-colors flex items-center gap-2
                {activeTab === 'unsure' ? 'border-warning text-warning font-medium' : 'border-transparent text-dim hover:text-text'}"
            >
              <HelpCircle size={14} />
              <span>모르겠음</span>
              <span class="text-[10px] bg-warning/10 text-warning rounded px-1.5 py-0.5">{s.unsure_count}</span>
            </button>
            <button
              type="button"
              onclick={() => (activeTab = 'correct')}
              class="px-4 py-2 text-sm border-b-2 transition-colors flex items-center gap-2
                {activeTab === 'correct' ? 'border-success text-success font-medium' : 'border-transparent text-dim hover:text-text'}"
            >
              <CheckCircle2 size={14} />
              <span>정답</span>
              <span class="text-[10px] bg-success/10 text-success rounded px-1.5 py-0.5">{s.correct_count}</span>
            </button>
          </div>

          {#if activeTab === 'correct'}
            {@render resultList(categorized.correct, 'correct')}
          {:else if activeTab === 'wrong'}
            {@render resultList(categorized.wrong, 'wrong')}
          {:else if activeTab === 'unsure'}
            {@render resultList(categorized.unsure, 'unsure')}
          {/if}

          <div class="flex gap-2 justify-end pt-3 border-t border-default">
            <Button href={`/study/topics/${topicId}/review-queue`} variant="ghost">복습함</Button>
            <Button href={`/study/topics/${topicId}`} variant="ghost" icon={ArrowLeft}>주제로</Button>
          </div>
        </div>
      {/snippet}
    </Card>
  {/if}
</div>

{#snippet resultList(items, kind)}
  {#if items.length === 0}
    <div class="text-xs text-dim p-4 text-center border border-dashed border-default rounded">
      {#if kind === 'correct'}정답 처리된 문제가 없습니다.
      {:else if kind === 'wrong'}오답 처리된 문제가 없습니다.
      {:else}모르겠음 처리된 문제가 없습니다.
      {/if}
    </div>
  {:else}
    <ul class="flex flex-col gap-2">
      {#each items as it (it.q.id)}
        {@const cardState = perCard[it.q.id] ?? {}}
        {@const isOpen = cardState.open === true}
        {@const reviewed = !!it.attempt.reviewed_at}
        {@const rc = relatedCounts[it.q.id] ?? null}
        {@const repeatBdg = repeatBadge(rc?.repeat_grade, rc?.repeat_round_count ?? 0)}
        <li class="rounded border bg-surface overflow-hidden
          {kind !== 'correct' && reviewed ? 'border-success/30 bg-success/5' : 'border-default'}">
          <button
            type="button"
            onclick={() => toggleCardOpen(it.q.id)}
            class="w-full text-left p-3 flex items-start gap-3 hover:bg-bg/30 transition-colors"
          >
            <div class="flex-1 min-w-0">
              <div class="text-sm text-text">{shortText(it.q.question_text, 100)}</div>
              <div class="text-[11px] text-dim mt-1 flex items-center gap-2 flex-wrap">
                {#if it.q.subject}<span>{it.q.subject}</span>{/if}
                {#if it.q.scope}<span>· {it.q.scope}</span>{/if}
                {#if it.q.exam_round}<span>· {it.q.exam_round}</span>{/if}
                <!-- PR-12-A: 반복 출제 / 유사 유형 배지 — round_count >= 2 일 때만 -->
                {#if repeatBdg && rc?.repeat_round_count >= 2}
                  <span class="text-error/90">{repeatBdg.emoji} {repeatBdg.text} · {rc.repeat_round_count}개 회차</span>
                {/if}
                {#if rc?.similar_round_count >= 2}
                  <span class="text-accent/80">🧩 유사 유형 · {rc.similar_round_count}개 회차</span>
                {/if}
                <span class="ml-auto">
                  {#if kind === 'correct'}
                    <span class="text-success">선택 {it.attempt.selected_choice}번 = 정답</span>
                  {:else if kind === 'wrong'}
                    <span class="text-error">선택 {it.attempt.selected_choice ?? '?'}번</span>
                    <span class="text-dim"> · 정답 {it.attempt.correct_choice}번</span>
                  {:else}
                    <span class="text-warning">모르겠음</span>
                    <span class="text-dim"> · 정답 {it.attempt.correct_choice}번</span>
                  {/if}
                  {#if kind !== 'correct' && reviewed}
                    <CheckCircle2 size={12} class="inline ml-1 text-success" />
                  {/if}
                </span>
              </div>
            </div>
          </button>

          {#if isOpen}
            <div class="border-t border-default p-3 flex flex-col gap-3 bg-bg/20">
              <div class="text-sm text-text leading-relaxed markdown-body math-area">{@html renderMathMarkdown(it.q.question_text)}</div>

              {#if it.q.images?.length > 0}
                <div class="grid grid-cols-1 sm:grid-cols-2 gap-2">
                  {#each it.q.images as img (img.id)}
                    <ImgAuth src={img.served_url} class="w-full max-h-60 object-contain rounded border border-default bg-bg/30" />
                  {/each}
                </div>
              {/if}

              <div class="flex flex-col gap-1.5">
                {#each it.q.choices as ch (ch.number)}
                  {@const isCorrect = it.attempt.correct_choice === ch.number}
                  {@const isSelected = it.attempt.selected_choice === ch.number}
                  <div class="p-2.5 rounded border flex items-start gap-3 text-sm
                    {isCorrect ? 'border-success bg-success/10 text-text'
                     : (isSelected && !isCorrect) ? 'border-error bg-error/10 text-text'
                     : 'border-default bg-surface text-dim'}"
                  >
                    <span class="font-semibold w-5 shrink-0">{ch.number}</span>
                    <span class="flex-1 math-area overflow-x-auto">{@html renderMathMarkdownInline(ch.text)}</span>
                    {#if isCorrect}<CheckCircle2 size={14} class="text-success shrink-0" />
                    {:else if isSelected && !isCorrect}<XCircle size={14} class="text-error shrink-0" />
                    {/if}
                  </div>
                {/each}
              </div>

              {#if it.q.explanation}
                <div class="rounded border border-default bg-surface p-3 text-xs text-text">
                  <div class="text-[10px] text-dim mb-1">사용자 입력 해설</div>
                  <div class="markdown-body math-area">{@html renderMathMarkdown(it.q.explanation)}</div>
                </div>
              {/if}

              {#if kind === 'wrong'}
                {@render aiExplanationBlock(it.q.id, cardState)}
              {:else if kind === 'unsure'}
                {@render subjectNoteBlock(it, cardState)}
              {/if}

              <div class="flex items-center justify-between gap-2 pt-2 border-t border-default">
                <button
                  type="button"
                  onclick={() => flagQuestion(it.q.id)}
                  disabled={flagBusy[it.q.id]}
                  class="flex items-center gap-1.5 text-xs px-2.5 py-1.5 rounded border transition-colors disabled:opacity-50
                    {flagged[it.q.id]
                      ? 'border-warning bg-warning/10 text-warning'
                      : 'border-default text-dim hover:text-warning hover:border-warning/40'}"
                  title="문제나 정답이 이상하면 신고해 검수함에 모아둡니다"
                >
                  <Flag size={13} />
                  <span>{flagged[it.q.id] ? '신고됨' : '이 문제 이상해요'}</span>
                </button>
                {#if kind !== 'correct'}
                  <button
                    type="button"
                    onclick={() => toggleReviewed(it.attempt)}
                    class="flex items-center gap-2 text-xs px-3 py-1.5 rounded border transition-colors
                      {reviewed
                        ? 'border-success bg-success/10 text-success hover:bg-success/15'
                        : 'border-default text-dim hover:text-text hover:border-warning/40'}"
                  >
                    {#if reviewed}<CheckSquare size={14} />{:else}<Square size={14} />{/if}
                    <span>{reviewed ? '학습완료' : '학습완료로 표시'}</span>
                  </button>
                {/if}
              </div>
            </div>
          {/if}
        </li>
      {/each}
    </ul>
  {/if}
{/snippet}

{#snippet aiExplanationBlock(qid, st)}
  {@const data = st.data}
  {@const isLoaded = !!data?.ai_explanation}
  <div class="rounded border border-default bg-bg/30 p-3 flex flex-col gap-2">
    <div class="flex items-center gap-2 flex-wrap">
      <Sparkles size={14} class="text-accent" />
      <span class="text-xs font-semibold text-text">AI 풀이</span>
      {#if data?.is_stale}
        <span class="text-[10px] text-warning border border-warning/40 rounded px-1.5 py-0.5">stale</span>
      {/if}
      {#if data?.from_cache && !data?.is_stale}
        <span class="text-[10px] text-dim">캐시</span>
      {/if}
      <span class="ml-auto flex items-center gap-1.5">
        {#if !isLoaded}
          <Button size="sm" variant="ghost" onclick={() => loadAiExplanation(qid, false)} loading={st.loading}>AI 해설 보기</Button>
        {:else if data?.is_stale}
          <Button size="sm" onclick={() => loadAiExplanation(qid, true)} loading={st.loading}>다시 생성</Button>
        {:else if data?.ai_explanation_status === 'failed'}
          <Button size="sm" onclick={() => loadAiExplanation(qid, true)} loading={st.loading}>다시 시도</Button>
        {:else}
          <Button size="sm" variant="ghost" onclick={() => loadAiExplanation(qid, true)} loading={st.loading}>다시 생성</Button>
        {/if}
      </span>
    </div>

    {#if data?.is_stale}
      <div class="text-[11px] text-warning bg-warning/5 border border-warning/30 rounded px-2 py-1.5 flex items-start gap-1.5">
        <AlertCircle size={12} class="mt-0.5 shrink-0" />
        <span>이 풀이는 정답·문제가 수정된 후의 이전 풀이입니다. "다시 생성" 으로 새로 만들 수 있습니다.</span>
      </div>
    {/if}
    {#if st.error}
      <div class="text-[11px] text-error">{st.error}</div>
    {/if}
    {#if isLoaded}
      <div class="text-xs text-text leading-relaxed markdown-body math-area">
        {@html renderMathMarkdown(data.ai_explanation)}
      </div>
      {#if data.evidence?.length}
        <details class="text-[10px] text-dim">
          <summary class="cursor-pointer hover:text-text">참고 근거 {data.evidence.length}건</summary>
          <ul class="mt-1 flex flex-col gap-1">
            {#each data.evidence as ev}
              <li>
                <span class="text-text">{ev.source_type === 'document' ? '📄' : '❓'} {ev.title}</span>
                <div class="pl-4 truncate">{ev.snippet}</div>
              </li>
            {/each}
          </ul>
        </details>
      {/if}
    {/if}
  </div>
{/snippet}

{#snippet subjectNoteBlock(it, st)}
  {@const data = st.data}
  {@const isLoaded = !!data?.content}
  <div class="rounded border border-default bg-bg/30 p-3 flex flex-col gap-2">
    <div class="flex items-center gap-2 flex-wrap">
      <BookOpen size={14} class="text-warning" />
      <span class="text-xs font-semibold text-text">분야 설명</span>
      <span class="text-[10px] text-dim">
        {it.q.subject || '(과목 미지정)'}{it.q.scope ? ` · ${it.q.scope}` : ''}
      </span>
      {#if data?.from_cache && data?.status === 'ready'}
        <span class="text-[10px] text-dim">캐시</span>
      {/if}
      <span class="ml-auto flex items-center gap-1.5">
        {#if !isLoaded}
          <Button size="sm" variant="ghost" onclick={() => loadSubjectNote(it.q.id, false)} loading={st.loading}>분야 설명 보기</Button>
        {:else if data?.status === 'failed'}
          <Button size="sm" onclick={() => loadSubjectNote(it.q.id, true)} loading={st.loading}>다시 시도</Button>
        {:else}
          <Button size="sm" variant="ghost" onclick={() => loadSubjectNote(it.q.id, true)} loading={st.loading}>다시 생성</Button>
        {/if}
      </span>
    </div>

    {#if st.error}
      <div class="text-[11px] text-error">{st.error}</div>
    {/if}
    {#if isLoaded}
      <div class="text-xs text-text leading-relaxed markdown-body math-area">
        {@html renderMathMarkdown(data.content)}
      </div>
      {#if data.evidence?.length}
        <details class="text-[10px] text-dim">
          <summary class="cursor-pointer hover:text-text">참고 근거 {data.evidence.length}건</summary>
          <ul class="mt-1 flex flex-col gap-1">
            {#each data.evidence as ev}
              <li>
                <span class="text-text">{ev.source_type === 'document' ? '📄' : '❓'} {ev.title}</span>
                <div class="pl-4 truncate">{ev.snippet}</div>
              </li>
            {/each}
          </ul>
        </details>
      {/if}
    {/if}
  </div>
{/snippet}
