/* app-admin-ocr.jsx — 관리자 전용 OCR 개선센터
   requires: firebase (전역), window.RX (rx-core.jsx), window.rxHelpers (rx-core.jsx),
             window.AuthContext (rx-auth.jsx)
   exports:  AppAdminOcr */

const VERDICT_LABELS = {
  owner_approved: '✅ 승인',
  owner_rejected: '❌ 반려',
  needs_more_samples: '🔄 샘플 더 필요',
  guideline_check_needed: '📋 가이드라인 확인 필요',
  db_check_needed: '🗄️ DB 확인 필요',
};

const STATUS_LABELS = {
  saved: '저장됨',
  ai_reviewed: 'AI 검토 완료',
  review_failed: 'AI 검토 실패',
  owner_decided: '판정 완료',
};

const SOURCE_LABELS = {
  admin: '관리자 테스트',
  user: '사용자 데이터',
};

function fmtTs(ts) {
  if (!ts) return '';
  const d = ts.seconds ? new Date(ts.seconds * 1000) : new Date(ts);
  return d.toLocaleDateString('ko-KR', { month: 'numeric', day: 'numeric' }) + ' ' +
    d.toLocaleTimeString('ko-KR', { hour: '2-digit', minute: '2-digit' });
}

function StatusBadge({ status }) {
  const colors = {
    saved: { bg: RX.shell, color: RX.sub },
    ai_reviewed: { bg: RX.tealTint, color: RX.tealDark },
    review_failed: { bg: RX.roseBg, color: RX.rose },
    owner_decided: { bg: RX.amberBg, color: RX.amber },
  };
  const c = colors[status] || { bg: RX.shell, color: RX.faint };
  return (
    <span style={{ fontSize: 10, fontWeight: 700, padding: '2px 6px', borderRadius: 4, background: c.bg, color: c.color }}>
      {STATUS_LABELS[status] || status}
    </span>
  );
}

function SourceBadge({ source }) {
  const isAdmin = source === 'admin';
  return (
    <span style={{ fontSize: 10, fontWeight: 700, padding: '2px 6px', borderRadius: 4, background: isAdmin ? 'rgba(156,59,84,0.08)' : RX.shell, color: isAdmin ? RX.rose : RX.faint }}>
      {SOURCE_LABELS[source] || source}
    </span>
  );
}

function SummaryCard({ label, value, accent }) {
  return (
    <div style={{ flex: 1, minWidth: 0, background: RX.paper, border: `1px solid ${RX.line}`, borderRadius: 10, padding: '10px 12px', textAlign: 'center' }}>
      <div style={{ fontSize: 20, fontWeight: 800, color: accent || RX.ink }}>{value}</div>
      <div style={{ fontSize: 10, color: RX.sub, marginTop: 2, lineHeight: 1.3 }}>{label}</div>
    </div>
  );
}

function sanitizeOcrCaseForAdmin(caseItem) {
  if (!caseItem || !window.rxHelpers || !window.rxHelpers.sanitizeOcrAnalysisForStorage) {
    return caseItem || {};
  }
  return { ...caseItem, ...window.rxHelpers.sanitizeOcrAnalysisForStorage(caseItem) };
}

function CaseListItem({ c, selected, onClick }) {
  const safeCase = sanitizeOcrCaseForAdmin(c);
  const rawLabel = (safeCase.rawDrugs || []).slice(0, 2).map(d => d.name || d.rawName || '').filter(Boolean).join(', ');
  return (
    <div
      onClick={onClick}
      style={{
        padding: '11px 14px', borderBottom: `1px solid ${RX.line}`,
        background: selected ? RX.tealTint : '#fff',
        cursor: 'pointer', transition: 'background .12s',
      }}
    >
      <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 4, flexWrap: 'wrap' }}>
        <SourceBadge source={c.source} />
        <StatusBadge status={c.reviewStatus} />
        {c.ownerVerdict && (
          <span style={{ fontSize: 10, color: RX.amber, fontWeight: 700 }}>{VERDICT_LABELS[c.ownerVerdict] || c.ownerVerdict}</span>
        )}
        <span style={{ fontSize: 10, color: RX.faint, marginLeft: 'auto' }}>{fmtTs(c.createdAt)}</span>
      </div>
      <div style={{ fontSize: 12, fontWeight: 700, color: RX.ink, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
        {rawLabel || '약물 정보 없음'}
      </div>
      {c.matchedCvDrugs && c.matchedCvDrugs.length > 0 && (
        <div style={{ fontSize: 11, color: RX.teal, marginTop: 2 }}>
          CV 매칭 {c.matchedCvDrugs.length}종
        </div>
      )}
    </div>
  );
}

function DrugTopCandidates({ drug }) {
  if (!drug.topCandidates || drug.topCandidates.length === 0) return null;
  return (
    <div style={{ marginTop: 4 }}>
      <div style={{ fontSize: 10, color: RX.faint, marginBottom: 2 }}>DB 후보 Top 3</div>
      {drug.topCandidates.slice(0, 3).map((c, i) => (
        <div key={i} style={{ fontSize: 11, color: RX.sub, paddingLeft: 8 }}>
          {i + 1}. {c.brand} ({c.generic}) — {typeof c.score === 'number' ? Math.round(c.score) + 'pt' : c.score}
        </div>
      ))}
    </div>
  );
}

const ASSESSMENT_LABELS = {
  correct: { text: '✓ 정확', color: RX.teal },
  wrong_disease: { text: '✗ 질환 오배정', color: RX.rose },
  wrong_role: { text: '✗ 역할 오류', color: RX.rose },
  missing: { text: '⚠ 누락', color: RX.amber },
  extra: { text: '⚠ 과잉 배정', color: RX.amber },
};

function AiReviewResult({ aiReview }) {
  if (!aiReview) return null;
  const sa = aiReview.scenarioAssessment || {};
  const cr = aiReview.clinical_review || {};
  const ocr = aiReview.ocr_correction_review || [];
  const fixes = aiReview.suggestedFixes || [];
  const warnings = aiReview.warnings || [];

  return (
    <div style={{ marginTop: 14 }}>
      <div style={{ fontSize: 13, fontWeight: 800, color: RX.tealDark, marginBottom: 8 }}>AI 검토 결과</div>

      {/* confidence */}
      <div style={{ fontSize: 11, color: RX.sub, marginBottom: 8 }}>
        신뢰도: <b style={{ color: aiReview.confidence === 'high' ? RX.teal : aiReview.confidence === 'low' ? RX.rose : RX.amber }}>{aiReview.confidence}</b>
      </div>

      {/* 처방 묶음 평가 — 핵심 */}
      {sa.summary && (
        <div style={{ background: sa.overallCorrect ? RX.tealTint : RX.roseBg, border: `1px solid ${sa.overallCorrect ? RX.tealLine : 'rgba(156,59,84,0.25)'}`, borderRadius: 8, padding: 10, marginBottom: 10 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 4 }}>
            <div style={{ fontSize: 11, fontWeight: 800, color: sa.overallCorrect ? RX.tealDark : RX.rose }}>
              {sa.overallCorrect ? '✓ 처방 묶음 해석 적절' : '✗ 처방 묶음 해석 문제 있음'}
            </div>
          </div>
          <div style={{ fontSize: 12, color: RX.ink, lineHeight: 1.5 }}>{sa.summary}</div>
        </div>
      )}

      {/* 시나리오별 약물 배정 평가 */}
      {sa.scenarios && sa.scenarios.length > 0 && (
        <div style={{ marginBottom: 12 }}>
          <div style={{ fontSize: 11, fontWeight: 800, color: RX.sub, marginBottom: 6 }}>질환 묶음별 약물 역할 평가</div>
          {sa.scenarios.map((sc, si) => (
            <div key={si} style={{ background: RX.paper, border: `1px solid ${sc.correct ? RX.line : 'rgba(156,59,84,0.25)'}`, borderRadius: 8, marginBottom: 8, overflow: 'hidden' }}>
              <div style={{ padding: '7px 11px', background: sc.correct ? RX.shell : RX.roseBg, borderBottom: `1px solid ${sc.correct ? RX.lineSoft : 'rgba(156,59,84,0.15)'}`, fontSize: 11, fontWeight: 800, color: sc.correct ? RX.sub : RX.rose }}>
                시나리오 {si + 1} {sc.correct ? '· 정확' : '· 오류 있음'}
              </div>
              <div style={{ padding: '8px 11px' }}>
                {(!sc.drugIssues || sc.drugIssues.length === 0)
                  ? <div style={{ fontSize: 11, color: RX.faint }}>모든 약물 배정 정확</div>
                  : sc.drugIssues.map((item, ii) => {
                    const badge = ASSESSMENT_LABELS[item.assessment] || { text: item.assessment, color: RX.faint };
                    return (
                      <div key={ii} style={{ paddingBottom: ii < sc.drugIssues.length - 1 ? 8 : 0, marginBottom: ii < sc.drugIssues.length - 1 ? 8 : 0, borderBottom: ii < sc.drugIssues.length - 1 ? `1px solid ${RX.lineSoft}` : 'none' }}>
                        <div style={{ display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
                          <span style={{ fontSize: 12, fontWeight: 700, color: RX.ink }}>{item.drug}</span>
                          <span style={{ fontSize: 10, fontWeight: 700, color: badge.color }}>{badge.text}</span>
                        </div>
                        <div style={{ fontSize: 11, color: RX.sub, marginTop: 2 }}>
                          엔진: {item.assignedDisease} · {item.assignedRole}
                        </div>
                        {item.suggestion && (
                          <div style={{ fontSize: 11, color: RX.amber, marginTop: 2 }}>제안: {item.suggestion}</div>
                        )}
                      </div>
                    );
                  })
                }
              </div>
            </div>
          ))}
        </div>
      )}

      {/* 올바른 최종 해석 */}
      {sa.correctedInterpretation && (sa.correctedInterpretation.diseaseGroups || []).length > 0 && (
        <div style={{ marginBottom: 12 }}>
          <div style={{ fontSize: 11, fontWeight: 800, color: RX.tealDark, marginBottom: 6 }}>
            ✓ 올바른 처방 해석
            <span style={{ fontSize: 10, color: RX.faint, fontWeight: 400, marginLeft: 6 }}>질환 + 약물 역할 완전판</span>
          </div>
          {sa.correctedInterpretation.diseaseGroups.map((g, gi) => (
            <div key={gi} style={{ background: RX.paper, border: `1px solid ${RX.tealLine}`, borderRadius: 8, marginBottom: 8, overflow: 'hidden' }}>
              <div style={{ padding: '7px 12px', background: RX.tealTint, fontSize: 12, fontWeight: 800, color: RX.tealDark }}>
                {g.disease}
              </div>
              <div style={{ padding: '8px 12px' }}>
                {(g.drugs || []).map((d, di) => (
                  <div key={di} style={{ display: 'flex', gap: 8, padding: '4px 0', borderBottom: di < g.drugs.length - 1 ? `1px solid ${RX.lineSoft}` : 'none' }}>
                    <span style={{ fontSize: 12, fontWeight: 700, color: RX.ink, minWidth: 0, flexShrink: 0 }}>{d.drug}</span>
                    <span style={{ fontSize: 11, color: RX.sub, lineHeight: 1.4 }}>{d.role}</span>
                  </div>
                ))}
              </div>
            </div>
          ))}
        </div>
      )}

      {/* better explanations */}
      {cr.betterExplanations && cr.betterExplanations.length > 0 && (
        <div style={{ marginBottom: 10 }}>
          <div style={{ fontSize: 11, fontWeight: 800, color: RX.amber, marginBottom: 4 }}>설명 개선 후보</div>
          {cr.betterExplanations.map((e, i) => (
            <div key={i} style={{ background: RX.amberBg, border: `1px solid rgba(154,106,20,0.2)`, borderRadius: 6, padding: '6px 10px', marginBottom: 4 }}>
              <div style={{ fontSize: 11, fontWeight: 700, color: RX.ink }}>{e.drug}</div>
              <div style={{ fontSize: 11, color: RX.sub, marginTop: 2 }}>현재: {e.currentExplanation}</div>
              <div style={{ fontSize: 11, color: RX.amber, marginTop: 2 }}>개선안: {e.suggestedExplanation}</div>
            </div>
          ))}
        </div>
      )}

      {/* OCR correction review */}
      {ocr.length > 0 && (
        <div style={{ marginBottom: 10 }}>
          <div style={{ fontSize: 11, fontWeight: 800, color: RX.rose, marginBottom: 4 }}>OCR 교정 검토</div>
          {ocr.map((item, i) => (
            <div key={i} style={{ background: RX.roseBg, border: `1px solid rgba(156,59,84,0.2)`, borderRadius: 6, padding: '7px 10px', marginBottom: 4 }}>
              <div style={{ fontSize: 10, color: RX.rose, fontWeight: 700, marginBottom: 2 }}>{item.failureStage}</div>
              <div style={{ fontSize: 11, color: RX.ink }}>원문: <b>{item.rawLine}</b></div>
              {item.recoveredCandidate && <div style={{ fontSize: 11, color: RX.teal, marginTop: 2 }}>회수 후보: {item.recoveredCandidate}</div>}
              <div style={{ fontSize: 11, color: RX.sub, marginTop: 2 }}>{item.issue}</div>
              <div style={{ fontSize: 10, color: RX.faint, marginTop: 2 }}>제안: {item.suggestedFixType}</div>
            </div>
          ))}
        </div>
      )}

      {/* suggested fixes */}
      {fixes.length > 0 && (
        <div style={{ marginBottom: 10 }}>
          <div style={{ fontSize: 11, fontWeight: 800, color: RX.sub, marginBottom: 4 }}>개선 제안</div>
          {fixes.map((f, i) => (
            <div key={i} style={{ background: RX.shell, border: `1px solid ${RX.line}`, borderRadius: 6, padding: '6px 10px', marginBottom: 4 }}>
              <div style={{ fontSize: 10, color: RX.faint, marginBottom: 2 }}>{f.fixType}</div>
              <div style={{ fontSize: 11, fontWeight: 700, color: RX.ink }}>{f.target}</div>
              <div style={{ fontSize: 11, color: RX.sub, marginTop: 2, lineHeight: 1.4 }}>{f.suggestion}</div>
            </div>
          ))}
        </div>
      )}

      {/* warnings */}
      {warnings.length > 0 && (
        <div style={{ background: RX.amberBg, border: `1px solid rgba(154,106,20,0.2)`, borderRadius: 6, padding: '7px 10px' }}>
          <div style={{ fontSize: 11, fontWeight: 800, color: RX.amber, marginBottom: 4 }}>주의 사항</div>
          {warnings.map((w, i) => <div key={i} style={{ fontSize: 11, color: RX.amber }}>{w}</div>)}
        </div>
      )}
    </div>
  );
}

function AppAdminOcr() {
  const { currentUser } = React.useContext(window.AuthContext || {});
  const [cases, setCases] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState(null);
  const [selectedCaseId, setSelectedCaseId] = React.useState(null);
  const [sourceFilter, setSourceFilter] = React.useState('all');
  const [statusFilter, setStatusFilter] = React.useState('all');
  const [preReviewNote, setPreReviewNote] = React.useState('');
  const [manuallyAddedDrugs, setManuallyAddedDrugs] = React.useState('');
  const [aiReviewLoading, setAiReviewLoading] = React.useState(false);
  const [postReviewNote, setPostReviewNote] = React.useState('');
  const [postNoteSaveStatus, setPostNoteSaveStatus] = React.useState(null);
  const [secondReviewLoading, setSecondReviewLoading] = React.useState(false);
  const [batchAnalysisResult, setBatchAnalysisResult] = React.useState(null);
  const [batchAnalysisLoading, setBatchAnalysisLoading] = React.useState(false);
  const [verdictLoading, setVerdictLoading] = React.useState(false);
  const [noteSaveStatus, setNoteSaveStatus] = React.useState(null); // null|'saving'|'saved'|'failed'
  const [ownerCorrectionNote, setOwnerCorrectionNote] = React.useState('');
  const [generatedPrompts, setGeneratedPrompts] = React.useState({});
  const [showImprovement, setShowImprovement] = React.useState(false);
  const [batches, setBatches] = React.useState([]);
  const [selectedBatchId, setSelectedBatchId] = React.useState(null);
  const [batchLoading, setBatchLoading] = React.useState(false);
  const [showLearn, setShowLearn] = React.useState(false);

  // Firestore 구독 — 케이스 + 자동 sync
  React.useEffect(() => {
    const db = firebase.firestore();
    let syncTimer = null;
    const unsub = db.collection('ocr_review_cases')
      .orderBy('createdAt', 'desc')
      .limit(100)
      .onSnapshot(
        snap => {
          const loaded = snap.docs.map(d => sanitizeOcrCaseForAdmin({ id: d.id, ...d.data() }));
          setCases(loaded);
          setLoading(false);

          // 승인 케이스가 있으면 로컬 파일에 자동 sync (debounce 2초)
          clearTimeout(syncTimer);
          syncTimer = setTimeout(() => {
            const approved = loaded.filter(c => c.ownerVerdict === 'owner_approved' && c.aiReview);
            if (approved.length === 0) return;
            const payload = approved.map(c => ({
              caseId: c.id,
              createdAt: c.createdAt,
              source: c.source,
              rawDrugs: sanitizeOcrCaseForAdmin(c).rawDrugs,
              matchedCvDrugs: (c.matchedCvDrugs || []).map(d => ({ displayName: d.displayName || d.brand, generic: d.generic, drug_class: d.drug_class, dose: d.dose })),
              unmatchedDrugs: sanitizeOcrCaseForAdmin(c).unmatchedDrugs,
              aiReview: c.aiReview,
              ownerCorrectionNote: c.ownerCorrectionNote || '',
              learnBatchId: c.learnBatchId || null,
            }));
            fetch('/api/ocr-sync', {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify({ cases: payload }),
            }).catch(() => {}); // 로컬 전용, 실패해도 무시
          }, 2000);
        },
        err => {
          setError('데이터 로드 실패: ' + err.message);
          setLoading(false);
        }
      );
    return () => { unsub(); clearTimeout(syncTimer); };
  }, []);

  // Firestore 구독 — 학습 배치
  React.useEffect(() => {
    const db = firebase.firestore();
    const unsub = db.collection('ocr_learning_batches')
      .orderBy('createdAt', 'desc')
      .limit(20)
      .onSnapshot(
        snap => setBatches(snap.docs.map(d => ({ id: d.id, ...d.data() }))),
        () => {}
      );
    return unsub;
  }, []);

  // 선택된 케이스 (live)
  const selectedCase = selectedCaseId ? cases.find(c => c.id === selectedCaseId) || null : null;

  // 케이스 선택 시 메모 동기화
  React.useEffect(() => {
    if (selectedCase) {
      setPreReviewNote(selectedCase.adminPreReviewNote || '');
      setManuallyAddedDrugs((selectedCase.manuallyAddedDrugs || []).join('\n'));
      setOwnerCorrectionNote(selectedCase.ownerCorrectionNote || '');
      setNoteSaveStatus(null);
      setPostReviewNote(selectedCase.postReviewNote || '');
      setPostNoteSaveStatus(null);
    }
  }, [selectedCaseId]);

  // 요약 통계
  const stats = React.useMemo(() => ({
    total: cases.length,
    aiPending: cases.filter(c => c.reviewStatus === 'saved').length,
    aiDone: cases.filter(c => c.reviewStatus === 'ai_reviewed').length,
    approvalPending: cases.filter(c => c.reviewStatus === 'ai_reviewed' && !c.ownerVerdict).length,
    ocrMisread: cases.filter(c => (c.aiReview?.ocr_correction_review || []).length > 0).length,
    explanationImprove: cases.filter(c => (c.aiReview?.clinical_review?.betterExplanations || []).length > 0).length,
  }), [cases]);

  // 반복 사례 (같은 caseSignature)
  const repeatCases = React.useMemo(() => {
    const sigMap = {};
    cases.forEach(c => {
      if (c.caseSignature) {
        if (!sigMap[c.caseSignature]) sigMap[c.caseSignature] = [];
        sigMap[c.caseSignature].push(c);
      }
    });
    return Object.entries(sigMap)
      .filter(([, cs]) => cs.length > 1)
      .sort((a, b) => b[1].length - a[1].length)
      .slice(0, 5);
  }, [cases]);

  // 필터 적용 케이스 목록
  const filteredCases = React.useMemo(() => {
    return cases.filter(c => {
      if (sourceFilter !== 'all' && c.source !== sourceFilter) return false;
      if (statusFilter !== 'all' && c.reviewStatus !== statusFilter) return false;
      return true;
    });
  }, [cases, sourceFilter, statusFilter]);

  // 배치 미포함 승인 케이스
  const unbatchedApproved = React.useMemo(
    () => cases.filter(c => c.ownerVerdict === 'owner_approved' && !c.learnBatchId && c.aiReview),
    [cases]
  );

  // AI 검토 완료 + 피드백 미처리 케이스 (배치 분석 대상)
  const batchTargets = React.useMemo(
    () => cases.filter(c => c.reviewStatus === 'ai_reviewed' && !c.feedbackDone),
    [cases]
  );

  // 학습 배치 실행
  const handleLearnBatch = async () => {
    const targets = unbatchedApproved.slice(0, 5);
    if (targets.length < 5) return;
    setBatchLoading(true);
    try {
      const payload = {
        cases: targets.map(c => ({
          caseId: c.id,
          aiReview: c.aiReview,
          matchedCvDrugs: c.matchedCvDrugs,
          rawDrugs: sanitizeOcrCaseForAdmin(c).rawDrugs,
        })),
      };
      const res = await fetch('/api/ocr-learn', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload),
      });
      const data = await res.json();
      if (!res.ok) { alert('학습 실패: ' + (data.error || '오류')); return; }

      const db = firebase.firestore();
      const batchRef = await db.collection('ocr_learning_batches').add({
        createdAt: firebase.firestore.FieldValue.serverTimestamp(),
        caseIds: targets.map(c => c.id),
        caseCount: targets.length,
        report: data,
        applied: false,
      });

      // 각 케이스에 배치 ID 마킹
      const batch = db.batch();
      targets.forEach(c => {
        batch.update(db.collection('ocr_review_cases').doc(c.id), { learnBatchId: batchRef.id });
      });
      await batch.commit();
      setShowLearn(true);
    } catch (e) {
      alert('학습 중 오류: ' + e.message);
    } finally {
      setBatchLoading(false);
    }
  };

  // AI 배치 분석 실행
  const handleBatchAnalyze = async () => {
    if (!currentUser || typeof currentUser.getIdToken !== 'function') {
      alert('관리자 인증 정보를 확인할 수 없습니다.');
      return;
    }
    setBatchAnalysisLoading(true);
    try {
      const idToken = await currentUser.getIdToken(/* forceRefresh= */ true);
      const payload = {
        cases: batchTargets.map(c => ({
          caseId: c.id,
          unmatchedDrugs: (sanitizeOcrCaseForAdmin(c).unmatchedDrugs || []).map(d => ({ original_name: d.original_name })),
          ocrCorrectionReview: (c.aiReview?.ocr_correction_review || []).map(r => ({
            rawLine: r.rawLine,
            issue: r.issue,
            recoveredCandidate: r.recoveredCandidate,
            suggestedFixType: r.suggestedFixType,
          })),
          suggestedFixes: (c.aiReview?.suggestedFixes || []).map(f => ({
            fixType: f.fixType,
            target: f.target,
            suggestion: f.suggestion,
          })),
        })),
      };
      const res = await fetch('/api/ocr-batch-analyze', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${idToken}` },
        body: JSON.stringify(payload),
      });
      const data = await res.json();
      if (!res.ok) {
        alert('배치 분석 실패: ' + (data.error || '오류'));
        return;
      }
      setBatchAnalysisResult(data.analysis);
      // 분석 완료된 케이스 전체 feedbackDone 자동 마킹 (재분석 방지)
      const db = firebase.firestore();
      await Promise.all(
        batchTargets.map(c =>
          db.collection('ocr_review_cases').doc(c.id).update({
            feedbackDone: true,
            feedbackAt: firebase.firestore.FieldValue.serverTimestamp(),
          })
        )
      );
    } catch (e) {
      console.error('[Batch Analyze]:', e);
      alert('배치 분석 중 오류: ' + e.message);
    } finally {
      setBatchAnalysisLoading(false);
    }
  };

  // 피드백완료 마킹
  const handleFeedbackDone = async (caseId) => {
    try {
      await firebase.firestore().collection('ocr_review_cases').doc(caseId).update({
        feedbackDone: true,
        feedbackAt: firebase.firestore.FieldValue.serverTimestamp(),
      });
    } catch (e) {
      alert('피드백완료 저장 실패: ' + e.message);
    }
  };

  // 메모 저장
  const handleSaveNote = async () => {
    if (!selectedCase) return;
    const note = preReviewNote.trim();
    if (note) {
      const preflight = rxHelpers.qualityFeedbackPreflight(note);
      if (!preflight.allowed) {
        alert('민감정보 감지로 저장 차단: ' + preflight.message);
        return;
      }
    }
    setNoteSaveStatus('saving');
    try {
      const manualDrugs = manuallyAddedDrugs.split('\n').map(s => s.trim()).filter(Boolean);
      await firebase.firestore().collection('ocr_review_cases').doc(selectedCase.id).update({
        adminPreReviewNote: note,
        manuallyAddedDrugs: manualDrugs,
      });
      setNoteSaveStatus('saved');
    } catch (e) {
      console.error('[Admin Note Save]:', e);
      setNoteSaveStatus('failed');
    }
  };

  // AI 검토 실행
  const handleAiReview = async () => {
    if (!selectedCase) return;
    const note = preReviewNote.trim();
    if (note) {
      const preflight = rxHelpers.qualityFeedbackPreflight(note);
      if (!preflight.allowed) {
        alert('메모에 민감정보가 감지되어 AI 검토를 차단합니다: ' + preflight.message);
        return;
      }
    }
    setAiReviewLoading(true);
    const manualDrugs = manuallyAddedDrugs.split('\n').map(s => s.trim()).filter(Boolean);
    try {
      if (!currentUser || typeof currentUser.getIdToken !== 'function') {
        alert('관리자 인증 정보를 확인할 수 없어 AI 검토를 실행할 수 없습니다.');
        return;
      }
      const idToken = await currentUser.getIdToken(/* forceRefresh= */ true);
      const payload = {
        caseData: {
          rawDrugs: sanitizeOcrCaseForAdmin(selectedCase).rawDrugs,
          matchedCvDrugs: sanitizeOcrCaseForAdmin(selectedCase).matchedCvDrugs,
          recognizedNonCvDrugs: sanitizeOcrCaseForAdmin(selectedCase).recognizedNonCvDrugs,
          unmatchedDrugs: sanitizeOcrCaseForAdmin(selectedCase).unmatchedDrugs,
          candidateContexts: sanitizeOcrCaseForAdmin(selectedCase).candidateContexts,
          combinationScenarios: sanitizeOcrCaseForAdmin(selectedCase).combinationScenarios,
          doseCounselingNotes: sanitizeOcrCaseForAdmin(selectedCase).doseCounselingNotes,
        },
        adminPreReviewNote: note,
        manuallyAddedDrugs: manualDrugs,
      };
      const res = await fetch('/api/ocr-review', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${idToken}` },
        body: JSON.stringify(payload),
      });
      const data = await res.json();
      const db = firebase.firestore();
      const ref = db.collection('ocr_review_cases').doc(selectedCase.id);
      if (!res.ok) {
        await ref.update({
          reviewStatus: 'review_failed',
          reviewedAt: firebase.firestore.FieldValue.serverTimestamp(),
          aiReviewError: data.error || '알 수 없는 오류',
          adminPreReviewNote: note,
          manuallyAddedDrugs: manualDrugs,
        });
        alert('AI 검토 실패: ' + (data.error || '알 수 없는 오류'));
        return;
      }
      await ref.update({
        aiReview: data,
        reviewStatus: 'ai_reviewed',
        reviewedAt: firebase.firestore.FieldValue.serverTimestamp(),
        adminPreReviewNote: note,
        manuallyAddedDrugs: manualDrugs,
      });
    } catch (e) {
      console.error('[AI Review]:', e);
      try {
        await firebase.firestore().collection('ocr_review_cases').doc(selectedCase.id).update({
          reviewStatus: 'review_failed',
          reviewedAt: firebase.firestore.FieldValue.serverTimestamp(),
        });
      } catch {}
      alert('AI 검토 중 오류: ' + e.message);
    } finally {
      setAiReviewLoading(false);
    }
  };

  // 검토 후 메모 저장
  const handlePostNoteSave = async () => {
    if (!selectedCase) return;
    setPostNoteSaveStatus('saving');
    try {
      await firebase.firestore().collection('ocr_review_cases').doc(selectedCase.id).update({
        postReviewNote: postReviewNote.trim(),
      });
      setPostNoteSaveStatus('saved');
    } catch {
      setPostNoteSaveStatus('failed');
    }
  };

  // AI 2차 검토
  const handleSecondAiReview = async () => {
    if (!selectedCase || !selectedCase.aiReview) return;
    setSecondReviewLoading(true);
    try {
      if (!currentUser || typeof currentUser.getIdToken !== 'function') {
        alert('관리자 인증 정보를 확인할 수 없어 AI 2차 검토를 실행할 수 없습니다.');
        return;
      }
      const idToken = await currentUser.getIdToken(/* forceRefresh= */ true);
      const payload = {
        caseData: {
          rawDrugs: sanitizeOcrCaseForAdmin(selectedCase).rawDrugs,
          matchedCvDrugs: sanitizeOcrCaseForAdmin(selectedCase).matchedCvDrugs,
          recognizedNonCvDrugs: sanitizeOcrCaseForAdmin(selectedCase).recognizedNonCvDrugs,
          unmatchedDrugs: sanitizeOcrCaseForAdmin(selectedCase).unmatchedDrugs,
          candidateContexts: sanitizeOcrCaseForAdmin(selectedCase).candidateContexts,
          combinationScenarios: sanitizeOcrCaseForAdmin(selectedCase).combinationScenarios,
          doseCounselingNotes: sanitizeOcrCaseForAdmin(selectedCase).doseCounselingNotes,
        },
        firstAiReview: selectedCase.aiReview,
        postReviewNote: postReviewNote.trim(),
      };
      const res = await fetch('/api/ocr-review', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${idToken}` },
        body: JSON.stringify(payload),
      });
      const data = await res.json();
      const db = firebase.firestore();
      const ref = db.collection('ocr_review_cases').doc(selectedCase.id);
      if (!res.ok) {
        alert('AI 2차 검토 실패: ' + (data.error || '알 수 없는 오류'));
        return;
      }
      await ref.update({
        aiReview2: data,
        postReviewNote: postReviewNote.trim(),
        reviewStatus: 'ai_reviewed',
        reviewedAt: firebase.firestore.FieldValue.serverTimestamp(),
      });
    } catch (e) {
      console.error('[AI 2차 Review]:', e);
      alert('AI 2차 검토 중 오류: ' + e.message);
    } finally {
      setSecondReviewLoading(false);
    }
  };

  // 오너 판정
  const handleVerdict = async (verdict) => {
    if (!selectedCase) return;
    setVerdictLoading(true);
    try {
      await firebase.firestore().collection('ocr_review_cases').doc(selectedCase.id).update({
        ownerVerdict: verdict,
        ownerDecisionAt: firebase.firestore.FieldValue.serverTimestamp(),
        reviewStatus: 'owner_decided',
      });
    } catch (e) {
      alert('판정 저장 실패: ' + e.message);
    } finally {
      setVerdictLoading(false);
    }
  };

  // 개선 작업 프롬프트 생성
  const handleGeneratePrompt = (caseItem) => {
    if (!caseItem || !caseItem.aiReview) return;
    const ocrFixes = caseItem.aiReview.ocr_correction_review || [];
    const fixes = caseItem.aiReview.suggestedFixes || [];
    let prompt = '';
    if (ocrFixes.length > 0) {
      const f = ocrFixes[0];
      prompt = `"${f.rawLine || ''}"이 "${f.recoveredCandidate || ''}"으로 회수되는지 테스트를 먼저 추가하고, 자동 alias 추가 없이 보정 후보를 검토하라. (suggestedFixType: ${f.suggestedFixType || ''})`;
    } else if (fixes.length > 0) {
      const f = fixes[0];
      prompt = `[${f.fixType}] ${f.target}: ${f.suggestion}`;
    } else {
      prompt = `caseId: ${caseItem.id} — AI 검토 결과를 바탕으로 개선 후보를 수동으로 검토하라.`;
    }
    setGeneratedPrompts(prev => ({ ...prev, [caseItem.id]: prompt }));
  };

  // 승인된 케이스
  const approvedCases = cases.filter(c => c.ownerVerdict === 'owner_approved');

  const inputStyle = {
    width: '100%', padding: '8px 10px', borderRadius: 7,
    border: `1px solid ${RX.line}`, font: 'inherit', fontSize: 12,
    background: '#fff', outline: 'none', boxSizing: 'border-box', resize: 'vertical',
  };

  // ── 상세 패널 ──
  if (selectedCase) {
    return (
      <div style={{ flex: 1, overflowY: 'auto', background: RX.shell }}>
        {/* 헤더 */}
        <div style={{ position: 'sticky', top: 0, zIndex: 10, background: RX.paper, borderBottom: `1px solid ${RX.line}`, padding: '10px 14px', display: 'flex', alignItems: 'center', gap: 10 }}>
          <button onClick={() => setSelectedCaseId(null)} style={{ background: 'none', border: 'none', cursor: 'pointer', color: RX.teal, fontSize: 20, lineHeight: 1, padding: '0 4px' }}>←</button>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontSize: 13, fontWeight: 800, color: RX.ink }}>케이스 상세</div>
            <div style={{ fontSize: 10, color: RX.faint }}>{fmtTs(selectedCase.createdAt)}</div>
          </div>
          <SourceBadge source={selectedCase.source} />
          <StatusBadge status={selectedCase.reviewStatus} />
        </div>

        <div style={{ padding: '14px 14px 80px' }}>

          {/* OCR 원문 약물 */}
          <div style={{ background: RX.paper, border: `1px solid ${RX.line}`, borderRadius: 10, marginBottom: 12, overflow: 'hidden' }}>
            <div style={{ padding: '9px 13px', borderBottom: `1px solid ${RX.lineSoft}`, fontSize: 11, fontWeight: 800, color: RX.faint }}>OCR 원문 약물</div>
            <div style={{ padding: '10px 13px' }}>
              {(selectedCase.rawDrugs || []).length === 0
                ? <div style={{ fontSize: 12, color: RX.faint }}>없음</div>
                : (selectedCase.rawDrugs || []).map((d, i) => (
                  <div key={i} style={{ fontSize: 12, color: RX.ink, padding: '3px 0', borderBottom: i < selectedCase.rawDrugs.length - 1 ? `1px solid ${RX.lineSoft}` : 'none' }}>
                    {d.rawName || d.name || JSON.stringify(d)}
                    {d.dosage && <span style={{ color: RX.sub }}> · {d.dosage}</span>}
                    {d.frequency && <span style={{ color: RX.faint }}> · {d.frequency}</span>}
                  </div>
                ))}
            </div>
          </div>

          {/* CV 매칭 약물 */}
          <div style={{ background: RX.paper, border: `1px solid ${RX.line}`, borderRadius: 10, marginBottom: 12, overflow: 'hidden' }}>
            <div style={{ padding: '9px 13px', borderBottom: `1px solid ${RX.lineSoft}`, fontSize: 11, fontWeight: 800, color: RX.tealDark }}>CV 매칭 약물</div>
            <div style={{ padding: '10px 13px' }}>
              {(selectedCase.matchedCvDrugs || []).length === 0
                ? <div style={{ fontSize: 12, color: RX.faint }}>없음</div>
                : (selectedCase.matchedCvDrugs || []).map((drug, i) => (
                  <div key={i} style={{ marginBottom: i < selectedCase.matchedCvDrugs.length - 1 ? 12 : 0, paddingBottom: i < selectedCase.matchedCvDrugs.length - 1 ? 12 : 0, borderBottom: i < selectedCase.matchedCvDrugs.length - 1 ? `1px solid ${RX.lineSoft}` : 'none' }}>
                    <div style={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 6 }}>
                      <span style={{ fontSize: 13, fontWeight: 800, color: RX.ink }}>{drug.displayName || drug.brand}</span>
                      {drug.corrected && (
                        <span style={{ fontSize: 10, color: RX.rose, background: RX.roseBg, padding: '1px 5px', borderRadius: 4 }}>
                          {drug.original_name} → 교정
                        </span>
                      )}
                      <span style={{ fontSize: 10, color: RX.faint, border: `1px solid ${RX.line}`, padding: '1px 5px', borderRadius: 4 }}>{drug.ingredientConfidence}</span>
                    </div>
                    <div style={{ fontSize: 11, color: RX.sub, marginTop: 3 }}>{drug.generic} · {drug.drug_class}</div>
                    {drug.dose && <div style={{ fontSize: 11, color: RX.sub, marginTop: 2 }}>용량 {drug.dose} ({drug.doseConfidence})</div>}
                    <DrugTopCandidates drug={drug} />
                  </div>
                ))}
            </div>
          </div>

          {/* 비CV 인식 약물 */}
          {(selectedCase.recognizedNonCvDrugs || []).length > 0 && (
            <div style={{ background: RX.paper, border: `1px solid ${RX.line}`, borderRadius: 10, marginBottom: 12, overflow: 'hidden' }}>
              <div style={{ padding: '9px 13px', borderBottom: `1px solid ${RX.lineSoft}`, fontSize: 11, fontWeight: 800, color: RX.sub }}>비CV 인식 약물</div>
              <div style={{ padding: '10px 13px' }}>
                {(selectedCase.recognizedNonCvDrugs || []).map((drug, i) => (
                  <div key={i} style={{ fontSize: 12, color: RX.ink, padding: '3px 0', borderBottom: i < selectedCase.recognizedNonCvDrugs.length - 1 ? `1px solid ${RX.lineSoft}` : 'none' }}>
                    {drug.displayName || drug.brand} <span style={{ color: RX.sub }}>· {drug.generic} · {drug.drug_class}</span>
                  </div>
                ))}
              </div>
            </div>
          )}

          {/* 확인 필요 약물 */}
          {(selectedCase.unmatchedDrugs || []).length > 0 && (
            <div style={{ background: RX.paper, border: `1px solid ${RX.line}`, borderRadius: 10, marginBottom: 12, overflow: 'hidden' }}>
              <div style={{ padding: '9px 13px', borderBottom: `1px solid ${RX.lineSoft}`, fontSize: 11, fontWeight: 800, color: RX.amber }}>확인 필요 약물</div>
              <div style={{ padding: '10px 13px' }}>
                {(selectedCase.unmatchedDrugs || []).map((drug, i) => (
                  <div key={i} style={{ fontSize: 12, color: RX.ink, padding: '3px 0', borderBottom: i < selectedCase.unmatchedDrugs.length - 1 ? `1px solid ${RX.lineSoft}` : 'none' }}>
                    {drug.original_name}
                  </div>
                ))}
              </div>
            </div>
          )}

          {/* 후보 질환/조합 */}
          {(selectedCase.combinationScenarios || []).length > 0 && (
            <div style={{ background: RX.paper, border: `1px solid ${RX.line}`, borderRadius: 10, marginBottom: 12, overflow: 'hidden' }}>
              <div style={{ padding: '9px 13px', borderBottom: `1px solid ${RX.lineSoft}`, fontSize: 11, fontWeight: 800, color: RX.sub }}>후보 질환/조합 ({selectedCase.combinationScenarios.length}건)</div>
              <div style={{ padding: '10px 13px' }}>
                {(selectedCase.combinationScenarios || []).slice(0, 2).map((sc, i) => (
                  <div key={i} style={{ marginBottom: i < 1 ? 10 : 0, paddingBottom: i < 1 ? 10 : 0, borderBottom: i < 1 ? `1px solid ${RX.lineSoft}` : 'none' }}>
                    <div style={{ fontSize: 12, fontWeight: 700, color: RX.tealDark, marginBottom: 4 }}>
                      {i === 0 ? '가장 높은 해석' : `대안 ${i}`} ({sc.score}%)
                    </div>
                    {(sc.diseases || []).map((d, di) => (
                      <div key={di} style={{ paddingLeft: 8, marginBottom: 4 }}>
                        <div style={{ fontSize: 11, fontWeight: 700, color: RX.ink }}>{d.diseaseLabel}</div>
                        {(d.drugs || []).map((dr, dri) => (
                          <div key={dri} style={{ fontSize: 11, color: RX.sub, marginTop: 2 }}>
                            {dr.brand} · {dr.roleLabel}
                          </div>
                        ))}
                      </div>
                    ))}
                  </div>
                ))}
              </div>
            </div>
          )}

          {/* AI 검토 전 메모 */}
          <div style={{ background: RX.paper, border: `1px solid ${RX.line}`, borderRadius: 10, marginBottom: 12, overflow: 'hidden' }}>
            <div style={{ padding: '9px 13px', borderBottom: `1px solid ${RX.lineSoft}`, fontSize: 11, fontWeight: 800, color: RX.sub }}>AI 검토 전 관리자 메모</div>
            <div style={{ padding: '12px 13px' }}>
              <div style={{ fontSize: 11, color: RX.faint, marginBottom: 6 }}>자유 메모 (민감정보 입력 금지)</div>
              <textarea
                value={preReviewNote}
                onChange={e => { setPreReviewNote(e.target.value); setNoteSaveStatus(null); }}
                placeholder="OCR 오류나 임상 맥락에 대한 힌트를 적어주세요..."
                rows={3}
                style={{ ...inputStyle }}
              />
              <div style={{ fontSize: 11, color: RX.faint, marginTop: 8, marginBottom: 4 }}>OCR이 놓친 약물명 (줄 단위)</div>
              <textarea
                value={manuallyAddedDrugs}
                onChange={e => { setManuallyAddedDrugs(e.target.value); setNoteSaveStatus(null); }}
                placeholder="예: 자디앙정25mg"
                rows={2}
                style={{ ...inputStyle }}
              />
              <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 8 }}>
                <button
                  onClick={handleSaveNote}
                  disabled={noteSaveStatus === 'saving'}
                  style={{ padding: '7px 14px', borderRadius: 7, background: RX.shell, color: RX.teal, border: `1px solid ${RX.tealLine}`, font: 'inherit', fontSize: 12, fontWeight: 700, cursor: 'pointer' }}
                >
                  {noteSaveStatus === 'saving' ? '저장 중...' : '메모 저장'}
                </button>
                {noteSaveStatus === 'saved' && <span style={{ fontSize: 11, color: RX.teal }}>저장됨</span>}
                {noteSaveStatus === 'failed' && <span style={{ fontSize: 11, color: RX.rose }}>저장 실패</span>}
              </div>
            </div>
          </div>

          {/* AI 검토 실행 버튼 */}
          {selectedCase.reviewStatus !== 'ai_reviewed' && selectedCase.reviewStatus !== 'owner_decided' && (
            <button
              onClick={handleAiReview}
              disabled={aiReviewLoading}
              style={{
                width: '100%', padding: '13px', borderRadius: 10, marginBottom: 12,
                background: aiReviewLoading ? RX.shell : RX.teal, color: aiReviewLoading ? RX.faint : '#fff',
                border: 'none', font: 'inherit', fontSize: 14, fontWeight: 800, cursor: aiReviewLoading ? 'default' : 'pointer',
              }}
            >
              {aiReviewLoading ? 'AI 검토 실행 중...' : 'AI 검토 실행'}
            </button>
          )}
          {selectedCase.reviewStatus === 'review_failed' && (
            <div style={{ background: RX.roseBg, border: `1px solid rgba(156,59,84,0.2)`, borderRadius: 8, padding: '8px 12px', marginBottom: 12, fontSize: 12, color: RX.rose }}>
              이전 AI 검토 실패: {selectedCase.aiReviewError || '알 수 없는 오류'}
              <button onClick={handleAiReview} disabled={aiReviewLoading} style={{ marginLeft: 10, fontSize: 11, color: RX.teal, background: 'none', border: 'none', cursor: 'pointer', fontWeight: 700 }}>
                재시도
              </button>
            </div>
          )}
          {(selectedCase.reviewStatus === 'ai_reviewed' || selectedCase.reviewStatus === 'owner_decided') && (
            <button
              onClick={handleAiReview}
              disabled={aiReviewLoading}
              style={{ width: '100%', padding: '10px', borderRadius: 8, marginBottom: 12, background: RX.shell, color: RX.teal, border: `1px solid ${RX.tealLine}`, font: 'inherit', fontSize: 12, fontWeight: 700, cursor: 'pointer' }}
            >
              {aiReviewLoading ? 'AI 재검토 중...' : 'AI 재검토'}
            </button>
          )}

          {/* AI 검토 결과 */}
          {selectedCase.aiReview && <AiReviewResult aiReview={selectedCase.aiReview} />}

          {/* 검토 후 메모 + AI 2차 검토 */}
          {selectedCase.aiReview && (
            <div style={{ background: RX.paper, border: `1px solid ${RX.line}`, borderRadius: 10, marginTop: 14, overflow: 'hidden' }}>
              <div style={{ padding: '9px 13px', borderBottom: `1px solid ${RX.lineSoft}`, fontSize: 11, fontWeight: 800, color: RX.sub }}>검토 후 메모 · AI 2차 검토</div>
              <div style={{ padding: '12px 13px' }}>
                <div style={{ fontSize: 11, color: RX.faint, marginBottom: 6 }}>1차 검토 결과에 대한 메모 (민감정보 입력 금지)</div>
                <textarea
                  value={postReviewNote}
                  onChange={e => { setPostReviewNote(e.target.value); setPostNoteSaveStatus(null); }}
                  placeholder="예) 크레젯정이 잘못 교정됨, 진단명 추가 필요 등"
                  rows={3}
                  style={{ width: '100%', fontSize: 12, borderRadius: 6, border: `1px solid ${RX.line}`, padding: '7px 9px', resize: 'vertical', fontFamily: 'inherit', boxSizing: 'border-box' }}
                />
                <div style={{ display: 'flex', gap: 8, marginTop: 8 }}>
                  <button
                    onClick={handlePostNoteSave}
                    disabled={postNoteSaveStatus === 'saving'}
                    style={{ fontSize: 12, padding: '6px 14px', borderRadius: 6, background: RX.shell, color: RX.ink, border: `1px solid ${RX.line}`, cursor: 'pointer', fontWeight: 700 }}
                  >
                    {postNoteSaveStatus === 'saving' ? '저장 중...' : postNoteSaveStatus === 'saved' ? '저장됨 ✓' : '메모 저장'}
                  </button>
                  <button
                    onClick={handleSecondAiReview}
                    disabled={secondReviewLoading}
                    style={{ flex: 1, fontSize: 13, padding: '6px 14px', borderRadius: 6, background: secondReviewLoading ? RX.shell : RX.teal, color: secondReviewLoading ? RX.faint : '#fff', border: 'none', cursor: secondReviewLoading ? 'default' : 'pointer', fontWeight: 800 }}
                  >
                    {secondReviewLoading ? 'AI 2차 검토 중...' : 'AI 2차 검토 실행'}
                  </button>
                </div>
              </div>
            </div>
          )}

          {/* AI 2차 검토 결과 */}
          {selectedCase.aiReview2 && (
            <div style={{ marginTop: 14 }}>
              <div style={{ fontSize: 12, fontWeight: 800, color: RX.tealDark, marginBottom: 6, paddingBottom: 6, borderBottom: `2px solid ${RX.tealLine}` }}>
                🔄 AI 2차 검토 결과
              </div>
              <AiReviewResult aiReview={selectedCase.aiReview2} />
            </div>
          )}

          {/* 오너 판정 */}
          {(selectedCase.reviewStatus === 'ai_reviewed' || selectedCase.reviewStatus === 'owner_decided') && (
            <div style={{ background: RX.paper, border: `1px solid ${RX.line}`, borderRadius: 10, marginTop: 14, overflow: 'hidden' }}>
              <div style={{ padding: '9px 13px', borderBottom: `1px solid ${RX.lineSoft}`, fontSize: 11, fontWeight: 800, color: RX.sub }}>오너 판정</div>
              <div style={{ padding: '12px 13px' }}>
                {selectedCase.ownerVerdict && (
                  <div style={{ fontSize: 12, color: RX.amber, fontWeight: 700, marginBottom: 10 }}>
                    현재 판정: {VERDICT_LABELS[selectedCase.ownerVerdict] || selectedCase.ownerVerdict}
                    {selectedCase.ownerDecisionAt && <span style={{ color: RX.faint, fontWeight: 400 }}> · {fmtTs(selectedCase.ownerDecisionAt)}</span>}
                  </div>
                )}
                <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 7 }}>
                  {Object.entries(VERDICT_LABELS).map(([v, label]) => (
                    <button
                      key={v}
                      onClick={() => handleVerdict(v)}
                      disabled={verdictLoading}
                      style={{
                        padding: '8px', borderRadius: 7, font: 'inherit', fontSize: 11, fontWeight: 700,
                        cursor: verdictLoading ? 'default' : 'pointer',
                        background: selectedCase.ownerVerdict === v ? RX.teal : RX.shell,
                        color: selectedCase.ownerVerdict === v ? '#fff' : RX.sub,
                        border: `1px solid ${selectedCase.ownerVerdict === v ? RX.teal : RX.line}`,
                      }}
                    >
                      {label}
                    </button>
                  ))}
                </div>
              </div>
            </div>
          )}

          {/* AI 판정 교정 메모 — 강화학습용 */}
          {selectedCase.ownerVerdict && (
            <div style={{ background: RX.paper, border: `1px solid ${RX.line}`, borderRadius: 10, marginTop: 10, overflow: 'hidden' }}>
              <div style={{ padding: '9px 13px', borderBottom: `1px solid ${RX.lineSoft}`, fontSize: 11, fontWeight: 800, color: RX.sub }}>
                AI 판정 교정 메모 <span style={{ fontSize: 10, color: RX.faint, fontWeight: 400 }}>(/ocr-learn 학습에 사용됨)</span>
              </div>
              <div style={{ padding: '10px 13px' }}>
                <div style={{ fontSize: 11, color: RX.faint, marginBottom: 6 }}>AI가 틀린 부분과 올바른 해석을 적어주세요 (선택)</div>
                <textarea
                  value={ownerCorrectionNote}
                  onChange={e => setOwnerCorrectionNote(e.target.value)}
                  placeholder="예: 자디앙은 심부전이 아닌 당뇨 그룹에 배정해야 함. 엔진이 SGLT2 심부전 적응증만 보고 잘못 묶음."
                  rows={3}
                  style={{ width: '100%', padding: '8px 10px', borderRadius: 7, border: `1px solid ${RX.line}`, font: 'inherit', fontSize: 12, background: '#fff', outline: 'none', boxSizing: 'border-box', resize: 'vertical' }}
                />
                <button
                  onClick={async () => {
                    try {
                      const note = ownerCorrectionNote.trim();
                      if (note) {
                        const preflight = rxHelpers.qualityFeedbackPreflight(note);
                        if (!preflight.allowed) {
                          alert('민감정보 감지로 저장 차단: ' + preflight.message);
                          return;
                        }
                      }
                      await firebase.firestore().collection('ocr_review_cases').doc(selectedCase.id).update({ ownerCorrectionNote: ownerCorrectionNote.trim() });
                    } catch (e) { alert('저장 실패: ' + e.message); }
                  }}
                  style={{ marginTop: 7, padding: '6px 14px', borderRadius: 7, background: RX.shell, color: RX.teal, border: `1px solid ${RX.tealLine}`, font: 'inherit', fontSize: 11, fontWeight: 700, cursor: 'pointer' }}
                >
                  교정 메모 저장
                </button>
              </div>
            </div>
          )}

          {/* 개선 작업함 (승인된 경우) */}
          {selectedCase.ownerVerdict === 'owner_approved' && (
            <div style={{ background: RX.tealTint, border: `1px solid ${RX.tealLine}`, borderRadius: 10, marginTop: 14, overflow: 'hidden' }}>
              <div style={{ padding: '9px 13px', borderBottom: `1px solid ${RX.tealLine}`, fontSize: 11, fontWeight: 800, color: RX.tealDark }}>개선 작업함</div>
              <div style={{ padding: '12px 13px' }}>
                {generatedPrompts[selectedCase.id] ? (
                  <div>
                    <div style={{ fontSize: 11, color: RX.sub, marginBottom: 6 }}>생성된 작업 프롬프트</div>
                    <div style={{ background: '#fff', border: `1px solid ${RX.tealLine}`, borderRadius: 7, padding: '10px 12px', fontSize: 12, color: RX.ink, lineHeight: 1.55, marginBottom: 8 }}>
                      {generatedPrompts[selectedCase.id]}
                    </div>
                    <button
                      onClick={() => {
                        navigator.clipboard && navigator.clipboard.writeText(generatedPrompts[selectedCase.id])
                          .then(() => alert('클립보드에 복사됨'))
                          .catch(() => alert(generatedPrompts[selectedCase.id]));
                      }}
                      style={{ padding: '7px 14px', borderRadius: 7, background: RX.teal, color: '#fff', border: 'none', font: 'inherit', fontSize: 12, fontWeight: 700, cursor: 'pointer' }}
                    >
                      프롬프트 복사
                    </button>
                  </div>
                ) : (
                  <button
                    onClick={() => handleGeneratePrompt(selectedCase)}
                    disabled={!selectedCase.aiReview}
                    style={{ width: '100%', padding: '10px', borderRadius: 8, background: RX.teal, color: '#fff', border: 'none', font: 'inherit', fontSize: 13, fontWeight: 700, cursor: selectedCase.aiReview ? 'pointer' : 'default', opacity: selectedCase.aiReview ? 1 : 0.5 }}
                  >
                    Codex/Antigravity 작업 프롬프트 생성
                  </button>
                )}
              </div>
            </div>
          )}
        </div>
      </div>
    );
  }

  // ── 목록 뷰 ──
  return (
    <div style={{ flex: 1, overflowY: 'auto', background: RX.shell }}>
      {/* 헤더 */}
      <div style={{ background: RX.paper, borderBottom: `1px solid ${RX.line}`, padding: '12px 14px' }}>
        <div style={{ fontSize: 15, fontWeight: 800, color: RX.ink, marginBottom: 10 }}>OCR 개선센터</div>

        {/* 요약 카드 */}
        <div style={{ display: 'flex', gap: 7, flexWrap: 'wrap', marginBottom: 10 }}>
          <SummaryCard label="전체" value={stats.total} accent={RX.ink} />
          <SummaryCard label="AI 대기" value={stats.aiPending} accent={RX.sub} />
          <SummaryCard label="AI 완료" value={stats.aiDone} accent={RX.teal} />
          <SummaryCard label="승인 대기" value={stats.approvalPending} accent={RX.amber} />
          <SummaryCard label="OCR 오독" value={stats.ocrMisread} accent={RX.rose} />
          <SummaryCard label="설명 개선" value={stats.explanationImprove} accent={RX.teal} />
        </div>

        {/* 필터 */}
        <div style={{ display: 'flex', gap: 7, flexWrap: 'wrap' }}>
          <select value={sourceFilter} onChange={e => setSourceFilter(e.target.value)} style={{ padding: '5px 8px', borderRadius: 6, border: `1px solid ${RX.line}`, font: 'inherit', fontSize: 11, background: '#fff', color: RX.ink, cursor: 'pointer' }}>
            <option value="all">전체 소스</option>
            <option value="admin">관리자 테스트</option>
            <option value="user">사용자 데이터</option>
          </select>
          <select value={statusFilter} onChange={e => setStatusFilter(e.target.value)} style={{ padding: '5px 8px', borderRadius: 6, border: `1px solid ${RX.line}`, font: 'inherit', fontSize: 11, background: '#fff', color: RX.ink, cursor: 'pointer' }}>
            <option value="all">전체 상태</option>
            <option value="saved">저장됨</option>
            <option value="ai_reviewed">AI 검토 완료</option>
            <option value="review_failed">AI 검토 실패</option>
            <option value="owner_decided">판정 완료</option>
          </select>
          <button
            onClick={() => setShowImprovement(v => !v)}
            style={{ padding: '5px 10px', borderRadius: 6, border: `1px solid ${approvedCases.length > 0 ? RX.tealLine : RX.line}`, background: showImprovement ? RX.tealTint : '#fff', color: approvedCases.length > 0 ? RX.tealDark : RX.faint, font: 'inherit', fontSize: 11, fontWeight: 700, cursor: 'pointer' }}
          >
            개선 작업함 {approvedCases.length > 0 ? `(${approvedCases.length})` : ''}
          </button>
        </div>
      </div>

      {/* 자가학습 현황 */}
      <div style={{ background: unbatchedApproved.length > 0 ? 'rgba(26,108,91,0.06)' : RX.shell, borderBottom: `1px solid ${RX.line}`, padding: '10px 14px' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 11, fontWeight: 800, color: RX.tealDark, marginBottom: 2 }}>자가학습</div>
            <div style={{ fontSize: 11, color: RX.sub }}>
              승인 케이스 <b style={{ color: unbatchedApproved.length > 0 ? RX.teal : RX.faint }}>{unbatchedApproved.length}개</b>
              {unbatchedApproved.length > 0
                ? <span style={{ color: RX.teal }}> · 자동 저장 중 · /ocr-learn 으로 분석</span>
                : <span style={{ color: RX.faint }}> · 승인된 케이스 없음</span>}
            </div>
          </div>
          {unbatchedApproved.length > 0 && (
            <button
              onClick={handleLearnBatch}
              disabled={batchLoading}
              style={{ padding: '7px 14px', borderRadius: 8, background: batchLoading ? RX.shell : RX.teal, color: batchLoading ? RX.faint : '#fff', border: 'none', font: 'inherit', fontSize: 12, fontWeight: 800, cursor: batchLoading ? 'default' : 'pointer' }}
            >
              {batchLoading ? '분석 중...' : '리포트 생성'}
            </button>
          )}
          {batches.length > 0 && (
            <button
              onClick={() => setShowLearn(v => !v)}
              style={{ padding: '7px 10px', borderRadius: 8, background: showLearn ? RX.tealTint : '#fff', color: RX.teal, border: `1px solid ${RX.tealLine}`, font: 'inherit', fontSize: 11, fontWeight: 700, cursor: 'pointer' }}
            >
              리포트 {batches.length}건
            </button>
          )}
        </div>
      </div>

      {/* AI 배치 분석 */}
      <div style={{ background: batchTargets.length > 0 ? 'rgba(26,108,91,0.04)' : RX.shell, borderBottom: `1px solid ${RX.line}`, padding: '10px 14px' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 11, fontWeight: 800, color: RX.tealDark, marginBottom: 2 }}>AI 배치 분석</div>
            <div style={{ fontSize: 11, color: RX.sub }}>
              분석 대기 <b style={{ color: batchTargets.length > 0 ? RX.teal : RX.faint }}>{batchTargets.length}건</b>
              {batchTargets.length > 0
                ? <span style={{ color: RX.teal }}> · AI 검토 완료, 피드백 미처리 · /ocr-learn 으로 개선 반영</span>
                : <span style={{ color: RX.faint }}> · 분석 대기 없음</span>}
            </div>
          </div>
          {batchTargets.length > 0 && (
            <button
              onClick={handleBatchAnalyze}
              disabled={batchAnalysisLoading}
              style={{ padding: '7px 14px', borderRadius: 8, background: batchAnalysisLoading ? RX.shell : RX.teal, color: batchAnalysisLoading ? RX.faint : '#fff', border: 'none', font: 'inherit', fontSize: 12, fontWeight: 800, cursor: batchAnalysisLoading ? 'default' : 'pointer' }}
            >
              {batchAnalysisLoading ? '분석 중...' : '배치 분석 실행'}
            </button>
          )}
          {batchAnalysisResult && (
            <button
              onClick={() => setBatchAnalysisResult(null)}
              style={{ padding: '7px 10px', borderRadius: 8, background: RX.tealTint, color: RX.teal, border: `1px solid ${RX.tealLine}`, font: 'inherit', fontSize: 11, fontWeight: 700, cursor: 'pointer' }}
            >
              닫기
            </button>
          )}
        </div>

        {batchAnalysisResult && (
          <div style={{ marginTop: 12 }}>
            {/* OCR 오류 패턴 */}
            {(batchAnalysisResult.ocrErrorPatterns || []).length > 0 && (
              <div style={{ marginBottom: 10 }}>
                <div style={{ fontSize: 11, fontWeight: 800, color: RX.rose, marginBottom: 5 }}>OCR 오류 패턴</div>
                {batchAnalysisResult.ocrErrorPatterns.map((p, i) => (
                  <div key={i} style={{ background: RX.roseBg, border: `1px solid rgba(156,59,84,0.15)`, borderRadius: 6, padding: '6px 10px', marginBottom: 4 }}>
                    <div style={{ fontSize: 11, fontWeight: 700, color: RX.ink }}>{p.pattern} <span style={{ color: RX.faint, fontWeight: 400 }}>({p.count}건)</span></div>
                    {(p.examples || []).length > 0 && <div style={{ fontSize: 10, color: RX.sub, marginTop: 2 }}>예: {p.examples.slice(0, 3).join(', ')}</div>}
                  </div>
                ))}
              </div>
            )}

            {/* DB 미등재 약물 */}
            {(batchAnalysisResult.unregisteredDrugs || []).length > 0 && (
              <div style={{ marginBottom: 10 }}>
                <div style={{ fontSize: 11, fontWeight: 800, color: RX.amber, marginBottom: 5 }}>DB 미등재 약물</div>
                {batchAnalysisResult.unregisteredDrugs.map((d, i) => (
                  <div key={i} style={{ background: RX.amberBg, border: `1px solid rgba(154,106,20,0.15)`, borderRadius: 6, padding: '6px 10px', marginBottom: 4, fontSize: 11, color: RX.ink }}>
                    {d.name} <span style={{ color: RX.faint }}>({d.occurrences}건)</span>
                  </div>
                ))}
              </div>
            )}

            {/* 개선 방향 */}
            {(batchAnalysisResult.improvementDirections || []).length > 0 && (
              <div style={{ marginBottom: 10 }}>
                <div style={{ fontSize: 11, fontWeight: 800, color: RX.tealDark, marginBottom: 5 }}>개선 방향</div>
                {batchAnalysisResult.improvementDirections.map((d, i) => (
                  <div key={i} style={{ background: '#fff', border: `1px solid ${RX.tealLine}`, borderRadius: 6, padding: '7px 10px', marginBottom: 4 }}>
                    <div style={{ display: 'flex', gap: 6, alignItems: 'center', marginBottom: 3 }}>
                      <span style={{ fontSize: 10, fontWeight: 800, color: '#fff', background: RX.teal, borderRadius: 99, width: 16, height: 16, display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>{d.priority}</span>
                      <span style={{ fontSize: 10, color: RX.faint, border: `1px solid ${RX.line}`, padding: '1px 5px', borderRadius: 4 }}>{d.target}</span>
                    </div>
                    <div style={{ fontSize: 11, fontWeight: 700, color: RX.ink }}>{d.action}</div>
                    {d.rationale && <div style={{ fontSize: 11, color: RX.sub, marginTop: 2 }}>{d.rationale}</div>}
                  </div>
                ))}
              </div>
            )}

            {/* 케이스별 요약 + 피드백완료 버튼 */}
            {(batchAnalysisResult.caseSummaries || []).length > 0 && (
              <div>
                <div style={{ fontSize: 11, fontWeight: 800, color: RX.sub, marginBottom: 5 }}>케이스별 요약</div>
                {batchAnalysisResult.caseSummaries.map((s, i) => {
                  const isDone = cases.find(c => c.id === s.caseId)?.feedbackDone;
                  return (
                    <div key={i} style={{ background: '#fff', border: `1px solid ${isDone ? RX.line : RX.tealLine}`, borderRadius: 6, padding: '7px 10px', marginBottom: 4, display: 'flex', alignItems: 'center', gap: 8 }}>
                      <div style={{ flex: 1, fontSize: 11, color: isDone ? RX.faint : RX.ink, lineHeight: 1.4 }}>{s.keyIssue}</div>
                      {isDone
                        ? <span style={{ fontSize: 10, color: RX.faint, flexShrink: 0 }}>완료</span>
                        : (
                          <button
                            onClick={() => handleFeedbackDone(s.caseId)}
                            style={{ fontSize: 11, padding: '4px 10px', borderRadius: 5, background: RX.tealTint, color: RX.tealDark, border: `1px solid ${RX.tealLine}`, cursor: 'pointer', fontWeight: 700, whiteSpace: 'nowrap', flexShrink: 0 }}
                          >
                            피드백완료
                          </button>
                        )
                      }
                    </div>
                  );
                })}
              </div>
            )}
          </div>
        )}
      </div>

      {/* 학습 리포트 목록 */}
      {showLearn && batches.length > 0 && (
        <div style={{ background: RX.tealTint, borderBottom: `1px solid ${RX.tealLine}`, padding: '10px 14px' }}>
          <div style={{ fontSize: 11, fontWeight: 800, color: RX.tealDark, marginBottom: 8 }}>학습 리포트</div>
          {batches.map(b => (
            <div key={b.id} style={{ background: '#fff', border: `1px solid ${RX.tealLine}`, borderRadius: 8, marginBottom: 8 }}>
              <div
                onClick={() => setSelectedBatchId(selectedBatchId === b.id ? null : b.id)}
                style={{ padding: '9px 12px', cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 8 }}
              >
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 11, fontWeight: 800, color: RX.ink }}>{fmtTs(b.createdAt)} · {b.caseCount}케이스</div>
                  <div style={{ fontSize: 11, color: RX.sub, marginTop: 2 }}>{b.report?.batchSummary || ''}</div>
                </div>
                <span style={{ fontSize: 11, color: RX.teal }}>{selectedBatchId === b.id ? '▲' : '▼'}</span>
              </div>
              {selectedBatchId === b.id && b.report && (
                <div style={{ padding: '0 12px 12px', borderTop: `1px solid ${RX.tealLine}` }}>
                  {/* 우선순위 액션 */}
                  {(b.report.prioritizedActions || []).length > 0 && (
                    <div style={{ marginTop: 10 }}>
                      <div style={{ fontSize: 11, fontWeight: 800, color: RX.tealDark, marginBottom: 6 }}>우선순위 개선 액션</div>
                      {b.report.prioritizedActions.map((a, i) => (
                        <div key={i} style={{ background: RX.shell, border: `1px solid ${RX.line}`, borderRadius: 6, padding: '7px 10px', marginBottom: 5 }}>
                          <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 3 }}>
                            <span style={{ fontSize: 10, fontWeight: 800, color: '#fff', background: RX.teal, borderRadius: 99, width: 16, height: 16, display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>{a.priority}</span>
                            <span style={{ fontSize: 10, color: RX.faint, border: `1px solid ${RX.line}`, padding: '1px 5px', borderRadius: 4 }}>{a.category}</span>
                            <span style={{ fontSize: 11, fontWeight: 700, color: RX.ink }}>{a.target}</span>
                          </div>
                          <div style={{ fontSize: 11, color: RX.sub, lineHeight: 1.5 }}>{a.action}</div>
                        </div>
                      ))}
                    </div>
                  )}
                  {/* OCR 패턴 */}
                  {(b.report.ocrPatterns || []).length > 0 && (
                    <div style={{ marginTop: 10 }}>
                      <div style={{ fontSize: 11, fontWeight: 800, color: RX.rose, marginBottom: 6 }}>OCR 반복 오류 패턴</div>
                      {b.report.ocrPatterns.map((p, i) => (
                        <div key={i} style={{ background: RX.roseBg, border: `1px solid rgba(156,59,84,0.15)`, borderRadius: 6, padding: '6px 10px', marginBottom: 4 }}>
                          <div style={{ fontSize: 11, fontWeight: 700, color: RX.ink }}>{p.pattern} <span style={{ color: RX.faint, fontWeight: 400 }}>({p.frequency}건)</span></div>
                          <div style={{ fontSize: 11, color: RX.sub, marginTop: 2 }}>{p.actionableStep}</div>
                          {(p.caseIndices || []).length > 0 && (
                            <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap', marginTop: 5 }}>
                              {p.caseIndices.map(idx => {
                                const cId = (b.caseIds || [])[idx - 1];
                                const c = cId ? cases.find(x => x.id === cId) : null;
                                const label = c ? ((c.matchedCvDrugs || [])[0]?.displayName || (c.rawDrugs || [])[0]?.rawName || `케이스 ${idx}`) : `케이스 ${idx}`;
                                return (
                                  <button key={idx} onClick={() => { setShowLearn(false); setSelectedCaseId(cId); }}
                                    style={{ fontSize: 10, padding: '2px 8px', borderRadius: 99, background: '#fff', border: `1px solid rgba(156,59,84,0.3)`, color: RX.rose, cursor: cId ? 'pointer' : 'default', fontWeight: 700 }}>
                                    {label}
                                  </button>
                                );
                              })}
                            </div>
                          )}
                        </div>
                      ))}
                    </div>
                  )}
                  {/* 역할 패턴 */}
                  {(b.report.rolePatterns || []).length > 0 && (
                    <div style={{ marginTop: 10 }}>
                      <div style={{ fontSize: 11, fontWeight: 800, color: RX.amber, marginBottom: 6 }}>역할/질환 반복 오류 패턴</div>
                      {b.report.rolePatterns.map((p, i) => (
                        <div key={i} style={{ background: RX.amberBg, border: `1px solid rgba(154,106,20,0.15)`, borderRadius: 6, padding: '6px 10px', marginBottom: 4 }}>
                          <div style={{ fontSize: 11, fontWeight: 700, color: RX.ink }}>{p.pattern} <span style={{ color: RX.faint, fontWeight: 400 }}>({p.frequency}건)</span></div>
                          <div style={{ fontSize: 11, color: RX.sub, marginTop: 2 }}>{p.actionableStep}</div>
                          {(p.caseIndices || []).length > 0 && (
                            <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap', marginTop: 5 }}>
                              {p.caseIndices.map(idx => {
                                const cId = (b.caseIds || [])[idx - 1];
                                const c = cId ? cases.find(x => x.id === cId) : null;
                                const label = c ? ((c.matchedCvDrugs || [])[0]?.displayName || (c.rawDrugs || [])[0]?.rawName || `케이스 ${idx}`) : `케이스 ${idx}`;
                                return (
                                  <button key={idx} onClick={() => { setShowLearn(false); setSelectedCaseId(cId); }}
                                    style={{ fontSize: 10, padding: '2px 8px', borderRadius: 99, background: '#fff', border: `1px solid rgba(154,106,20,0.3)`, color: RX.amber, cursor: cId ? 'pointer' : 'default', fontWeight: 700 }}>
                                    {label}
                                  </button>
                                );
                              })}
                            </div>
                          )}
                        </div>
                      ))}
                    </div>
                  )}
                </div>
              )}
            </div>
          ))}
        </div>
      )}

      {/* 반복 사례 카드 */}
      {repeatCases.length > 0 && (
        <div style={{ background: RX.amberBg, borderBottom: `1px solid rgba(154,106,20,0.15)`, padding: '10px 14px' }}>
          <div style={{ fontSize: 11, fontWeight: 800, color: RX.amber, marginBottom: 6 }}>반복 사례</div>
          <div style={{ display: 'flex', gap: 7, overflowX: 'auto', paddingBottom: 2 }}>
            {repeatCases.map(([sig, cs]) => {
              const top = cs[0];
              const label = (top.matchedCvDrugs || []).slice(0, 2).map(d => d.displayName || d.brand || '').filter(Boolean).join(' + ') || sig.slice(0, 8);
              return (
                <button
                  key={sig}
                  onClick={() => { setSourceFilter('all'); setStatusFilter('all'); }}
                  style={{ flexShrink: 0, padding: '6px 10px', borderRadius: 8, background: '#fff', border: `1px solid rgba(154,106,20,0.25)`, font: 'inherit', fontSize: 11, fontWeight: 700, color: RX.amber, cursor: 'pointer' }}
                >
                  {label} <span style={{ color: RX.faint, fontWeight: 400 }}>({cs.length}건)</span>
                </button>
              );
            })}
          </div>
        </div>
      )}

      {/* 개선 작업함 패널 */}
      {showImprovement && (
        <div style={{ background: RX.tealTint, borderBottom: `1px solid ${RX.tealLine}`, padding: '12px 14px' }}>
          <div style={{ fontSize: 12, fontWeight: 800, color: RX.tealDark, marginBottom: 8 }}>개선 작업함 — 승인된 항목 ({approvedCases.length}건)</div>
          {approvedCases.length === 0 ? (
            <div style={{ fontSize: 12, color: RX.faint }}>승인된 항목이 없습니다.</div>
          ) : approvedCases.map(c => (
            <div key={c.id} style={{ background: '#fff', border: `1px solid ${RX.tealLine}`, borderRadius: 8, padding: '10px 12px', marginBottom: 8 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 6 }}>
                <span style={{ fontSize: 11, fontWeight: 700, color: RX.ink }}>
                  {(c.matchedCvDrugs || []).slice(0, 2).map(d => d.displayName || d.brand || '').filter(Boolean).join(' + ') || '약물 정보 없음'}
                </span>
                <span style={{ fontSize: 10, color: RX.faint }}>{fmtTs(c.ownerDecisionAt)}</span>
              </div>
              {generatedPrompts[c.id] ? (
                <div>
                  <div style={{ fontSize: 11, color: RX.ink, lineHeight: 1.5, marginBottom: 6, background: RX.shell, borderRadius: 6, padding: '7px 10px' }}>{generatedPrompts[c.id]}</div>
                  <button onClick={() => {
                    navigator.clipboard && navigator.clipboard.writeText(generatedPrompts[c.id])
                      .then(() => alert('클립보드에 복사됨'))
                      .catch(() => alert(generatedPrompts[c.id]));
                  }} style={{ fontSize: 11, color: RX.teal, background: 'none', border: `1px solid ${RX.tealLine}`, borderRadius: 5, padding: '4px 9px', cursor: 'pointer', fontWeight: 700 }}>
                    복사
                  </button>
                </div>
              ) : (
                <button
                  onClick={() => handleGeneratePrompt(c)}
                  disabled={!c.aiReview}
                  style={{ fontSize: 11, color: RX.teal, background: 'none', border: `1px solid ${RX.tealLine}`, borderRadius: 5, padding: '4px 9px', cursor: c.aiReview ? 'pointer' : 'default', opacity: c.aiReview ? 1 : 0.5, fontWeight: 700 }}
                >
                  프롬프트 생성
                </button>
              )}
            </div>
          ))}
        </div>
      )}

      {/* 케이스 목록 */}
      {loading ? (
        <div style={{ padding: 24, textAlign: 'center', fontSize: 13, color: RX.faint }}>로딩 중...</div>
      ) : error ? (
        <div style={{ padding: 16, background: RX.roseBg, margin: 12, borderRadius: 8, fontSize: 12, color: RX.rose }}>{error}</div>
      ) : filteredCases.length === 0 ? (
        <div style={{ padding: 24, textAlign: 'center', fontSize: 13, color: RX.faint }}>
          {cases.length === 0 ? 'OCR 분석 기록이 없습니다. OCR을 실행하면 자동으로 저장됩니다.' : '선택한 필터에 해당하는 항목이 없습니다.'}
        </div>
      ) : (
        <div style={{ background: '#fff', borderRadius: 0 }}>
          {filteredCases.map(c => (
            <CaseListItem
              key={c.id}
              c={c}
              selected={c.id === selectedCaseId}
              onClick={() => setSelectedCaseId(c.id)}
            />
          ))}
        </div>
      )}
    </div>
  );
}

window.AppAdminOcr = AppAdminOcr;
