// App shell: sidebar + topbar + routing

const NAV_ITEMS = [
  { key: "dashboard", label: "儀表板", icon: "home", group: "overview" },
  { key: "users", label: "用戶管理", icon: "users", group: "ops" },
  { key: "applications", label: "創作者申請", icon: "palette", group: "ops" },
  { key: "orders", label: "訂單管理", icon: "order", group: "ops" },
  { key: "disputes", label: "調解中心", icon: "alert", group: "ops" },
  { key: "content", label: "內容管理", icon: "palette", group: "ops" },
  { key: "r18",     label: "R18 專區",  icon: "eye",     group: "ops" },
  { key: "finance", label: "財務報表", icon: "coin", group: "finance" },
  { key: "admins", label: "管理員", icon: "shield", group: "system" },
  { key: "audit", label: "審計日誌", icon: "scroll", group: "system" },
  { key: "settings", label: "系統設定", icon: "settings", group: "system" },
];
const GROUP_LABELS = {
  overview: "總覽",
  ops: "營運",
  finance: "金流",
  system: "系統",
};

const Sidebar = ({ current, onNav, variant = "classic", collapsed, badges = {}, adminEmail = "" }) => {
  if (variant === "compact") return <SidebarCompact current={current} onNav={onNav} badges={badges} adminEmail={adminEmail}/>;
  const [chatOpen, setChatOpen] = React.useState(false);
  const groups = {};
  NAV_ITEMS.forEach(n => { (groups[n.group] = groups[n.group] || []).push(n); });
  return (
    <aside style={{
      width: collapsed ? 64 : 232,
      background: "var(--surface)",
      borderRight: "1px solid var(--border)",
      display: "flex", flexDirection: "column",
      transition: "width .2s ease",
      flexShrink: 0,
    }}>
      <div style={{
        padding: collapsed ? "16px 12px" : "18px 20px",
        display: "flex", alignItems: "center", gap: 10,
        borderBottom: "1px solid var(--border)", height: 64,
      }}>
        <BrandMark size={28}/>
        {!collapsed && <>
          <div style={{ lineHeight: 1.1 }}>
            <div style={{ fontSize: 14, fontWeight: 600, letterSpacing: 0.3 }}>堆堆</div>
            <div style={{ fontSize: 11, color: "var(--ink-3)" }}>Admin Console</div>
          </div>
        </>}
      </div>

      <nav style={{ flex: 1, overflow: "auto", padding: "12px 10px" }}>
        {Object.entries(groups).map(([g, items]) => (
          <div key={g} style={{ marginBottom: 18 }}>
            {!collapsed && (
              <div style={{
                fontSize: 10.5, fontWeight: 600, letterSpacing: 1,
                color: "var(--ink-4)", textTransform: "uppercase",
                padding: "4px 10px", marginBottom: 4,
              }}>{GROUP_LABELS[g]}</div>
            )}
            {items.map(n => {
              const active = current === n.key;
              const badgeVal = badges[n.key] || n.badge;
              const badgeBg = active ? "rgba(255,255,255,0.2)" : (badges[n.key] ? "var(--danger)" : "var(--accent)");
              return (
                <button key={n.key} onClick={() => onNav(n.key)} title={collapsed ? n.label : undefined} style={{
                  display: "flex", alignItems: "center", gap: 10,
                  width: "100%", padding: collapsed ? "10px" : "8px 10px",
                  borderRadius: 8, marginBottom: 2,
                  background: active ? "var(--ink)" : "transparent",
                  color: active ? "#fff" : "var(--ink-2)",
                  fontSize: 13.5, fontWeight: active ? 500 : 400,
                  justifyContent: collapsed ? "center" : "flex-start",
                  position: "relative",
                }}
                onMouseEnter={e => { if (!active) e.currentTarget.style.background = "var(--surface-2)"; }}
                onMouseLeave={e => { if (!active) e.currentTarget.style.background = "transparent"; }}
                >
                  <Icon name={n.icon} size={17}/>
                  {!collapsed && <>
                    <span style={{ flex: 1, textAlign: "left" }}>{n.label}</span>
                    {badgeVal ? (
                      <span style={{
                        background: badgeBg,
                        color: "#fff", fontSize: 10.5, padding: "0 6px", height: 16,
                        borderRadius: 999, display: "inline-flex", alignItems: "center", fontWeight: 500,
                      }}>{badgeVal}</span>
                    ) : null}
                  </>}
                  {collapsed && badgeVal ? (
                    <span style={{ position: "absolute", top: 6, right: 6, width: 7, height: 7, background: badges[n.key] ? "var(--danger)" : "var(--accent)", borderRadius: 999 }}/>
                  ) : null}
                </button>
              );
            })}
          </div>
        ))}
      </nav>

      {/* Chat footer */}
      <div style={{ borderTop: "1px solid var(--border)", padding: "10px" }}>
        <button onClick={() => setChatOpen(o => !o)} title={collapsed ? "內部頻道" : undefined} style={{
          display: "flex", alignItems: "center", gap: 10,
          width: "100%", padding: collapsed ? "10px" : "8px 10px",
          borderRadius: 8,
          background: chatOpen ? "var(--ink)" : "transparent",
          color: chatOpen ? "#fff" : "var(--ink-2)",
          fontSize: 13.5, fontWeight: chatOpen ? 500 : 400,
          justifyContent: collapsed ? "center" : "flex-start",
        }}
        onMouseEnter={e => { if (!chatOpen) e.currentTarget.style.background = "var(--surface-2)"; }}
        onMouseLeave={e => { if (!chatOpen) e.currentTarget.style.background = "transparent"; }}
        >
          <Icon name="message" size={17}/>
          {!collapsed && <span style={{ flex: 1, textAlign: "left" }}>內部頻道</span>}
        </button>
      </div>

      {chatOpen && <AdminChatPanel adminEmail={adminEmail} onClose={() => setChatOpen(false)}/>}
    </aside>
  );
};

const SidebarCompact = ({ current, onNav, badges = {} }) => {
  // Variant 2: icon rail + contextual label panel (NotionCalendar/Linear vibe)
  return (
    <aside style={{
      width: 68, background: "var(--ink)", color: "#fff",
      display: "flex", flexDirection: "column", alignItems: "center",
      padding: "14px 0", flexShrink: 0,
    }}>
      <div style={{ marginBottom: 14 }}><BrandMark size={32} light/></div>
      <div style={{ width: 28, height: 1, background: "rgba(255,255,255,0.1)", marginBottom: 14 }}/>
      <nav style={{ flex: 1, display: "flex", flexDirection: "column", gap: 4, width: "100%", padding: "0 10px" }}>
        {NAV_ITEMS.map(n => {
          const active = current === n.key;
          const badgeVal = badges[n.key] || n.badge;
          return (
            <button key={n.key} onClick={() => onNav(n.key)} title={n.label} style={{
              width: 44, height: 44, borderRadius: 10,
              background: active ? "var(--accent)" : "transparent",
              color: "#fff", display: "flex", alignItems: "center", justifyContent: "center",
              position: "relative", margin: "0 auto",
            }}
            onMouseEnter={e => { if (!active) e.currentTarget.style.background = "rgba(255,255,255,0.08)"; }}
            onMouseLeave={e => { if (!active) e.currentTarget.style.background = "transparent"; }}
            >
              <Icon name={n.icon} size={19}/>
              {badgeVal ? <span style={{ position: "absolute", top: 6, right: 6, width: 7, height: 7, background: active ? "#fff" : (badges[n.key] ? "var(--danger)" : "var(--accent)"), borderRadius: 999, boxShadow: "0 0 0 2px var(--ink)" }}/> : null}
            </button>
          );
        })}
      </nav>
      <button style={{ width: 40, height: 40, borderRadius: 10, color: "rgba(255,255,255,0.6)" }} title="說明">
        <Icon name="info" size={18}/>
      </button>
    </aside>
  );
};

const TopBar = ({ onLogout, onNav, variant = "classic", adminEmail = "", adminAvatar = null, onAvatarChange }) => {
  const [userOpen, setUserOpen] = useState(false);
  const [notifOpen, setNotifOpen] = useState(false);
  const [query, setQuery] = useState("");
  const [searchOpen, setSearchOpen] = useState(false);
  const userRef = useRef(null);
  const notifRef = useRef(null);
  const searchRef = useRef(null);

  const NAV_SEARCH = [
    { page: "dashboard",    label: "儀表板",      keywords: ["儀表板","首頁","總覽","dashboard"] },
    { page: "users",        label: "用戶管理",    keywords: ["用戶","會員","委託人","users"] },
    { page: "applications", label: "創作者申請",  keywords: ["創作者","申請","審核","作者","applications"] },
    { page: "orders",       label: "訂單管理",    keywords: ["訂單","委託","已中止","orders"] },
    { page: "disputes",     label: "調解中心",    keywords: ["調解","糾紛","disputes"] },
    { page: "content",      label: "內容管理",    keywords: ["內容","作品","商品","徵稿","動態","content"] },
    { page: "r18",          label: "R18 專區",    keywords: ["r18","成人","限制","adult","18","專區"] },
    { page: "finance",      label: "財務報表",    keywords: ["財務","提領","費率","結算","交易","finance"] },
    { page: "admins",       label: "管理員",      keywords: ["管理員","admin","權限"] },
    { page: "settings",     label: "系統設定",    keywords: ["設定","系統","費率","維護","settings"] },
  ];

  const results = query.trim().length >= 1
    ? NAV_SEARCH.filter(item => {
        const q = query.toLowerCase();
        return item.keywords.some(k => k.includes(q)) || item.label.includes(q);
      })
    : [];

  useEffect(() => {
    const h = e => {
      if (userRef.current && !userRef.current.contains(e.target)) setUserOpen(false);
      if (notifRef.current && !notifRef.current.contains(e.target)) setNotifOpen(false);
      if (searchRef.current && !searchRef.current.contains(e.target)) setSearchOpen(false);
    };
    document.addEventListener("mousedown", h);
    return () => document.removeEventListener("mousedown", h);
  }, []);

  return (
    <div style={{
      height: 64, borderBottom: "1px solid var(--border)",
      background: "var(--surface)", display: "flex", alignItems: "center",
      padding: "0 24px", gap: 16, flexShrink: 0,
    }}>
      {variant === "compact" && (
        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
          <div style={{ fontSize: 15, fontWeight: 600, letterSpacing: 0.3 }}>堆堆 · Admin</div>
          <div style={{ width: 1, height: 16, background: "var(--border-strong)" }}/>
        </div>
      )}

      <div ref={searchRef} style={{ flex: 1, maxWidth: 480, position: "relative" }}>
        <Input icon="search" placeholder="搜尋功能頁面…" value={query}
          onChange={e => { setQuery(e.target.value); setSearchOpen(true); }}
          onFocus={() => setSearchOpen(true)}
        />
        {searchOpen && query.trim().length >= 1 && (
          <div style={{
            position: "absolute", top: "calc(100% + 6px)", left: 0, right: 0,
            background: "var(--surface)", border: "1px solid var(--border)",
            borderRadius: 10, boxShadow: "0 8px 24px rgba(0,0,0,0.1)", zIndex: 100, overflow: "hidden",
          }}>
            {results.length > 0 ? results.map(item => (
              <button key={item.page} onClick={() => { onNav?.(item.page); setQuery(""); setSearchOpen(false); }} style={{
                display: "flex", alignItems: "center", gap: 10, width: "100%",
                padding: "10px 14px", textAlign: "left",
              }}
                onMouseEnter={e => e.currentTarget.style.background = "var(--surface-2)"}
                onMouseLeave={e => e.currentTarget.style.background = "transparent"}
              >
                <Icon name="arrow-right" size={14} style={{ color: "var(--ink-3)" }}/>
                <span style={{ fontSize: 13, fontWeight: 500 }}>{item.label}</span>
              </button>
            )) : (
              <div style={{ padding: "14px", fontSize: 13, color: "var(--ink-3)", textAlign: "center" }}>找不到符合的頁面</div>
            )}
          </div>
        )}
      </div>

      <div style={{ flex: 1 }}/>

      <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
        {/* System status */}
        <div style={{ display: "flex", alignItems: "center", gap: 6, padding: "6px 10px", background: "var(--success-soft)", borderRadius: 8, fontSize: 12, color: "#1E6B40", fontWeight: 500 }}>
          <span style={{ width: 6, height: 6, borderRadius: 999, background: "var(--success)", boxShadow: "0 0 0 3px rgba(47,143,90,0.18)" }}/>
          系統正常
        </div>

        {/* Notifications */}
        <div ref={notifRef} style={{ position: "relative" }}>
          <button onClick={() => setNotifOpen(o => !o)} style={{
            width: 36, height: 36, borderRadius: 8, display: "flex", alignItems: "center", justifyContent: "center", position: "relative",
            background: notifOpen ? "var(--surface-2)" : "transparent",
          }}
          onMouseEnter={e => { if (!notifOpen) e.currentTarget.style.background = "var(--surface-2)"; }}
          onMouseLeave={e => { if (!notifOpen) e.currentTarget.style.background = "transparent"; }}
          >
            <Icon name="bell" size={18}/>
          </button>
          {notifOpen && <NotificationPanel/>}
        </div>

        {/* User */}
        <div ref={userRef} style={{ position: "relative" }}>
          <button onClick={() => setUserOpen(o => !o)} style={{
            display: "flex", alignItems: "center", gap: 8, padding: "4px 10px 4px 4px",
            borderRadius: 999, background: userOpen ? "var(--surface-2)" : "transparent",
          }}
          onMouseEnter={e => { if (!userOpen) e.currentTarget.style.background = "var(--surface-2)"; }}
          onMouseLeave={e => { if (!userOpen) e.currentTarget.style.background = "transparent"; }}
          >
            {(() => {
              const name = adminEmail.split("@")[0] || "admin";
              return (<>
                {adminAvatar
                  ? <img src={adminAvatar} style={{ width: 28, height: 28, borderRadius: "50%", objectFit: "cover", flexShrink: 0 }} onError={e => { e.target.style.display = "none"; }}/>
                  : <Avatar hue={25} glyph={name[0].toUpperCase()} size={28}/>}
                <div style={{ textAlign: "left", lineHeight: 1.15 }}>
                  <div style={{ fontSize: 13, fontWeight: 500 }}>{name} (Super Admin)</div>
                  <div style={{ fontSize: 11, color: "var(--ink-3)" }}>{adminEmail}</div>
                </div>
              </>);
            })()}
            <Icon name="chevron-down" size={14} style={{ color: "var(--ink-3)" }}/>
          </button>
          {userOpen && <UserMenu adminEmail={adminEmail} adminAvatar={adminAvatar} onAvatarChange={onAvatarChange} onLogout={onLogout}/>}
        </div>
      </div>
    </div>
  );
};

const NotificationPanel = () => (
  <div style={{
    position: "absolute", top: "calc(100% + 8px)", right: 0,
    width: 360, background: "var(--surface)", borderRadius: 12,
    border: "1px solid var(--border)", boxShadow: "var(--shadow-lg)",
    zIndex: 50, overflow: "hidden",
  }}>
    <div style={{ padding: "14px 16px", borderBottom: "1px solid var(--border)" }}>
      <div style={{ fontSize: 14, fontWeight: 600 }}>通知</div>
    </div>
    <div style={{ padding: "48px 0", textAlign: "center", color: "var(--ink-4)" }}>
      <Icon name="bell" size={24} style={{ marginBottom: 10 }}/>
      <div style={{ fontSize: 13 }}>目前沒有任何通知</div>
    </div>
  </div>
);

const ChangePasswordModal = ({ onClose, adminEmail = "" }) => {
  const { useState: uS, useRef } = React;
  const email = adminEmail;
  const [step, setStep] = uS("form"); // form | otp | done
  const [newPw, setNewPw] = uS("");
  const [confirmPw, setConfirmPw] = uS("");
  const [otp, setOtp] = uS(["","","","","",""]);
  const [loading, setLoading] = uS(false);
  const [error, setError] = uS("");
  const [resendIn, setResendIn] = uS(0);
  const otpRefs = useRef([]);

  React.useEffect(() => {
    if (resendIn <= 0) return;
    const t = setTimeout(() => setResendIn(r => r - 1), 1000);
    return () => clearTimeout(t);
  }, [resendIn]);

  const sendOtp = async () => {
    setError("");
    if (!newPw || newPw.length < 8) { setError("密碼至少需要 8 個字元"); return; }
    if (newPw !== confirmPw) { setError("兩次密碼不一致"); return; }
    setLoading(true);
    const res = await fetch("/api/admin/otp/send", {
      method: "POST", headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ email }),
    });
    setLoading(false);
    if (!res.ok) { const d = await res.json().catch(()=>({})); setError(d.error || "發送失敗"); return; }
    setStep("otp");
    setResendIn(30);
    setTimeout(() => otpRefs.current[0]?.focus(), 100);
  };

  const submit = async () => {
    const code = otp.join("");
    if (code.length < 6) { setError("請輸入完整驗證碼"); return; }
    setError(""); setLoading(true);
    const res = await fetch("/api/admin/auth/change-password", {
      method: "POST", headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ email, otp: code, newPassword: newPw }),
    });
    setLoading(false);
    if (!res.ok) { const d = await res.json().catch(()=>({})); setError(d.error || "更新失敗"); return; }
    setStep("done");
  };

  const setDigit = (i, v) => {
    v = v.replace(/\D/g,"").slice(0,1);
    const next = [...otp]; next[i] = v; setOtp(next);
    if (v && i < 5) otpRefs.current[i+1]?.focus();
  };

  return (
    <div style={{ position:"fixed", inset:0, background:"rgba(0,0,0,0.45)", zIndex:200, display:"flex", alignItems:"center", justifyContent:"center" }}
      onClick={e => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="fade-in" style={{ background:"var(--surface)", borderRadius:16, padding:32, width:400, boxShadow:"var(--shadow-lg)" }}>
        {step === "done" ? (<>
          <div style={{ textAlign:"center", padding:"16px 0" }}>
            <Icon name="check" size={40} style={{ color:"var(--success)", marginBottom:12 }}/>
            <div style={{ fontSize:18, fontWeight:600, marginBottom:6 }}>密碼已更新</div>
            <div style={{ fontSize:13, color:"var(--ink-3)", marginBottom:24 }}>下次登入請使用新密碼。</div>
            <Button variant="primary" onClick={onClose}>關閉</Button>
          </div>
        </>) : (<>
          <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", marginBottom:20 }}>
            <div style={{ fontSize:17, fontWeight:600 }}>修改密碼</div>
            <button onClick={onClose} style={{ color:"var(--ink-3)" }}><Icon name="x" size={18}/></button>
          </div>
          {step === "form" && (<>
            <div style={{ display:"flex", flexDirection:"column", gap:12 }}>
              <div>
                <label style={{ fontSize:12, fontWeight:500, color:"var(--ink-2)", display:"block", marginBottom:6 }}>新密碼</label>
                <Input icon="lock" type="password" value={newPw} onChange={e=>setNewPw(e.target.value)} placeholder="至少 8 個字元"/>
              </div>
              <div>
                <label style={{ fontSize:12, fontWeight:500, color:"var(--ink-2)", display:"block", marginBottom:6 }}>確認新密碼</label>
                <Input icon="lock" type="password" value={confirmPw} onChange={e=>setConfirmPw(e.target.value)} placeholder="再輸入一次"/>
              </div>
              {error && <div style={{ fontSize:12.5, color:"var(--danger)", display:"flex", alignItems:"center", gap:6 }}><Icon name="alert" size={14}/>{error}</div>}
              <Button variant="primary" size="lg" onClick={sendOtp} disabled={loading} style={{ width:"100%", marginTop:4 }}>
                {loading ? "發送中…" : "發送驗證碼至 " + email}
              </Button>
            </div>
          </>)}
          {step === "otp" && (<>
            <div style={{ fontSize:13.5, color:"var(--ink-3)", marginBottom:20 }}>
              已將驗證碼發送至 <span style={{ color:"var(--ink-2)", fontWeight:500 }}>{email}</span>，5 分鐘內有效。
            </div>
            <div style={{ display:"flex", gap:8, justifyContent:"space-between", marginBottom:16 }}>
              {otp.map((d,i) => (
                <input key={i} ref={el=>otpRefs.current[i]=el} value={d}
                  onChange={e=>setDigit(i,e.target.value)}
                  onKeyDown={e=>{ if(e.key==="Backspace"&&!otp[i]&&i>0) otpRefs.current[i-1]?.focus(); }}
                  inputMode="numeric" maxLength={1}
                  style={{ width:48, height:56, fontSize:22, textAlign:"center",
                    border:"1.5px solid "+(d?"var(--ink)":"var(--border-strong)"),
                    borderRadius:10, fontFamily:"var(--font-mono)", fontWeight:600,
                    outline:0, background:"var(--surface)", transition:"border-color .15s" }}/>
              ))}
            </div>
            {error && <div style={{ fontSize:12.5, color:"var(--danger)", marginBottom:12, display:"flex", alignItems:"center", gap:6 }}><Icon name="alert" size={14}/>{error}</div>}
            <Button variant="primary" size="lg" onClick={submit} disabled={loading||otp.some(d=>!d)} style={{ width:"100%" }}>
              {loading ? "驗證中…" : "確認修改密碼"}
            </Button>
            <div style={{ marginTop:14, textAlign:"center", fontSize:13, color:"var(--ink-3)" }}>
              {resendIn > 0 ? <span>可於 {resendIn} 秒後重發</span> : (
                <button onClick={async()=>{ setResendIn(30); await fetch("/api/admin/otp/send",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email})}); }} style={{ color:"var(--accent-ink)", fontWeight:500 }}>重新發送</button>
              )}
            </div>
          </>)}
        </>)}
      </div>
    </div>
  );
};

const UserMenu = ({ onLogout, adminEmail = "", adminAvatar = null, onAvatarChange }) => {
  const [changePwOpen, setChangePwOpen] = React.useState(false);
  const [uploading, setUploading] = React.useState(false);
  const fileInputRef = React.useRef(null);
  const email = adminEmail;
  const name = email.split("@")[0] || "admin";

  const handleAvatarChange = async (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    e.target.value = "";
    setUploading(true);
    try {
      const fd = new FormData();
      fd.append("file", file);
      const res = await fetch("/api/admin/profile/avatar", { method: "POST", body: fd });
      const data = await res.json();
      if (res.ok && data.url) onAvatarChange?.(data.url);
    } catch {}
    setUploading(false);
  };

  return (<>
    {changePwOpen && <ChangePasswordModal onClose={()=>setChangePwOpen(false)} adminEmail={email}/>}
    <input ref={fileInputRef} type="file" accept="image/*" style={{ display: "none" }} onChange={handleAvatarChange}/>
    <div style={{
      position: "absolute", top: "calc(100% + 8px)", right: 0,
      width: 240, background: "var(--surface)", borderRadius: 12,
      border: "1px solid var(--border)", boxShadow: "var(--shadow-lg)",
      zIndex: 50, padding: 6, overflow: "hidden",
    }}>
      <div style={{ padding: "10px 12px", borderBottom: "1px solid var(--border)", marginBottom: 4 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <div style={{ position: "relative", flexShrink: 0 }}>
            {adminAvatar
              ? <img src={adminAvatar} style={{ width: 36, height: 36, borderRadius: "50%", objectFit: "cover", display: "block" }} onError={e => { e.target.style.display = "none"; }}/>
              : <Avatar hue={25} glyph={name[0].toUpperCase()} size={36}/>}
            <button onClick={() => fileInputRef.current?.click()} disabled={uploading} title="更換頭像" style={{
              position: "absolute", inset: 0, borderRadius: "50%",
              background: "rgba(0,0,0,0)", display: "flex", alignItems: "center", justifyContent: "center",
              opacity: 0, transition: "opacity .15s, background .15s",
            }}
              onMouseEnter={e => { e.currentTarget.style.opacity = "1"; e.currentTarget.style.background = "rgba(0,0,0,0.45)"; }}
              onMouseLeave={e => { e.currentTarget.style.opacity = "0"; e.currentTarget.style.background = "rgba(0,0,0,0)"; }}
            >
              {uploading ? <span style={{ color: "#fff", fontSize: 10 }}>…</span> : <Icon name="edit" size={12} style={{ color: "#fff" }}/>}
            </button>
          </div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontSize: 13.5, fontWeight: 600 }}>{name}</div>
            <div style={{ fontSize: 11.5, color: "var(--ink-3)", overflow: "hidden", textOverflow: "ellipsis" }}>{email}</div>
          </div>
        </div>
        <div style={{ marginTop: 10, display: "flex", gap: 6 }}>
          <Badge tone="accent" size="sm">Super Admin</Badge>
          <Badge tone="success" size="sm" dot>MFA 已啟用</Badge>
        </div>
      </div>
      <button onClick={()=>setChangePwOpen(true)} style={{
        display: "flex", alignItems: "center", gap: 10, width: "100%",
        padding: "8px 10px", borderRadius: 6, fontSize: 13, color: "var(--ink-2)", textAlign: "left",
      }}
        onMouseEnter={e => e.currentTarget.style.background = "var(--surface-2)"}
        onMouseLeave={e => e.currentTarget.style.background = "transparent"}
      >
        <Icon name="key" size={15} style={{ color: "var(--ink-3)" }}/>
        <span>修改密碼</span>
      </button>
      <div style={{ height: 1, background: "var(--border)", margin: "4px 0" }}/>
      <button onClick={onLogout} style={{
        display: "flex", alignItems: "center", gap: 10, width: "100%",
        padding: "8px 10px", borderRadius: 6, fontSize: 13, color: "var(--danger)", textAlign: "left",
      }}
        onMouseEnter={e => e.currentTarget.style.background = "var(--danger-soft)"}
        onMouseLeave={e => e.currentTarget.style.background = "transparent"}
      >
        <Icon name="logout" size={15}/>
        <span>登出</span>
      </button>
    </div>
  </>);
};

const fmtChatTime = (iso) => {
  if (!iso) return "";
  return new Date(iso).toLocaleTimeString("zh-TW", {
    hour: "2-digit", minute: "2-digit", hour12: false, timeZone: "Asia/Taipei",
  });
};

const AdminChatPanel = ({ adminEmail, onClose }) => {
  const [messages, setMessages] = React.useState([]);
  const [text, setText] = React.useState("");
  const [sending, setSending] = React.useState(false);
  const [pendingImages, setPendingImages] = React.useState([]);
  const [uploading, setUploading] = React.useState(false);
  const bottomRef = React.useRef(null);
  const fileInputRef = React.useRef(null);
  const senderName = adminEmail.split("@")[0] || "admin";

  const load = () => {
    fetch("/api/admin/chat")
      .then(r => r.ok ? r.json() : [])
      .then(d => Array.isArray(d) && setMessages(d))
      .catch(() => {});
  };

  React.useEffect(() => {
    load();
    const t = setInterval(load, 5000);
    return () => clearInterval(t);
  }, []);

  React.useEffect(() => {
    bottomRef.current?.scrollIntoView({ behavior: "smooth" });
  }, [messages]);

  const handleFileChange = async (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    e.target.value = "";
    setUploading(true);
    try {
      const fd = new FormData();
      fd.append("file", file);
      const res = await fetch("/api/admin/chat/upload", { method: "POST", body: fd });
      const data = await res.json();
      if (res.ok && data.url) {
        setPendingImages(prev => [...prev, data.url]);
      }
    } catch {}
    setUploading(false);
  };

  const send = async () => {
    const msg = text.trim();
    if ((!msg && pendingImages.length === 0) || sending) return;
    setSending(true);
    setText("");
    const imgs = [...pendingImages];
    setPendingImages([]);
    await fetch("/api/admin/chat", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ message: msg, images: imgs }),
    }).catch(() => {});
    setSending(false);
    load();
  };

  const canSend = (text.trim() || pendingImages.length > 0) && !sending && !uploading;

  return (
    <div style={{
      position: "fixed", left: 10, bottom: 10,
      width: 320, height: 460,
      background: "var(--surface)", border: "1px solid var(--border)",
      borderRadius: 14, boxShadow: "var(--shadow-lg)",
      display: "flex", flexDirection: "column", zIndex: 200,
    }}>
      {/* Header */}
      <div style={{
        padding: "12px 14px", borderBottom: "1px solid var(--border)",
        display: "flex", alignItems: "center", gap: 10, flexShrink: 0,
      }}>
        <Icon name="message" size={16} style={{ color: "var(--accent)" }}/>
        <div style={{ flex: 1 }}>
          <div style={{ fontSize: 13.5, fontWeight: 600 }}>內部頻道</div>
          <div style={{ fontSize: 11, color: "var(--ink-3)" }}>管理員專用頻道</div>
        </div>
        <button onClick={onClose} style={{ color: "var(--ink-3)", padding: 4 }}>
          <Icon name="x" size={16}/>
        </button>
      </div>

      {/* Messages */}
      <div style={{ flex: 1, overflow: "auto", padding: "12px 14px", display: "flex", flexDirection: "column", gap: 8 }}>
        {messages.length === 0 && (
          <div style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", color: "var(--ink-4)", fontSize: 13 }}>
            目前沒有訊息
          </div>
        )}
        {messages.map(m => {
          const isMe = m.email === adminEmail;
          const imgs = m.images ?? [];
          return (
            <div key={m.id} style={{ display: "flex", flexDirection: "column", alignItems: isMe ? "flex-end" : "flex-start" }}>
              {!isMe && (
                <div style={{ fontSize: 11, color: "var(--ink-3)", marginBottom: 2, paddingLeft: 4 }}>{m.name}</div>
              )}
              {imgs.length > 0 && (
                <div style={{ display: "flex", flexWrap: "wrap", gap: 4, marginBottom: m.message ? 4 : 0, maxWidth: "82%", justifyContent: isMe ? "flex-end" : "flex-start" }}>
                  {imgs.map((url, i) => (
                    <a key={i} href={url} target="_blank" rel="noopener noreferrer">
                      <img src={url} alt="" style={{ width: 120, height: 90, objectFit: "cover", borderRadius: 8, display: "block", border: "1px solid var(--border)" }}
                        onError={e => { e.target.style.display = "none"; }}/>
                    </a>
                  ))}
                </div>
              )}
              {m.message && (
                <div style={{
                  maxWidth: "82%", padding: "7px 11px", borderRadius: isMe ? "12px 12px 4px 12px" : "12px 12px 12px 4px",
                  background: isMe ? "var(--ink)" : "var(--surface-2)",
                  color: isMe ? "#fff" : "var(--ink)",
                  fontSize: 13, lineHeight: 1.5, wordBreak: "break-word",
                }}>{m.message}</div>
              )}
              <div style={{ fontSize: 10.5, color: "var(--ink-4)", marginTop: 2, paddingLeft: 4, paddingRight: 4 }}>
                {fmtChatTime(m.createdAt)}
              </div>
            </div>
          );
        })}
        <div ref={bottomRef}/>
      </div>

      {/* Pending image previews */}
      {pendingImages.length > 0 && (
        <div style={{ padding: "6px 12px 0", display: "flex", gap: 6, flexWrap: "wrap", borderTop: "1px solid var(--border)" }}>
          {pendingImages.map((url, i) => (
            <div key={i} style={{ position: "relative" }}>
              <img src={url} alt="" style={{ width: 52, height: 40, objectFit: "cover", borderRadius: 6, display: "block" }}/>
              <button onClick={() => setPendingImages(prev => prev.filter((_, j) => j !== i))} style={{
                position: "absolute", top: -5, right: -5, width: 16, height: 16,
                borderRadius: "50%", background: "var(--ink)", color: "#fff",
                fontSize: 10, display: "flex", alignItems: "center", justifyContent: "center", lineHeight: 1,
              }}>×</button>
            </div>
          ))}
        </div>
      )}

      {/* Input */}
      <div style={{ padding: "10px 12px", borderTop: "1px solid var(--border)", display: "flex", gap: 6, flexShrink: 0, alignItems: "center" }}>
        <input ref={fileInputRef} type="file" accept="image/*" style={{ display: "none" }} onChange={handleFileChange}/>
        <button onClick={() => fileInputRef.current?.click()} disabled={uploading} title="上傳圖片" style={{
          width: 30, height: 30, borderRadius: 6, flexShrink: 0,
          background: "var(--surface-2)", color: uploading ? "var(--ink-4)" : "var(--ink-2)",
          display: "flex", alignItems: "center", justifyContent: "center",
        }}>
          {uploading ? "…" : <Icon name="image" size={15}/>}
        </button>
        <input
          value={text}
          onChange={e => setText(e.target.value)}
          onKeyDown={e => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send(); } }}
          placeholder="輸入訊息…"
          style={{
            flex: 1, padding: "7px 10px", border: "1px solid var(--border)",
            borderRadius: 8, fontSize: 13, outline: "none", background: "var(--bg)",
          }}
          onFocus={e => e.target.style.borderColor = "var(--ink-3)"}
          onBlur={e => e.target.style.borderColor = "var(--border)"}
        />
        <button onClick={send} disabled={!canSend} style={{
          width: 34, height: 34, borderRadius: 8, flexShrink: 0,
          background: canSend ? "var(--ink)" : "var(--surface-2)",
          color: canSend ? "#fff" : "var(--ink-4)",
          display: "flex", alignItems: "center", justifyContent: "center",
          transition: "background .15s, color .15s",
        }}>
          <Icon name="arrow-right" size={15}/>
        </button>
      </div>
    </div>
  );
};

Object.assign(window, { Sidebar, TopBar, NAV_ITEMS });
