const { useState, useEffect } = React;

const API = "";
const PALETTE = ["#4F7CFF", "#9B6DFF", "#22A06B", "#F2994A", "#EB5757", "#8E8E93"];

// ?token=... 으로 최초 1회 진입 시 저장하고 URL에서 제거
(function captureToken() {
  const p = new URLSearchParams(location.search);
  const t = p.get("token");
  if (t) {
    localStorage.setItem("token", t);
    history.replaceState({}, "", location.pathname);
  }
})();
function authHeaders(extra) {
  const h = Object.assign({}, extra || {});
  const t = localStorage.getItem("token");
  if (t) h["Authorization"] = "Bearer " + t;
  return h;
}

async function getJSON(path) {
  const r = await fetch(path, { headers: authHeaders() });
  if (!r.ok) throw new Error(path + " -> " + r.status);
  return r.json();
}
async function postJSON(path, body) {
  const r = await fetch(path, {
    method: "POST", headers: authHeaders({ "Content-Type": "application/json" }),
    body: JSON.stringify(body),
  });
  if (!r.ok) {
    let detail = r.status;
    try { detail = (await r.json()).detail || detail; } catch (e) {}
    throw new Error(String(detail));
  }
  return r.json();
}

function useLongPress(onLong, ms = 500) {
  const timer = React.useRef(null);
  const fired = React.useRef(false);
  const cb = React.useRef(onLong);
  cb.current = onLong;
  const start = () => {
    fired.current = false;
    timer.current = setTimeout(() => { fired.current = true; cb.current(); }, ms);
  };
  const cancel = () => { if (timer.current) { clearTimeout(timer.current); timer.current = null; } };
  return {
    onPointerDown: start,
    onPointerUp: cancel,
    onPointerLeave: cancel,
    onClickCapture: (e) => {
      if (fired.current) {
        e.preventDefault();
        e.stopPropagation();
        if (e.nativeEvent && e.nativeEvent.stopImmediatePropagation) e.nativeEvent.stopImmediatePropagation();
        fired.current = false;
      }
    },
  };
}

function SiteIcon({ site, onOpen, onLongPress }) {
  const lp = useLongPress(() => onLongPress(site));
  return (
    <button {...lp} onClick={() => onOpen(site.id)}
      className="flex flex-col items-center gap-2 group focus:outline-none">
      <span className="relative">
        <span className="w-16 h-16 rounded-full flex items-center justify-center text-white text-[11px] font-semibold text-center leading-tight px-1 shadow-lg transition-transform duration-150 group-active:scale-90"
          style={{ backgroundColor: site.color }}>{site.short}</span>
        {site.needs_login && (
          <span className="absolute -bottom-1 -left-1 w-5 h-5 rounded-full bg-neutral-800 border border-neutral-950 flex items-center justify-center text-[10px]">🔒</span>
        )}
        {site.unread > 0 && (
          <span className="absolute -top-2 -right-2 min-w-[22px] h-[22px] px-1.5 rounded-full bg-rose-500 text-white text-xs font-bold flex items-center justify-center shadow ring-2 ring-neutral-950">{site.unread}</span>
        )}
        {site.pinned && (
          <span className="absolute -top-3 left-1/2 text-base leading-none drop-shadow" style={{ transform: "translateX(-50%) scaleX(1.2)" }} title="상단 고정">🎩</span>
        )}
      </span>
      <span className="text-[11px] text-neutral-400 text-center leading-tight max-w-[5.5rem] truncate w-full">{site.name}</span>
    </button>
  );
}

function urlBase64ToUint8Array(base64) {
  const pad = "=".repeat((4 - (base64.length % 4)) % 4);
  const b64 = (base64 + pad).replace(/-/g, "+").replace(/_/g, "/");
  const raw = atob(b64);
  return Uint8Array.from([...raw].map((c) => c.charCodeAt(0)));
}

function App() {
  const [sites, setSites] = useState([]);
  const [postsBySite, setPostsBySite] = useState({});
  const [bodyById, setBodyById] = useState({});
  const [selectedId, setSelectedId] = useState(null);
  const [openPostId, setOpenPostId] = useState(null);
  const [shown, setShown] = useState(false);
  const [adding, setAdding] = useState(false);
  const [sheetSite, setSheetSite] = useState(null);
  const [pushOn, setPushOn] = useState(false);
  const [settingsOpen, setSettingsOpen] = useState(false);

  useEffect(() => {
    if (!("serviceWorker" in navigator)) return;
    navigator.serviceWorker.register("/service-worker.js").catch((e) => console.error(e));
    // 이미 구독돼 있으면 "알림 켜기" 버튼 숨김(앱 재실행 시 재등장 방지)
    navigator.serviceWorker.ready
      .then((reg) => reg.pushManager.getSubscription())
      .then((sub) => { if (sub) setPushOn(true); })
      .catch((e) => console.error(e));
  }, []);

  async function enablePush() {
    try {
      const perm = await Notification.requestPermission();
      if (perm !== "granted") { window.alert("알림 권한이 거부되었습니다."); return; }
      const reg = await navigator.serviceWorker.ready;
      const { key } = await getJSON("/api/push/public-key");
      if (!key) { window.alert("서버에 VAPID 키가 설정되지 않았습니다."); return; }
      const sub = await reg.pushManager.subscribe({
        userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(key),
      });
      const j = sub.toJSON();
      await postJSON("/api/push/subscribe", { endpoint: j.endpoint, keys: j.keys });
      setPushOn(true);
      window.alert("알림이 켜졌습니다.");
    } catch (e) { console.error(e); window.alert("알림 설정 실패: " + e.message); }
  }

  function loadSites() {
    getJSON("/api/sites").then(setSites).catch((e) => console.error(e));
  }
  useEffect(() => { loadSites(); }, []);

  const selected = sites.find((s) => s.id === selectedId) || null;
  const selectedPosts = selected ? postsBySite[selected.id] || [] : [];
  const postsLoaded = selected ? postsBySite[selected.id] !== undefined : false;
  const isReview = !!(selected && selected.auto_review);
  const visiblePosts = isReview ? selectedPosts.filter((p) => p.review_relevant !== false) : selectedPosts;

  useEffect(() => {
    if (selectedId == null) return;
    setShown(false);
    const t = requestAnimationFrame(() => setShown(true));
    getJSON(`/api/sites/${selectedId}/posts`)
      .then((posts) => setPostsBySite((m) => ({ ...m, [selectedId]: posts })))
      .catch((e) => console.error(e));
    return () => cancelAnimationFrame(t);
  }, [selectedId]);

  async function openPost(siteId, postId) {
    if (openPostId === postId) { setOpenPostId(null); return; }
    setOpenPostId(postId);
    if (bodyById[postId] === undefined) {
      try {
        const post = await getJSON(`/api/posts/${postId}`);
        setBodyById((m) => ({ ...m, [postId]: post.body || "(본문 없음)" }));
      } catch (e) {
        console.error(e);
        setBodyById((m) => ({ ...m, [postId]: "(본문을 불러오지 못했습니다)" }));
      }
    }
    const wasUnread = selectedPosts.find((p) => p.id === postId && !p.is_read);
    if (wasUnread) {
      fetch(`/api/posts/${postId}/read`, { method: "POST", headers: authHeaders() }).catch((e) => console.error(e));
      setPostsBySite((m) => ({
        ...m,
        [siteId]: (m[siteId] || []).map((p) => (p.id === postId ? { ...p, is_read: true } : p)),
      }));
      setSites((prev) => prev.map((s) => (s.id === siteId ? { ...s, unread: Math.max(0, s.unread - 1) } : s)));
    }
  }

  async function deleteSite(siteId) {
    if (!window.confirm("이 사이트를 삭제할까요? 저장된 글도 함께 지워집니다.")) return;
    try {
      const r = await fetch(`/api/sites/${siteId}`, { method: "DELETE", headers: authHeaders() });
      if (!r.ok) throw new Error(String(r.status));
      setSelectedId(null);
      setSites((prev) => prev.filter((s) => s.id !== siteId));
      setPostsBySite((m) => { const n = { ...m }; delete n[siteId]; return n; });
    } catch (e) { console.error(e); }
  }

  async function renameSite(site) {
    const name = window.prompt("새 이름", site.name);
    if (!name || !name.trim()) return;
    try {
      const r = await fetch(`/api/sites/${site.id}`, {
        method: "PATCH", headers: authHeaders({ "Content-Type": "application/json" }),
        body: JSON.stringify({ name: name.trim() }),
      });
      if (!r.ok) throw new Error(String(r.status));
    } catch (e) { console.error(e); window.alert("이름 변경 실패"); return; }
    setSheetSite(null); loadSites();
  }
  async function togglePin(site) {
    try {
      const r = await fetch(`/api/sites/${site.id}`, {
        method: "PATCH", headers: authHeaders({ "Content-Type": "application/json" }),
        body: JSON.stringify({ pinned: !site.pinned }),
      });
      if (!r.ok) throw new Error(String(r.status));
    } catch (e) { console.error(e); window.alert("고정 변경 실패"); return; }
    setSheetSite(null); loadSites();
  }
  async function markAllRead(site) {
    try {
      const r = await fetch(`/api/sites/${site.id}/read-all`, { method: "POST", headers: authHeaders() });
      if (!r.ok) throw new Error(String(r.status));
    } catch (e) { console.error(e); window.alert("모두 읽음 처리 실패"); return; }
    setSheetSite(null); loadSites();
  }
  async function toggleAutoReview(site) {
    try {
      const r = await fetch(`/api/sites/${site.id}`, {
        method: "PATCH", headers: authHeaders({ "Content-Type": "application/json" }),
        body: JSON.stringify({ auto_review: !site.auto_review }),
      });
      if (!r.ok) throw new Error(String(r.status));
    } catch (e) { console.error(e); window.alert("자동검토 변경 실패"); return; }
    setSheetSite(null); loadSites();
  }
  const totalNew = sites.reduce((n, s) => n + (s.unread || 0), 0);

  return (
    <div className="min-h-screen bg-neutral-950 flex justify-center font-sans antialiased">
      <div className="w-full max-w-md min-h-screen bg-neutral-950 text-neutral-100 relative overflow-hidden">

        <div className="px-6 pt-12 pb-4">
          <div className="flex items-end justify-between">
            <div>
              <p className="text-xs tracking-widest text-neutral-500 uppercase">새 소식</p>
              <h1 className="text-3xl font-semibold tracking-tight mt-1">알림함</h1>
            </div>
            {totalNew > 0 && (
              <span className="text-sm text-neutral-400 mb-1">
                안 읽은 글 <span className="text-rose-400 font-semibold">{totalNew}</span>
              </span>
            )}
          </div>
          {!pushOn && (
            <button onClick={enablePush} className="mt-2 text-xs text-neutral-400 underline">🔔 새 글 알림 켜기</button>
          )}
          <button onClick={() => setSettingsOpen(true)} className="mt-2 ml-3 text-xs text-neutral-400 underline">🎯 관심사 설정</button>
        </div>

        {/* 홈: 아이콘 그리드 + [+] */}
        <div className={"px-5 transition-all duration-300 " + (selectedId || adding ? "opacity-0 scale-95 pointer-events-none" : "opacity-100 scale-100")}>
          <div className="grid grid-cols-3 gap-y-7 gap-x-2 pt-2">
            {sites.map((site) => (
              <SiteIcon key={site.id} site={site}
                onOpen={(id) => { setSelectedId(id); setOpenPostId(null); }}
                onLongPress={(s) => setSheetSite(s)} />
            ))}
            {/* [+] 추가 타일 */}
            <button onClick={() => setAdding(true)} className="flex flex-col items-center gap-2 group focus:outline-none">
              <span className="w-16 h-16 rounded-full flex items-center justify-center border-2 border-dashed border-neutral-700 text-neutral-500 text-2xl transition-transform duration-150 group-active:scale-90">+</span>
              <span className="text-[11px] text-neutral-500 text-center leading-tight">사이트 추가</span>
            </button>
          </div>
          <p className="text-center text-xs text-neutral-600 mt-10 px-6">아이콘을 누르면 해당 사이트의 새 글이 펼쳐집니다.</p>
        </div>

        {/* 사이트 추가 화면 */}
        {adding && <AddSite onClose={() => setAdding(false)} onAdded={() => { setAdding(false); loadSites(); }} />}
        {settingsOpen && <SettingsPanel onClose={() => setSettingsOpen(false)} />}

        {/* 아이콘 long-press 액션시트 */}
        {sheetSite && (
          <div className="absolute inset-0 z-20 bg-black/50 flex items-end" onClick={() => setSheetSite(null)}>
            <div className="w-full bg-neutral-900 rounded-t-3xl p-4 pb-8" onClick={(e) => e.stopPropagation()}>
              <div className="flex items-center gap-3 px-2 pb-3 border-b border-neutral-800">
                <span className="w-10 h-10 rounded-full flex items-center justify-center text-white text-[10px] font-semibold" style={{ backgroundColor: sheetSite.color }}>{sheetSite.short}</span>
                <span className="text-sm font-semibold text-neutral-100">{sheetSite.name}</span>
              </div>
              <button onClick={() => renameSite(sheetSite)} className="w-full text-left px-2 py-3 text-sm text-neutral-200 active:bg-neutral-800 rounded-lg">이름 변경</button>
              <button onClick={() => markAllRead(sheetSite)} className="w-full text-left px-2 py-3 text-sm text-neutral-200 active:bg-neutral-800 rounded-lg">모두 읽음 처리</button>
              <button onClick={() => togglePin(sheetSite)} className="w-full text-left px-2 py-3 text-sm text-neutral-200 active:bg-neutral-800 rounded-lg">{sheetSite.pinned ? "상단 고정 해제" : "상단에 고정"}</button>
              <button onClick={() => toggleAutoReview(sheetSite)} className="w-full text-left px-2 py-3 text-sm text-neutral-200 active:bg-neutral-800 rounded-lg">{sheetSite.auto_review ? "🤖 자동검토 끄기" : "🤖 자동검토로 분류"}</button>
              <button onClick={() => setSheetSite(null)} className="w-full text-center px-2 py-3 mt-2 text-sm text-neutral-500 active:bg-neutral-800 rounded-lg">닫기</button>
            </div>
          </div>
        )}

        {/* 상세 보기 */}
        {selected && (
          <div className="absolute inset-0 bg-neutral-950 flex flex-col">
            <div className="px-5 pt-12 pb-3 flex items-center gap-3">
              <button onClick={() => setSelectedId(null)} className="w-9 h-9 rounded-full bg-neutral-800/70 flex items-center justify-center text-neutral-300 text-lg active:scale-90 transition">‹</button>
              <div className={"flex items-center gap-3 flex-1 transition-all duration-300 ease-out " + (shown ? "opacity-100 translate-y-0" : "opacity-0 translate-y-6")}>
                <span className="w-11 h-11 rounded-full flex items-center justify-center text-white text-[10px] font-semibold text-center leading-tight px-1" style={{ backgroundColor: selected.color }}>{selected.short}</span>
                <div className="flex-1">
                  <h2 className="text-base font-semibold leading-tight">{selected.name}</h2>
                  <p className="text-xs text-neutral-500">{selected.auto_review ? "🤖 AI가 관심사로 추려봄" : (selected.needs_login ? "🔒 로그인 연결됨" : "공개 피드")}</p>
                </div>
              </div>
              <button onClick={() => deleteSite(selected.id)} className="w-9 h-9 rounded-full bg-neutral-800/70 flex items-center justify-center text-neutral-400 text-base active:scale-90 transition" title="사이트 삭제">🗑</button>
            </div>

            <div className="flex-1 overflow-y-auto px-4 pb-10">
              {!postsLoaded && (<p className="text-center text-xs text-neutral-600 mt-10">글을 불러오는 중…</p>)}
              {postsLoaded && visiblePosts.length === 0 && (
                <p className="text-center text-xs text-neutral-600 mt-10">
                  {isReview ? "표시할 글이 없습니다." : ("글이 없습니다." + (selected.needs_login ? " 로그인이 필요한 게시판이면 ‹ 뒤로 → 삭제 후 자격증명으로 다시 추가하세요." : ""))}
                </p>
              )}
              {visiblePosts.map((post, i) => {
                const open = openPostId === post.id;
                return (
                  <div key={post.id} onClick={() => openPost(selected.id, post.id)}
                    style={{ transitionDelay: shown ? `${i * 45}ms` : "0ms" }}
                    className={"cursor-pointer rounded-2xl mb-2.5 px-4 py-3.5 border transition-all duration-300 ease-out " +
                      (shown ? "opacity-100 translate-y-0" : "opacity-0 translate-y-4") + " " +
                      (open ? "bg-neutral-800/80 border-neutral-700" : post.is_read ? "bg-neutral-900/60 border-neutral-800/60" : "bg-neutral-900 border-neutral-700")}>
                    <div className="flex items-start gap-2">
                      {!post.is_read && !open && (<span className="mt-1.5 w-2 h-2 rounded-full bg-rose-500 shrink-0" />)}
                      <div className="flex-1 min-w-0">
                        <div className="flex items-baseline justify-between gap-2">
                          <h3 className={"text-sm leading-snug " + (post.is_read ? "text-neutral-400 font-normal" : "text-neutral-100 font-semibold")}>{post.title}</h3>
                          <span className="text-[11px] text-neutral-500 shrink-0">{post.date_text}</span>
                        </div>
                        <div className={"overflow-hidden transition-all duration-300 ease-out " + (open ? "max-h-96 mt-2 overflow-y-auto" : "max-h-0")}>
                          <p className="text-sm text-neutral-300 whitespace-pre-line leading-relaxed">
                            {bodyById[post.id] === undefined ? "본문을 불러오는 중…" : bodyById[post.id]}
                          </p>
                        </div>
                        {!open && isReview && post.review_summary && (
                          <p className="text-xs text-amber-300/80 mt-1 leading-snug">{post.review_summary}</p>
                        )}
                        {!open && !isReview && post.author && (<p className="text-xs text-neutral-500 mt-1 truncate">{post.author}</p>)}
                      </div>
                    </div>
                  </div>
                );
              })}
              <div className="h-6" />
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

function AddSite({ onClose, onAdded }) {
  const [url, setUrl] = useState("");
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");
  const [preview, setPreview] = useState(null);  // {cms, detected_name, sample_posts}
  const [name, setName] = useState("");
  const [short, setShort] = useState("");
  const [color, setColor] = useState(PALETTE[0]);
  const [saving, setSaving] = useState(false);
  const [needsLogin, setNeedsLogin] = useState(false);
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const [useCookie, setUseCookie] = useState(false);
  const [cookie, setCookie] = useState("");

  async function doPreview() {
    setLoading(true); setError(""); setPreview(null);
    const body = { url: url.trim() };
    if (needsLogin) {
      if (useCookie) body.cookie = cookie.trim();
      else { body.username = username.trim(); body.password = password; }
    }
    try {
      const p = await postJSON("/api/sites/preview", body);
      setPreview(p);
      setName(p.detected_name || "");
      setShort((p.detected_name || "").slice(0, 4));
    } catch (e) {
      setError(needsLogin && !useCookie
        ? "미리보기 실패: " + e.message + " — '쿠키 직접 입력'을 시도해보세요."
        : "미리보기 실패: " + e.message);
    } finally { setLoading(false); }
  }

  async function doSave() {
    if (!preview) return;
    setSaving(true); setError("");
    const body = { name: name.trim(), short: short.trim() || name.trim().slice(0, 4),
      color, list_url: url.trim(), cms: preview.cms, needs_login: needsLogin };
    if (needsLogin) {
      if (useCookie) body.cookie = cookie.trim();
      else { body.username = username.trim(); body.password = password; }
    }
    try {
      await postJSON("/api/sites", body);
      onAdded();
    } catch (e) {
      setError("저장 실패: " + e.message); setSaving(false);
    }
  }

  return (
    <div className="absolute inset-0 bg-neutral-950 flex flex-col">
      <div className="px-5 pt-12 pb-3 flex items-center gap-3">
        <button onClick={onClose} className="w-9 h-9 rounded-full bg-neutral-800/70 flex items-center justify-center text-neutral-300 text-lg active:scale-90 transition">‹</button>
        <h2 className="text-base font-semibold">사이트 추가</h2>
      </div>

      <div className="flex-1 overflow-y-auto px-5 pb-10">
        <label className="block text-xs text-neutral-500 mb-1">게시판 목록 URL</label>
        <input value={url} onChange={(e) => setUrl(e.target.value)} placeholder="https://…/artclList.do"
          className="w-full rounded-xl bg-neutral-900 border border-neutral-700 px-3 py-2.5 text-sm text-neutral-100 placeholder-neutral-600 focus:outline-none focus:border-neutral-500" />
        <label className="flex items-center gap-2 mt-3 text-sm text-neutral-300">
          <input type="checkbox" checked={needsLogin} onChange={(e) => setNeedsLogin(e.target.checked)} />
          로그인이 필요한 게시판
        </label>
        {needsLogin && (
          <div className="mt-2 space-y-2">
            {!useCookie && (
              <>
                <input value={username} onChange={(e) => setUsername(e.target.value)} placeholder="아이디"
                  className="w-full rounded-lg bg-neutral-900 border border-neutral-700 px-3 py-2 text-sm text-neutral-100 placeholder-neutral-600 focus:outline-none focus:border-neutral-500" />
                <input value={password} onChange={(e) => setPassword(e.target.value)} type="password" placeholder="비밀번호"
                  className="w-full rounded-lg bg-neutral-900 border border-neutral-700 px-3 py-2 text-sm text-neutral-100 placeholder-neutral-600 focus:outline-none focus:border-neutral-500" />
              </>
            )}
            {useCookie && (
              <textarea value={cookie} onChange={(e) => setCookie(e.target.value)} rows={2}
                placeholder="브라우저 로그인 후 JSESSIONID=… 쿠키 붙여넣기"
                className="w-full rounded-lg bg-neutral-900 border border-neutral-700 px-3 py-2 text-sm text-neutral-100 placeholder-neutral-600 focus:outline-none focus:border-neutral-500" />
            )}
            <button onClick={() => setUseCookie((v) => !v)} className="text-xs text-neutral-500 underline">
              {useCookie ? "아이디·비번으로 로그인" : "자동로그인 대신 쿠키 직접 입력"}
            </button>
          </div>
        )}
        <button onClick={doPreview} disabled={!url.trim() || loading}
          className="mt-3 w-full rounded-xl bg-neutral-100 text-neutral-900 font-semibold py-2.5 text-sm disabled:opacity-40">
          {loading ? "확인 중…" : "미리보기"}
        </button>

        {error && <p className="mt-3 text-sm text-rose-400">{error}</p>}

        {preview && (
          <div className="mt-6">
            <div className="flex items-center gap-3 mb-4">
              <span className="w-12 h-12 rounded-full flex items-center justify-center text-white text-[10px] font-semibold text-center px-1" style={{ backgroundColor: color }}>{short || "?"}</span>
              <div className="flex-1">
                <label className="block text-xs text-neutral-500 mb-1">이름</label>
                <input value={name} onChange={(e) => setName(e.target.value)}
                  className="w-full rounded-lg bg-neutral-900 border border-neutral-700 px-2.5 py-1.5 text-sm text-neutral-100 focus:outline-none focus:border-neutral-500" />
              </div>
            </div>
            <label className="block text-xs text-neutral-500 mb-1">약칭(아이콘 글자)</label>
            <input value={short} onChange={(e) => setShort(e.target.value)} maxLength={6}
              className="w-full rounded-lg bg-neutral-900 border border-neutral-700 px-2.5 py-1.5 text-sm text-neutral-100 focus:outline-none focus:border-neutral-500" />
            <label className="block text-xs text-neutral-500 mt-3 mb-1">색</label>
            <div className="flex gap-2">
              {PALETTE.map((c) => (
                <button key={c} onClick={() => setColor(c)}
                  className={"w-8 h-8 rounded-full transition " + (color === c ? "ring-2 ring-white ring-offset-2 ring-offset-neutral-950" : "")}
                  style={{ backgroundColor: c }} />
              ))}
            </div>

            <p className="text-xs text-neutral-500 mt-6 mb-2">미리보기 ({preview.sample_posts.length}개)</p>
            <div className="space-y-1.5">
              {preview.sample_posts.map((p, i) => (
                <div key={i} className="rounded-lg bg-neutral-900/60 border border-neutral-800 px-3 py-2">
                  <div className="flex items-baseline justify-between gap-2">
                    <span className="text-sm text-neutral-200 truncate">{p.title}</span>
                    <span className="text-[11px] text-neutral-500 shrink-0">{p.date_text}</span>
                  </div>
                </div>
              ))}
            </div>

            <button onClick={doSave} disabled={!name.trim() || saving}
              className="mt-6 w-full rounded-xl bg-rose-500 text-white font-semibold py-2.5 text-sm disabled:opacity-40">
              {saving ? "추가 중…" : "추가"}
            </button>
          </div>
        )}
      </div>
    </div>
  );
}

function SettingsPanel({ onClose }) {
  const [audience, setAudience] = useState("");
  const [keywords, setKeywords] = useState("");
  const [loaded, setLoaded] = useState(false);
  const [saving, setSaving] = useState(false);

  useEffect(() => {
    getJSON("/api/settings")
      .then((s) => { setAudience(s.audience_spec || ""); setKeywords((s.interest_keywords || []).join(", ")); })
      .catch((e) => console.error(e))
      .finally(() => setLoaded(true));
  }, []);

  async function save() {
    setSaving(true);
    const list = keywords.split(",").map((w) => w.trim()).filter(Boolean);
    try {
      await fetch("/api/settings", {
        method: "PUT", headers: authHeaders({ "Content-Type": "application/json" }),
        body: JSON.stringify({ audience_spec: audience.trim(), interest_keywords: list }),
      });
      onClose();
    } catch (e) { console.error(e); window.alert("저장 실패"); setSaving(false); }
  }

  return (
    <div className="absolute inset-0 bg-neutral-950 flex flex-col">
      <div className="px-5 pt-12 pb-3 flex items-center gap-3">
        <button onClick={onClose} className="w-9 h-9 rounded-full bg-neutral-800/70 flex items-center justify-center text-neutral-300 text-lg active:scale-90 transition">‹</button>
        <h2 className="text-base font-semibold">관심사 설정</h2>
      </div>
      <div className="flex-1 overflow-y-auto px-5 pb-10">
        <p className="text-xs text-neutral-500 mb-4">자동검토 사이트의 글을 이 기준으로 추려줍니다.</p>
        <label className="block text-xs text-neutral-500 mb-1">대상자 (내가 누구인지 — 글의 대상이 나와 맞는지 판단)</label>
        <textarea value={audience} onChange={(e) => setAudience(e.target.value)} rows={3}
          placeholder="(예) 인하대 의대 의예과 2학년"
          className="w-full rounded-xl bg-neutral-900 border border-neutral-700 px-3 py-2.5 text-sm text-neutral-100 placeholder-neutral-600 focus:outline-none focus:border-neutral-500" />
        <label className="block text-xs text-neutral-500 mt-4 mb-1">관심 키워드 (쉼표로 구분)</label>
        <input value={keywords} onChange={(e) => setKeywords(e.target.value)}
          placeholder="(예) 장학금, 학사일정, 의학, 여행"
          className="w-full rounded-xl bg-neutral-900 border border-neutral-700 px-3 py-2.5 text-sm text-neutral-100 placeholder-neutral-600 focus:outline-none focus:border-neutral-500" />
        <button onClick={save} disabled={!loaded || saving}
          className="mt-6 w-full rounded-xl bg-rose-500 text-white font-semibold py-2.5 text-sm disabled:opacity-40">
          {saving ? "저장 중…" : "저장"}
        </button>
      </div>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
