// 그란데 독서타임 — 관리자 (책 목록 / 책 등록 / 구성원)
// 디자인: 3탭 + 어드민 배너
// 운영 통합: localStorage CRUD + /api/book-search 자동 보강 + 시드 초기화

const Admin = ({ books, setBooks, members, stats, goHome }) => {
  const t = window.tokens;
  const store = window.bookStore;
  const [tab, setTab] = React.useState('books');
  const [editingId, setEditingId] = React.useState(null);

  const today = (() => {
    const d = new Date();
    return `${d.getFullYear()}.${String(d.getMonth() + 1).padStart(2, '0')}.${String(d.getDate()).padStart(2, '0')}`;
  })();

  const emptyForm = {
    readers: '', quoteBy: '',
    title: '', author: '', publisher: '',
    pages: '', pagesRead: '', done: false,
    genre: '', quote: '', role: '', date: today, thumbnail: '',
  };
  const [form, setForm] = React.useState(emptyForm);
  const [error, setError] = React.useState('');
  const [searching, setSearching] = React.useState(false);
  const [searchResults, setSearchResults] = React.useState(null);
  const [searchError, setSearchError] = React.useState('');

  // === 스레드 붙여넣기 ===
  const [pasteText, setPasteText] = React.useState('');
  const [pasteEntries, setPasteEntries] = React.useState(null);
  const [pasteError, setPasteError] = React.useState('');
  const [importing, setImporting] = React.useState(false);
  const [importProgress, setImportProgress] = React.useState({ done: 0, total: 0 });

  // === 원정대 도끼꾼 신청 현황 (Apps Script 웹앱 → 시트, 기기 무관 연동) ===
  const [applicants, setApplicants] = React.useState(null);
  const [applyLoading, setApplyLoading] = React.useState(false);
  const [applyErr, setApplyErr] = React.useState('');
  const expCfg = window.expeditionConfig || {};
  const loadApplicants = () => {
    const api = window.expeditionApi;
    if (!api || !api.isConfigured()) { setApplyErr('unconfigured'); setApplicants([]); return; }
    setApplyLoading(true); setApplyErr('');
    api.list()
      .then(res => setApplicants((res && res.applicants) || []))
      .catch(() => setApplyErr('신청 목록을 불러오지 못했어요. 잠시 후 새로고침해주세요.'))
      .finally(() => setApplyLoading(false));
  };
  React.useEffect(() => { if (tab === 'apply' && applicants === null) loadApplicants(); }, [tab]);

  const onChange = (k) => (e) => setForm({ ...form, [k]: e.target.value });

  // 책 메타 자동 보강 — /api/book-search (카카오 + 알라딘 + Google Books)
  const lookupBook = async () => {
    const q = form.title.trim();
    if (q.length < 2) {
      setSearchError('책 제목을 2자 이상 입력해주세요.');
      return;
    }
    setSearching(true);
    setSearchError('');
    setSearchResults(null);
    try {
      const r = await fetch(`/api/book-search?q=${encodeURIComponent(q)}`);
      const data = await r.json();
      if (!r.ok) {
        setSearchError(data.error || '검색 실패');
        return;
      }
      const docs = (data.documents || []).slice(0, 5);
      if (docs.length === 0) {
        setSearchError('검색 결과가 없어요. 제목을 다듬어 다시 시도해보세요.');
        return;
      }
      setSearchResults(docs);
    } catch (e) {
      setSearchError(`네트워크 오류: ${e.message}`);
    } finally {
      setSearching(false);
    }
  };

  const applySearchResult = (doc) => {
    setForm({
      ...form,
      title: doc.title || form.title,
      author: (doc.authors || []).join(', '),
      publisher: doc.publisher || form.publisher,
      pages: doc.pages ? String(doc.pages) : form.pages,
      genre: doc.genre || form.genre,
      thumbnail: doc.thumbnail || form.thumbnail || '',
    });
    setSearchResults(null);
    setSearchError('');
  };

  const submit = (e) => {
    e.preventDefault();
    const readersList = form.readers.split(',').map(s => s.trim()).filter(Boolean);
    if (readersList.length === 0 || !form.title.trim()) {
      setError('공유자와 책 제목은 필수예요. 공유자가 여러 명이면 콤마로 구분해주세요.');
      return;
    }
    setError('');
    const quote = form.quote.trim();
    const quoteBy = form.quoteBy.trim() || (quote ? readersList[0] : '');
    const pagesNum = Number(form.pages) || 0;
    // pagesRead 비우면 pages로 자동 채움(완독 가정). 명시하면 그 값으로 진행중 표시
    const pagesReadStr = String(form.pagesRead).trim();
    const pagesReadNum = pagesReadStr === '' ? pagesNum : (Number(pagesReadStr) || 0);
    const payload = {
      title: form.title.trim(),
      readers: readersList,
      reader: readersList[0],          // legacy mirror
      quoteBy,
      author: form.author.trim(),
      publisher: form.publisher.trim(),
      pages: pagesNum,
      pagesRead: pagesReadNum,
      done: form.done === true,
      genre: form.genre.trim(),
      quote,
      role: form.role.trim(),
      date: form.date.trim() || today,
      thumbnail: form.thumbnail || '',
    };
    if (editingId != null) {
      setBooks(books.map(b => b.id === editingId ? { ...b, ...payload } : b));
      setEditingId(null);
    } else {
      setBooks([...books, { id: store.nextId(books), ...payload }]);
    }
    setForm(emptyForm);
    setTab('books');
  };

  const startEdit = (b) => {
    setEditingId(b.id);
    const readers = (b.readers && b.readers.length > 0) ? b.readers : (b.reader ? [b.reader] : []);
    setForm({
      readers: readers.join(', '),
      quoteBy: b.quoteBy || '',
      title: b.title || '',
      author: b.author || '',
      publisher: b.publisher || '',
      pages: b.pages ? String(b.pages) : '',
      pagesRead: (b.pagesRead != null && b.pagesRead !== '') ? String(b.pagesRead) : '',
      done: b.done === true,
      genre: b.genre || '',
      quote: b.quote || '',
      role: b.role || '',
      date: b.date || today,
      thumbnail: b.thumbnail || '',
    });
    setTab('add');
  };

  const cancelEdit = () => {
    setEditingId(null);
    setForm(emptyForm);
    setError('');
    setSearchResults(null);
    setSearchError('');
  };

  const remove = (id) => {
    if (!window.confirm('이 책 기록을 삭제할까요?')) return;
    setBooks(books.filter(b => b.id !== id));
    if (editingId === id) cancelEdit();
  };

  const resetAll = () => {
    if (!window.confirm('모든 입력을 지우고 초기 시드 6권으로 되돌릴까요?')) return;
    setBooks(store.resetToSeed());
    cancelEdit();
  };

  // === 슬랙 스레드 파서 ===
  // 슬랙 데스크탑 복붙 포맷:
  //   "Kyury.Kim (김규리)  [오전 9:03]\n메시지..."
  //   "IMG_9064 jinsil.kim (김진실)  [오전 9:05]\n메시지..."
  // 한 줄에 (한글이름) + [오전/오후 HH:MM] 함께 옴. 첨부(IMG_*, *.jpg)는 다음 화자 머리에 붙기도 함.

  const extractTitleCandidate = (msg) => {
    // 1순위: 책 제목 전용 마커 (<>, 《》, 「」, 『』, [])
    // 따옴표("/'/")는 인용문 마커로 양보 — extractQuote에서 처리
    const patterns = [
      /<((?!https?:)[^<>\n]{2,80})>/,
      /《([^》\n]{2,80})》/,
      /「([^」\n]{2,80})」/,
      /『([^』\n]{2,80})』/,
      /\[([^\[\]\n]{2,80})\]/,
    ];
    for (const re of patterns) {
      const m = msg.match(re);
      if (m) return m[1].trim();
    }
    // 2순위: 인용문("...") 영역을 제거한 뒤 첫 문장 + 어미 정리
    let cleaned = msg
      .replace(/"[^"\n]{10,}"/g, ' ')  // smart quotes 인용문 제거
      .replace(/"[^"\n]{10,}"/g, ' ')  // straight quotes 인용문 제거
      .split(/[.!?…]\s/)[0]
      .replace(/[.!?~…]+$/, '')
      .replace(/\s*(?:읽고\s?있어요|읽고\s?있습니다|읽는\s?중이?에?요?|읽었어요|읽었습니다|추천해요|추천합니다|추천이?에?요?|읽어\s?봤어요|읽는다|읽음|시작해보겠습니다|시작해보겠습니당|시작합니다|시작할게요|시작해요|완독하고|일고\s?있어요|읽어요|읽어보았습니다|읽어봤어요)$/, '')
      .replace(/^[—\-•·]\s*/, '')
      .trim();
    if (cleaned.length > 60) cleaned = cleaned.slice(0, 60).trim();
    return cleaned;
  };

  // 페이지 추출 — "120페이지", "120쪽", "120p" 등 첫 매치
  // "p.50~120" 같은 범위는 끝 페이지(도달 페이지) 사용
  const extractPagesRead = (msg) => {
    // 범위 우선: "50~120쪽" / "p.50-120" / "50-120 페이지"
    const range = msg.match(/(\d{1,4})\s*[~\-–]\s*(\d{1,4})\s*(?:페이지|쪽|p\b)/i);
    if (range) return Number(range[2]) || 0;
    // 단일: "120페이지" / "120쪽" / "120p" / "p.120"
    const single = msg.match(/(?:p\.?\s*)?(\d{1,4})\s*(?:페이지|쪽|p\b)/i);
    if (single) return Number(single[1]) || 0;
    return 0;
  };

  // 인용문 추출 — 따옴표("/'/「) 안 10자 이상. 책 제목과 안 겹치게
  const extractQuote = (msg, titleCandidate) => {
    const patterns = [
      /"([^"\n]{10,200})"/,
      /"([^"\n]{10,200})"/,
      /'([^'\n]{10,200})'/,
      /「([^」\n]{10,200})」/,
    ];
    for (const re of patterns) {
      const m = msg.match(re);
      if (m) {
        const text = m[1].trim();
        // 책 제목으로 이미 잡힌 텍스트면 스킵
        if (titleCandidate && text === titleCandidate) continue;
        return text;
      }
    }
    return '';
  };

  const cleanMessage = (raw) => {
    let s = raw;
    // 슬랙 이모지 코드 :xxx: 제거
    s = s.replace(/:[a-z0-9_+\-]+:/gi, ' ');
    // 끝에 다음 화자의 첨부 헤더가 묻어 있으면 제거 (IMG_xxxx EnglishName / filename.ext EnglishName)
    s = s.replace(/\s*(?:IMG_\w+|[\w]+\.(?:jpg|jpeg|png|gif|heic))(?:\s+[A-Za-z][\w.\s]*?)?\s*$/i, '');
    // 줄바꿈·공백 정리
    s = s.replace(/\s+/g, ' ').trim();
    return s;
  };

  const parseSlackText = (text) => {
    // 헤더: (한글이름) [오전/오후 HH:MM]
    const headerRe = /\(([가-힣][가-힣\s]*)\)\s*\[(?:오전|오후)\s*\d{1,2}:\d{2}\]/g;
    const matches = [];
    let m;
    while ((m = headerRe.exec(text)) !== null) {
      matches.push({
        name: m[1].replace(/\s+/g, ''),
        nameStart: m.index,
        headerEnd: m.index + m[0].length,
      });
    }
    if (matches.length === 0) return [];

    const out = [];
    for (let i = 0; i < matches.length; i++) {
      const here = matches[i];
      const nextStart = i + 1 < matches.length ? matches[i + 1].nameStart : text.length;
      const slice = text.slice(here.headerEnd, nextStart);
      const rawMessage = cleanMessage(slice);
      if (!rawMessage) continue;
      const titleCandidate = extractTitleCandidate(rawMessage);
      const pagesRead = extractPagesRead(rawMessage);
      const quote = extractQuote(rawMessage, titleCandidate);
      out.push({
        name: here.name,
        titleCandidate,
        pagesRead: pagesRead ? String(pagesRead) : '',
        quote,
        rawMessage,
        selected: true,
      });
    }
    return out;
  };

  const analyzePaste = () => {
    setPasteError('');
    const text = pasteText.trim();
    if (!text) { setPasteError('붙여넣은 텍스트가 비어 있어요.'); return; }
    const entries = parseSlackText(text);
    if (entries.length === 0) {
      setPasteError('이름·시간 패턴을 못 찾았어요. 슬랙 스레드를 그대로 드래그 복사해서 붙여보세요.');
      setPasteEntries([]);
      return;
    }
    setPasteEntries(entries);
  };

  const updateEntry = (idx, key, val) => {
    const next = [...pasteEntries];
    next[idx] = { ...next[idx], [key]: val };
    setPasteEntries(next);
  };

  const importEntries = async () => {
    const toAdd = (pasteEntries || []).filter(e => e.selected && e.titleCandidate.trim() && e.name.trim());
    if (toAdd.length === 0) {
      setPasteError('추가할 항목이 없어요. 체크박스·이름·책 제목을 확인해주세요.');
      return;
    }
    setPasteError('');
    setImporting(true);
    setImportProgress({ done: 0, total: toAdd.length });

    const newBooks = [];
    let id = store.nextId(books);
    for (let k = 0; k < toAdd.length; k++) {
      const p = toAdd[k];
      let enriched = {};
      try {
        const r = await fetch(`/api/book-search?q=${encodeURIComponent(p.titleCandidate)}`);
        if (r.ok) {
          const data = await r.json();
          const top = (data.documents || [])[0];
          if (top) {
            enriched = {
              title: top.title || p.titleCandidate,
              author: (top.authors || []).join(', '),
              publisher: top.publisher || '',
              pages: top.pages || 0,
              genre: top.genre || '',
              thumbnail: top.thumbnail || '',
            };
          }
        }
      } catch (e) { /* 카카오 실패해도 진행 */ }

      const readerName = p.name.trim();
      const pagesTotal = Number(enriched.pages) || 0;
      // 사용자가 입력한 "오늘 N페이지"가 있으면 그 값(누적 도달점), 없으면 완독 가정
      const userPagesRead = Number(p.pagesRead) || 0;
      const pagesRead = userPagesRead > 0 ? userPagesRead : pagesTotal;
      const quote = (p.quote || '').trim();
      newBooks.push({
        id: id++,
        title: enriched.title || p.titleCandidate.trim(),
        author: enriched.author || '',
        publisher: enriched.publisher || '',
        pages: pagesTotal,
        pagesRead,
        done: false,
        genre: enriched.genre || '',
        readers: [readerName],
        reader: readerName,
        role: '',
        date: today,
        quote,
        quoteBy: quote ? readerName : '',
        thumbnail: enriched.thumbnail || '',
      });
      setImportProgress({ done: k + 1, total: toAdd.length });
    }

    setBooks([...books, ...newBooks]);
    setImporting(false);
    setImportProgress({ done: 0, total: 0 });
    setPasteText('');
    setPasteEntries(null);
    setTab('books');
  };

  const exportCsv = () => {
    const header = ['id', 'title', 'author', 'publisher', 'pages', 'pagesRead', 'done', 'genre', 'readers', 'role', 'date', 'quote', 'quoteBy'];
    const escape = (v) => {
      const s = String(v ?? '');
      return /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
    };
    const cellFor = (b, k) => {
      if (k === 'readers') {
        const rs = (b.readers && b.readers.length > 0) ? b.readers : (b.reader ? [b.reader] : []);
        return rs.join(', ');
      }
      return b[k];
    };
    const rows = [
      header.join(','),
      ...books.map(b => header.map(k => escape(cellFor(b, k))).join(',')),
    ];
    const blob = new Blob(['﻿' + rows.join('\n')], { type: 'text/csv;charset=utf-8' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `grande-reading-${today.replace(/\./g, '')}.csv`;
    document.body.appendChild(a);
    a.click();
    a.remove();
    URL.revokeObjectURL(url);
  };

  const inputBase = {
    width: '100%', padding: '12px 14px',
    border: `1px solid ${t.color.ruleSoft}`, background: '#fff',
    fontSize: 14, fontFamily: t.font.sans, outline: 'none',
    color: t.color.ink, letterSpacing: '-0.01em',
  };
  const labelBase = {
    fontSize: 11, fontWeight: 600, color: t.color.muted,
    letterSpacing: '0.08em', textTransform: 'uppercase', marginBottom: 8,
    display: 'block',
  };

  return (
    <div style={{ padding: '40px 64px 56px', minHeight: 816, display: 'flex', flexDirection: 'column', background: '#FAFAF8' }}>
      {/* Admin banner */}
      <div style={{
        display: 'flex', alignItems: 'center', gap: 12,
        padding: '10px 16px', background: t.color.ink, color: '#fff',
        marginBottom: 24, alignSelf: 'flex-start',
      }}>
        <div style={{ width: 6, height: 6, borderRadius: '50%', background: '#7CD992' }} />
        <div style={{ fontSize: 11, fontWeight: 600, letterSpacing: '0.18em', textTransform: 'uppercase' }}>
          Admin Mode
        </div>
        <div onClick={goHome} style={{ fontSize: 11, color: 'rgba(255,255,255,0.6)', cursor: 'pointer', marginLeft: 12, letterSpacing: '0.06em' }}>
          ← 일반 화면으로
        </div>
      </div>

      {/* Header */}
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', marginBottom: 28 }}>
        <div>
          <div style={{ fontSize: 14, fontWeight: 500, color: t.color.muted, marginBottom: 10 }}>관리자</div>
          <div style={{ fontSize: 48, fontWeight: 700, letterSpacing: '-0.04em', lineHeight: 1 }}>
            책장 관리
          </div>
        </div>
        <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
          <div onClick={exportCsv} style={{ padding: '10px 18px', border: `1px solid ${t.color.ruleSoft}`, fontSize: 13, fontWeight: 500, cursor: 'pointer', background: '#fff' }}>
            CSV 내보내기
          </div>
          <div onClick={resetAll} style={{ padding: '10px 18px', border: `1px solid ${t.color.ruleSoft}`, fontSize: 13, fontWeight: 500, cursor: 'pointer', background: '#fff', color: t.color.muted }}>
            시드 초기화
          </div>
          <div onClick={() => { cancelEdit(); setTab('add'); }} style={{ padding: '10px 18px', background: t.color.ink, color: '#fff', fontSize: 13, fontWeight: 600, cursor: 'pointer' }}>
            + 새 책 등록
          </div>
        </div>
      </div>

      {/* Tabs */}
      <div style={{ display: 'flex', gap: 0, borderBottom: `1px solid ${t.color.ruleSoft}`, marginBottom: 24 }}>
        {[
          { id: 'books', label: '책 목록', count: books.length },
          { id: 'add', label: editingId != null ? '책 수정' : '책 등록' },
          { id: 'paste', label: '스레드 붙여넣기' },
          { id: 'members', label: '구성원', count: stats.members },
          { id: 'apply', label: '원정대 신청', count: applicants ? applicants.filter(a => String(a.month) === String(expCfg.currentMonth)).length : undefined },
        ].map(x => (
          <div key={x.id} onClick={() => setTab(x.id)} style={{
            padding: '12px 20px', fontSize: 13, fontWeight: 600, cursor: 'pointer',
            color: tab === x.id ? t.color.ink : t.color.muted,
            borderBottom: tab === x.id ? `2px solid ${t.color.ink}` : '2px solid transparent',
            marginBottom: -1,
            display: 'flex', alignItems: 'center', gap: 8,
          }}>
            {x.label}
            {x.count !== undefined && (
              <span style={{ fontSize: 11, color: t.color.muted, fontWeight: 500 }}>{x.count}</span>
            )}
          </div>
        ))}
      </div>

      {/* Body */}
      <div style={{ flex: 1, paddingBottom: 40 }}>
        {tab === 'books' && (
          <div style={{ background: '#fff', border: `1px solid ${t.color.ruleSoft}` }}>
            <div style={{
              display: 'grid', gridTemplateColumns: '40px 1fr 140px 110px 90px 70px 80px 90px',
              gap: 14, padding: '12px 18px',
              borderBottom: `1px solid ${t.color.ruleSoft}`,
              fontSize: 11, fontWeight: 600, color: t.color.muted, letterSpacing: '0.08em', textTransform: 'uppercase',
              background: '#FAFAF8',
            }}>
              <div>#</div>
              <div>제목</div>
              <div>저자</div>
              <div>출판사</div>
              <div>장르</div>
              <div style={{ textAlign: 'right' }}>쪽수</div>
              <div>공유자</div>
              <div style={{ textAlign: 'right' }}>액션</div>
            </div>
            {books.length === 0 && (
              <div style={{ padding: '24px 18px', color: t.color.muted, fontSize: 13 }}>등록된 책이 없어요.</div>
            )}
            {books.map((b, i) => (
              <div key={b.id} style={{
                display: 'grid', gridTemplateColumns: '40px 1fr 140px 110px 90px 70px 80px 90px',
                gap: 14, padding: '14px 18px',
                borderBottom: i === books.length - 1 ? 'none' : `1px solid ${t.color.ruleSoft}`,
                fontSize: 13, alignItems: 'center',
                background: editingId === b.id ? '#FFFBEB' : '#fff',
              }}>
                <div style={{ fontSize: 11, color: t.color.mutedLight, fontVariantNumeric: 'tabular-nums' }}>{String(i + 1).padStart(2, '0')}</div>
                <div style={{ fontWeight: 600, letterSpacing: '-0.01em', minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{b.title || '(제목 미입력)'}</div>
                <div style={{ color: t.color.inkSoft, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{b.author || '—'}</div>
                <div style={{ color: t.color.muted, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{b.publisher || '—'}</div>
                <div>
                  <span style={{ fontSize: 11, padding: '3px 8px', background: '#F4F4F2', letterSpacing: '-0.01em' }}>{b.genre || '미분류'}</span>
                </div>
                <div style={{ textAlign: 'right', fontVariantNumeric: 'tabular-nums', color: t.color.inkSoft }}>
                  {(() => {
                    const total = b.pages || 0;
                    const read = (b.pagesRead != null && b.pagesRead !== '') ? Number(b.pagesRead) : total;
                    if (!total) return '—';
                    if (read < total) return <span><span style={{ color: t.color.blue, fontWeight: 600 }}>{read}</span><span style={{ color: t.color.mutedLight }}>/{total}</span></span>;
                    return total;
                  })()}
                </div>
                <div style={{ color: t.color.inkSoft, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={(b.readers || []).join(', ')}>
                  {(b.readers && b.readers.length > 0) ? b.readers.join(', ') : (b.reader || '—')}
                </div>
                <div style={{ display: 'flex', gap: 10, justifyContent: 'flex-end' }}>
                  <div onClick={() => startEdit(b)} style={{ fontSize: 12, fontWeight: 600, cursor: 'pointer', color: t.color.ink }}>편집</div>
                  <div onClick={() => remove(b.id)} style={{ fontSize: 12, color: '#C53030', cursor: 'pointer' }}>삭제</div>
                </div>
              </div>
            ))}
          </div>
        )}

        {tab === 'add' && (
          <form onSubmit={submit} style={{ background: '#fff', border: `1px solid ${t.color.ruleSoft}`, padding: 32, maxWidth: 760 }}>
            <div style={{ fontSize: 18, fontWeight: 700, letterSpacing: '-0.02em', marginBottom: 6 }}>
              {editingId != null ? '책 정보 수정' : '새 책 등록'}
            </div>
            <div style={{ fontSize: 13, color: t.color.muted, marginBottom: 28 }}>
              이름과 책 제목만 채워도 저장돼요. 제목 입력 후 <b>🔍 자동 보강</b>을 누르면 저자·페이지·장르가 자동으로 채워집니다.
            </div>

            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 20 }}>
              {/* 제목 + 자동 보강 버튼 */}
              <div style={{ gridColumn: 'span 2' }}>
                <label style={labelBase}>책 제목 *</label>
                <div style={{ display: 'flex', gap: 8 }}>
                  <input value={form.title} onChange={onChange('title')} placeholder="예: 사피엔스" style={{ ...inputBase, flex: 1 }} />
                  <button type="button" onClick={lookupBook} disabled={searching} style={{
                    padding: '0 18px', fontSize: 12, fontWeight: 600, letterSpacing: '-0.01em',
                    color: searching ? t.color.muted : '#fff', background: searching ? '#E2E2E2' : t.color.ink,
                    border: 'none', cursor: searching ? 'wait' : 'pointer',
                    fontFamily: t.font.sans, whiteSpace: 'nowrap',
                  }}>
                    {searching ? '검색중…' : '🔍 자동 보강'}
                  </button>
                </div>
                {searchError && (
                  <div style={{ fontSize: 11, color: '#C53030', marginTop: 6 }}>{searchError}</div>
                )}
                {searchResults && searchResults.length > 0 && (
                  <div style={{
                    marginTop: 8, background: '#fff', border: `1px solid ${t.color.ruleSoft}`,
                    maxHeight: 260, overflow: 'auto',
                  }}>
                    <div style={{ padding: '8px 12px', fontSize: 10, color: t.color.muted, letterSpacing: '0.18em', textTransform: 'uppercase', borderBottom: `1px solid ${t.color.ruleSoft}` }}>
                      검색 결과 — 클릭해서 채우기
                    </div>
                    {searchResults.map((d, i) => (
                      <div key={i} onClick={() => applySearchResult(d)} style={{
                        padding: '10px 12px', cursor: 'pointer',
                        borderBottom: i === searchResults.length - 1 ? 'none' : `1px solid ${t.color.ruleSoft}`,
                        fontSize: 12, lineHeight: 1.4,
                      }}
                      onMouseEnter={(e) => e.currentTarget.style.background = t.color.blueSoft}
                      onMouseLeave={(e) => e.currentTarget.style.background = '#fff'}>
                        <div style={{ fontWeight: 600, color: t.color.ink, marginBottom: 2 }}>{d.title}</div>
                        <div style={{ color: t.color.muted, fontSize: 11 }}>
                          {(d.authors || []).join(', ') || '저자 미상'} · {d.publisher || '출판사 미상'}
                        </div>
                        <div style={{ marginTop: 4, display: 'flex', gap: 6 }}>
                          {d.pages ? (
                            <span style={{ fontSize: 10, padding: '2px 6px', background: '#E8E8E6', color: t.color.ink, fontWeight: 500 }}>{d.pages}쪽</span>
                          ) : (
                            <span style={{ fontSize: 10, padding: '2px 6px', background: '#F4F4F2', color: t.color.mutedLight }}>페이지 없음</span>
                          )}
                          {d.genre ? (
                            <span style={{ fontSize: 10, padding: '2px 6px', background: t.color.blue, color: '#fff', fontWeight: 500 }}>{d.genre}</span>
                          ) : (
                            <span style={{ fontSize: 10, padding: '2px 6px', background: '#F4F4F2', color: t.color.mutedLight }}>장르 미추정</span>
                          )}
                        </div>
                      </div>
                    ))}
                  </div>
                )}
              </div>

              <div>
                <label style={labelBase}>저자</label>
                <input value={form.author} onChange={onChange('author')} placeholder="예: 유발 하라리" style={inputBase} />
              </div>
              <div>
                <label style={labelBase}>출판사</label>
                <input value={form.publisher} onChange={onChange('publisher')} placeholder="예: 김영사" style={inputBase} />
              </div>
              <div>
                <label style={labelBase}>전체 페이지 수</label>
                <input type="number" min="0" value={form.pages} onChange={onChange('pages')} placeholder="예: 636" style={inputBase} />
              </div>
              <div>
                <label style={labelBase}>읽은 페이지 (누적)</label>
                <input type="number" min="0" value={form.pagesRead} onChange={onChange('pagesRead')} placeholder="진행중이면 누적값 입력 · 비우면 미표시" style={inputBase} />
              </div>
              <div style={{ gridColumn: 'span 2' }}>
                <label style={{ ...labelBase, display: 'flex', alignItems: 'center', gap: 8, cursor: 'pointer' }}>
                  <input type="checkbox" checked={form.done === true} onChange={(e) => setForm(f => ({ ...f, done: e.target.checked }))} style={{ width: 16, height: 16, cursor: 'pointer' }} />
                  완독 (체크하면 서재에 '완독' 배지 표시)
                </label>
              </div>
              <div>
                <label style={labelBase}>장르</label>
                <input value={form.genre} onChange={onChange('genre')} placeholder="인문 / 에세이 / 경제경영 / 과학 / 자기계발 / 소설" style={inputBase} />
              </div>
              <div>
                <label style={labelBase}>공유자 *</label>
                <input value={form.readers} onChange={onChange('readers')} placeholder="예: 김규리, 김소희 (여러 명은 콤마로)" style={inputBase} />
              </div>
              <div>
                <label style={labelBase}>팀 (선택)</label>
                <input value={form.role} onChange={onChange('role')} placeholder="예: 피플팀" style={inputBase} />
              </div>
              <div style={{ gridColumn: 'span 2' }}>
                <label style={labelBase}>날짜</label>
                <input value={form.date} onChange={onChange('date')} placeholder="2026.04.24" style={inputBase} />
              </div>
              <div style={{ gridColumn: 'span 2' }}>
                <label style={labelBase}>인용구 (선택)</label>
                <textarea value={form.quote} onChange={onChange('quote')} placeholder="책에서 가장 인상 깊었던 한 문장" style={{
                  ...inputBase, minHeight: 96, resize: 'vertical',
                }} />
              </div>
              <div style={{ gridColumn: 'span 2' }}>
                <label style={labelBase}>인용문 작성자 (선택, 비우면 첫 번째 공유자)</label>
                <input value={form.quoteBy} onChange={onChange('quoteBy')} placeholder="예: 김소희 — 한 권을 여러 명이 같이 읽었을 때 누가 남긴 인용문인지" style={inputBase} />
              </div>
            </div>

            {error && (
              <div style={{ fontSize: 12, color: '#C53030', marginTop: 16 }}>{error}</div>
            )}

            <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 28, paddingTop: 20, borderTop: `1px solid ${t.color.ruleSoft}` }}>
              {editingId != null && (
                <button type="button" onClick={() => { cancelEdit(); setTab('books'); }} style={{
                  padding: '12px 22px', border: `1px solid ${t.color.ruleSoft}`,
                  fontSize: 13, fontWeight: 500, cursor: 'pointer', background: '#fff',
                  fontFamily: t.font.sans,
                }}>
                  취소
                </button>
              )}
              <button type="submit" style={{
                padding: '12px 22px', background: t.color.ink, color: '#fff',
                fontSize: 13, fontWeight: 600, cursor: 'pointer', border: 'none',
                fontFamily: t.font.sans,
              }}>
                {editingId != null ? '수정 저장' : '등록하기'}
              </button>
            </div>
          </form>
        )}

        {tab === 'paste' && (
          <div style={{ background: '#fff', border: `1px solid ${t.color.ruleSoft}`, padding: 32, maxWidth: 860 }}>
            <div style={{ fontSize: 18, fontWeight: 700, letterSpacing: '-0.02em', marginBottom: 6 }}>
              스레드 통째로 붙여넣기
            </div>
            <div style={{ fontSize: 13, color: t.color.muted, marginBottom: 24, lineHeight: 1.6 }}>
              슬랙 독서타임 스레드를 드래그 복사해서 붙여넣으면 <b>이름·책 제목·읽은 페이지·인용문</b>을 자동 추출해요.<br />
              인식 마커 — 책 제목: <code>《》</code>·<code>「」</code>·<code>『』</code>·<code>&lt;&gt;</code>·<code>[]</code> / 인용문: <code>""</code>·<code>''</code> / 페이지: <code>120페이지</code>·<code>120쪽</code>·<code>120p</code>·<code>50~120쪽</code><br />
              미리보기에서 보정 후 <b>일괄 추가</b>를 누르면 카카오 책 정보(저자·전체 페이지·표지)까지 자동으로 같이 채워져요.
            </div>

            <div style={{ marginBottom: 16 }}>
              <label style={labelBase}>붙여넣을 텍스트</label>
              <textarea
                value={pasteText}
                onChange={(e) => setPasteText(e.target.value)}
                placeholder={'예시\n\nKyury.Kim (김규리)  [오전 9:03]\n《조직문화 통찰》 이번 주 120페이지 읽었어요!\n"어떤 조직은 함께일하고 싶은 사람들이 있다" 이 구절이 좋았어요\nIMG_9064 jinsil.kim (김진실)  [오전 9:05]\n《시대예보 : 경량문명의 탄생》 80쪽까지 진도 나갔습니다'}
                style={{ ...inputBase, minHeight: 220, resize: 'vertical', lineHeight: 1.6 }}
              />
            </div>

            <div style={{ display: 'flex', gap: 8, marginBottom: 20 }}>
              <button type="button" onClick={analyzePaste} disabled={!pasteText.trim()} style={{
                padding: '12px 22px',
                background: pasteText.trim() ? t.color.ink : '#E2E2E2',
                color: pasteText.trim() ? '#fff' : t.color.muted,
                fontSize: 13, fontWeight: 600, border: 'none',
                cursor: pasteText.trim() ? 'pointer' : 'not-allowed',
                fontFamily: t.font.sans,
              }}>
                ✂️ 분리해서 보여줘
              </button>
              {pasteEntries && (
                <button type="button" onClick={() => { setPasteEntries(null); setPasteError(''); }} style={{
                  padding: '12px 22px', background: '#fff', color: t.color.muted,
                  fontSize: 13, fontWeight: 500, cursor: 'pointer',
                  border: `1px solid ${t.color.ruleSoft}`, fontFamily: t.font.sans,
                }}>
                  다시 분석
                </button>
              )}
            </div>

            {pasteError && (
              <div style={{ fontSize: 12, color: '#C53030', marginBottom: 16 }}>{pasteError}</div>
            )}

            {pasteEntries && pasteEntries.length > 0 && (
              <div>
                <div style={{ fontSize: 14, fontWeight: 600, marginBottom: 12, letterSpacing: '-0.01em' }}>
                  ✅ {pasteEntries.length}개 발견 — 체크 항목만 추가됩니다
                </div>
                <div style={{ border: `1px solid ${t.color.ruleSoft}`, background: '#FAFAF8' }}>
                  {pasteEntries.map((e, idx) => (
                    <div key={idx} style={{
                      padding: '14px 16px',
                      borderBottom: idx === pasteEntries.length - 1 ? 'none' : `1px solid ${t.color.ruleSoft}`,
                      background: e.selected ? '#fff' : '#F4F4F2',
                      opacity: e.selected ? 1 : 0.55,
                    }}>
                      <div style={{ display: 'flex', gap: 12, alignItems: 'flex-start' }}>
                        <input
                          type="checkbox"
                          checked={e.selected}
                          onChange={(ev) => updateEntry(idx, 'selected', ev.target.checked)}
                          style={{ marginTop: 10, cursor: 'pointer', width: 16, height: 16 }}
                        />
                        <div style={{ flex: 1, display: 'grid', gridTemplateColumns: '160px 1fr 110px', gap: 10 }}>
                          <div>
                            <label style={{ ...labelBase, marginBottom: 4 }}>공유자</label>
                            <input
                              value={e.name}
                              onChange={(ev) => updateEntry(idx, 'name', ev.target.value)}
                              style={{ ...inputBase, padding: '8px 10px', fontSize: 13 }}
                            />
                          </div>
                          <div>
                            <label style={{ ...labelBase, marginBottom: 4 }}>책 제목 (추정)</label>
                            <input
                              value={e.titleCandidate}
                              onChange={(ev) => updateEntry(idx, 'titleCandidate', ev.target.value)}
                              style={{ ...inputBase, padding: '8px 10px', fontSize: 13 }}
                            />
                          </div>
                          <div>
                            <label style={{ ...labelBase, marginBottom: 4 }}>읽은 페이지</label>
                            <input
                              type="number" min="0"
                              value={e.pagesRead || ''}
                              onChange={(ev) => updateEntry(idx, 'pagesRead', ev.target.value)}
                              placeholder="비우면 완독"
                              style={{ ...inputBase, padding: '8px 10px', fontSize: 13 }}
                            />
                          </div>
                        </div>
                      </div>
                      <div style={{ marginLeft: 30, marginTop: 10 }}>
                        <label style={{ ...labelBase, marginBottom: 4 }}>인용문 (선택)</label>
                        <textarea
                          value={e.quote || ''}
                          onChange={(ev) => updateEntry(idx, 'quote', ev.target.value)}
                          placeholder="추출 못 했으면 비워두거나 직접 붙여넣기"
                          style={{ ...inputBase, padding: '8px 10px', fontSize: 13, minHeight: 44, resize: 'vertical', lineHeight: 1.5 }}
                        />
                      </div>
                      <div style={{ marginLeft: 30, marginTop: 8, fontSize: 11, color: t.color.mutedLight, lineHeight: 1.5 }}>
                        원문: "{e.rawMessage}"
                      </div>
                    </div>
                  ))}
                </div>

                <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 20 }}>
                  <button type="button" onClick={importEntries} disabled={importing} style={{
                    padding: '12px 22px',
                    background: importing ? '#E2E2E2' : t.color.ink,
                    color: importing ? t.color.muted : '#fff',
                    fontSize: 13, fontWeight: 600, border: 'none',
                    cursor: importing ? 'wait' : 'pointer',
                    fontFamily: t.font.sans,
                  }}>
                    {importing
                      ? `추가 중… (${importProgress.done}/${importProgress.total})`
                      : `선택한 ${pasteEntries.filter(e => e.selected).length}권 일괄 추가`}
                  </button>
                </div>
              </div>
            )}
          </div>
        )}

        {tab === 'members' && (
          <div style={{ background: '#fff', border: `1px solid ${t.color.ruleSoft}` }}>
            {members.length === 0 && (
              <div style={{ padding: '24px 20px', color: t.color.muted, fontSize: 13 }}>구성원 데이터가 없어요.</div>
            )}
            {members.map((m, i) => (
              <div key={m.name} style={{
                display: 'grid', gridTemplateColumns: '40px 1fr 120px 80px 90px',
                gap: 16, padding: '16px 20px', alignItems: 'center',
                borderBottom: i === members.length - 1 ? 'none' : `1px solid ${t.color.ruleSoft}`,
                fontSize: 13,
              }}>
                <div style={{ width: 28, height: 28, borderRadius: '50%', background: t.color.ink, color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 11, fontWeight: 600 }}>
                  {m.name[0] || '·'}
                </div>
                <div>
                  <div style={{ fontSize: 14, fontWeight: 700, letterSpacing: '-0.02em' }}>{m.name}</div>
                  <div style={{ fontSize: 11, color: t.color.muted, marginTop: 2 }}>{m.role || '—'}</div>
                </div>
                <div style={{ color: t.color.muted, fontSize: 12 }}>{m.role || '—'}</div>
                <div style={{ textAlign: 'right', fontVariantNumeric: 'tabular-nums', fontWeight: 600 }}>{m.books}권</div>
                <div style={{ textAlign: 'right', fontVariantNumeric: 'tabular-nums', color: t.color.muted }}>{m.pages.toLocaleString('ko-KR')}쪽</div>
              </div>
            ))}
          </div>
        )}

        {tab === 'apply' && (
          <div>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', marginBottom: 16 }}>
              <div>
                <div style={{ fontSize: 18, fontWeight: 700, letterSpacing: '-0.02em' }}>
                  도끼책 원정대 · 도끼꾼 신청 현황
                </div>
                <div style={{ fontSize: 13, color: t.color.muted, marginTop: 6 }}>
                  {expCfg.currentMonth}월 신청{' '}
                  <b style={{ color: t.color.ink }}>
                    {applicants ? applicants.filter(a => String(a.month) === String(expCfg.currentMonth)).length : 0}
                  </b>
                  /{expCfg.maxDoggikkun || 8}명 · 기기 무관 연동 (Apps Script + 시트)
                </div>
              </div>
              <div onClick={() => loadApplicants()} style={{
                padding: '10px 18px', border: `1px solid ${t.color.ruleSoft}`, fontSize: 13,
                fontWeight: 500, cursor: applyLoading ? 'wait' : 'pointer', background: '#fff',
                color: applyLoading ? t.color.muted : t.color.ink,
              }}>
                {applyLoading ? '불러오는 중…' : '↻ 새로고침'}
              </div>
            </div>

            {applyErr === 'unconfigured' ? (
              <div style={{ padding: '24px 20px', background: '#FFFBEB', border: `1px solid ${t.color.ruleSoft}`, fontSize: 13, color: t.color.inkSoft, lineHeight: 1.7 }}>
                아직 <b>신청 연동(Apps Script)이 설정되기 전</b>이에요.<br />
                <code>web/apps-script/expedition-apply.gs</code> 를 웹앱으로 배포한 뒤,{' '}
                나온 <code>/exec</code> URL을 <code>components/expedition.jsx</code> 의{' '}
                <code>EXPEDITION.applyEndpoint</code> 에 붙여넣으면 여기에서 신청이 보여요.
              </div>
            ) : applyErr ? (
              <div style={{ padding: '20px', border: `1px solid ${t.color.ruleSoft}`, fontSize: 13, color: '#C53030', background: '#fff' }}>{applyErr}</div>
            ) : (
              <div style={{ background: '#fff', border: `1px solid ${t.color.ruleSoft}` }}>
                <div style={{
                  display: 'grid', gridTemplateColumns: '48px 70px 1fr 180px',
                  gap: 14, padding: '12px 18px', borderBottom: `1px solid ${t.color.ruleSoft}`,
                  fontSize: 11, fontWeight: 600, color: t.color.muted, letterSpacing: '0.08em',
                  textTransform: 'uppercase', background: '#FAFAF8',
                }}>
                  <div>순번</div>
                  <div>기간</div>
                  <div>이름</div>
                  <div>신청 시각</div>
                </div>
                {(applicants || []).length === 0 && (
                  <div style={{ padding: '24px 18px', color: t.color.muted, fontSize: 13 }}>
                    {applyLoading ? '불러오는 중…' : '아직 신청자가 없어요.'}
                  </div>
                )}
                {(applicants || [])
                  .map((a, idx) => ({ ...a, _idx: idx }))
                  .sort((a, b) => String(a.at).localeCompare(String(b.at)))
                  .map((a, i, arr) => (
                    <div key={a._idx} style={{
                      display: 'grid', gridTemplateColumns: '48px 70px 1fr 180px',
                      gap: 14, padding: '14px 18px', alignItems: 'center', fontSize: 13,
                      borderBottom: i === arr.length - 1 ? 'none' : `1px solid ${t.color.ruleSoft}`,
                      background: String(a.month) === String(expCfg.currentMonth) ? '#fff' : '#FAFAFA',
                    }}>
                      <div style={{ fontSize: 12, color: t.color.mutedLight, fontVariantNumeric: 'tabular-nums' }}>{String(i + 1).padStart(2, '0')}</div>
                      <div style={{ color: t.color.muted, fontVariantNumeric: 'tabular-nums' }}>{a.period || (a.month ? a.month + '월' : '—')}</div>
                      <div style={{ fontWeight: 700, letterSpacing: '-0.02em' }}>{a.name}</div>
                      <div style={{ color: t.color.inkSoft, fontVariantNumeric: 'tabular-nums', fontSize: 12 }}>{a.at || '—'}</div>
                    </div>
                  ))}
              </div>
            )}
          </div>
        )}
      </div>
    </div>
  );
};

window.Admin = Admin;
