/* ============================================================
   Fórum — feed, detalhe do post, editor
   ============================================================ */

const { useState, useEffect, useRef } = React;

function memberById(members, id) { return members.find((m) => m.id === id) || { display: "—", color: "#888", role: "helper" }; }

/* ---------- Chip de categoria ---------- */
function CatChip({ cat, small, categories }) {
  const list = categories || PR.CATEGORIES;
  const c = list.find((x) => x.id === cat);
  if (!c) return null;
  return (
    <span className={"pr-catchip" + (small ? " sm" : "")} style={{ color: c.accent, background: c.accent + "18", borderColor: c.accent + "3a" }}>
      <Icon name="dot" size={small ? 7 : 8} />{c.label}
    </span>
  );
}

/* ============================================================
   Editor de post (criar / editar)
   ============================================================ */
function PostEditor({ post, currentUser, members, categories, onSave, onClose }) {
  const editing = !!post;
  const cats = categories || PR.CATEGORIES;
  // categorias em que o usuário pode postar (na edição, mantém a atual disponível)
  const postable = cats.filter((c) => PR.canCat(currentUser.role, c, "post") || (post && c.id === post.category));
  const [title, setTitle] = useState(post ? post.title : "");
  const [category, setCategory] = useState(post ? post.category : (postable[0] ? postable[0].id : (cats[0] && cats[0].id)));
  const [body, setBody] = useState(post ? post.body.replace(/\[\[img:\d+\]\]/g, "").replace(/\n{3,}/g, "\n\n").trim() : "");
  const [images, setImages] = useState(post && post.images ? post.images.slice() : []);
  const [pinned, setPinned] = useState(post ? post.pinned : false);
  const [tab, setTab] = useState("editar");
  const bodyRef = useRef(null);
  const imgRef = useRef(null);
  const canPin = PR.can(currentUser.role, "post.pin");

  // envolve a seleção com marcadores (ex: ** **)
  function surround(before, after) {
    const ta = bodyRef.current; if (!ta) return;
    const s = ta.selectionStart, e = ta.selectionEnd;
    const sel = body.slice(s, e);
    const next = body.slice(0, s) + before + sel + (after ?? before) + body.slice(e);
    setBody(next);
    requestAnimationFrame(() => {
      ta.focus();
      ta.selectionStart = s + before.length;
      ta.selectionEnd = s + before.length + sel.length;
    });
  }
  // adiciona prefixo às linhas selecionadas (ex: > citação)
  function prefixLines(prefix) {
    const ta = bodyRef.current; if (!ta) return;
    const s = ta.selectionStart, e = ta.selectionEnd;
    const lineStart = body.lastIndexOf("\n", s - 1) + 1;
    const block = body.slice(lineStart, e);
    const replaced = block.split("\n").map((l) => prefix + l).join("\n");
    const next = body.slice(0, lineStart) + replaced + body.slice(e);
    setBody(next);
    requestAnimationFrame(() => { ta.focus(); });
  }
  function insertCodeBlock() {
    const ta = bodyRef.current; if (!ta) return;
    const s = ta.selectionStart, e = ta.selectionEnd;
    const sel = body.slice(s, e) || "código aqui";
    const next = body.slice(0, s) + "```\n" + sel + "\n```" + body.slice(e);
    setBody(next);
    requestAnimationFrame(() => ta.focus());
  }
  function addImages(ev) {
    const files = Array.from(ev.target.files || []);
    files.forEach((f) => {
      const reader = new FileReader();
      reader.onload = () => setImages((prev) => [...prev, reader.result]);
      reader.readAsDataURL(f);
    });
    ev.target.value = "";
  }
  function removeImage(i) {
    setImages((prev) => prev.filter((_, idx) => idx !== i));
  }

  function save() {
    if (!title.trim() || !body.trim()) return;
    onSave({ title: title.trim(), category, body: body.trim(), images, pinned: canPin ? pinned : (post ? post.pinned : false) });
    onClose();
  }

  function insertLink() {
    const ta = bodyRef.current; if (!ta) return;
    const s = ta.selectionStart, e = ta.selectionEnd;
    const sel = body.slice(s, e) || "texto";
    const snippet = `[${sel}](https://)`;
    const next = body.slice(0, s) + snippet + body.slice(e);
    setBody(next);
    const caret = s + sel.length + 3 + 8; // depois de "](https://"
    requestAnimationFrame(() => { ta.focus(); ta.setSelectionRange(caret, caret); });
  }

  const tools = [
    { t: "Título grande", label: "H1", small: true, fn: () => prefixLines("# ") },
    { t: "Título médio", label: "H2", small: true, fn: () => prefixLines("## ") },
    { t: "Título pequeno", label: "H3", small: true, fn: () => prefixLines("### ") },
    { t: "Negrito", label: "B", style: { fontWeight: 800 }, fn: () => surround("**") },
    { t: "Itálico", label: "i", style: { fontStyle: "italic", fontFamily: "serif" }, fn: () => surround("*") },
    { t: "Sublinhado", label: "U", style: { textDecoration: "underline" }, fn: () => surround("__") },
    { t: "Riscado", label: "S", style: { textDecoration: "line-through" }, fn: () => surround("~~") },
    { t: "Link", label: "🔗", small: true, fn: insertLink },
    { t: "Código", label: "</>", small: true, fn: () => surround("`") },
    { t: "Bloco de código", label: "{ }", small: true, fn: insertCodeBlock },
    { t: "Citação", label: "❝", fn: () => prefixLines("> ") },
  ];

  return (
    <Modal
      title={editing ? "Editar postagem" : "Nova postagem"}
      onClose={onClose}
      width={640}
      footer={<>
        {canPin && (
          <label className="pr-pintoggle" onClick={() => setPinned((p) => !p)}>
            <span className={"pr-switch" + (pinned ? " on" : "")}><i></i></span>
            <Icon name="pin" size={15} /> Fixar no topo
          </label>
        )}
        <div style={{ flex: 1 }}></div>
        <Button variant="ghost" onClick={onClose}>Cancelar</Button>
        <Button icon="check" onClick={save} disabled={!title.trim() || !body.trim()}>{editing ? "Salvar" : "Publicar"}</Button>
      </>}
    >
      <div className="pr-editor">
        <Field label="Categoria">
          <Select2 value={category} onChange={(v) => setCategory(v)}
            options={postable.map((c) => ({ value: c.id, label: c.label, color: c.accent }))} />
        </Field>
        <Field label="Título">
          <Input placeholder="Ex: Evento BedWars — Sábado 20h" value={title} maxLength={90} onChange={(e) => setTitle(e.target.value)} />
        </Field>
        <Field label="Conteúdo">
          <div className="pr-editor-tabs">
            <button type="button" className={"pr-etab" + (tab === "editar" ? " active" : "")} onClick={() => setTab("editar")}>
              <Icon name="edit" size={15} /> Editar
            </button>
            <button type="button" className={"pr-etab" + (tab === "ver" ? " active" : "")} onClick={() => setTab("ver")}>
              <Icon name="eye" size={15} /> Visualizar
            </button>
          </div>
          {tab === "editar" ? (
            <>
              <div className="pr-mdtoolbar">
                {tools.map((b) => (
                  <button key={b.t} type="button" className="pr-mdbtn" title={b.t} onClick={b.fn}>
                    <span style={{ ...b.style, fontSize: b.small ? 12 : 15 }}>{b.label}</span>
                  </button>
                ))}
                <span className="pr-mdsep"></span>
                <button type="button" className="pr-mdbtn img" title="Adicionar imagem (aparece abaixo da mensagem)" onClick={() => imgRef.current && imgRef.current.click()}>
                  <Icon name="plus" size={15} /> Imagem
                </button>
                <input ref={imgRef} type="file" accept="image/*" multiple hidden onChange={addImages} />
              </div>
              <MentionTextarea taRef={bodyRef} rows={9} members={members || []}
                placeholder="Escreva o comunicado... use @ para mencionar, 😀 para emoji, e selecione texto + botões pra formatar."
                value={body} onChange={setBody} />
              {images.length > 0 && (
                <div className="pr-editor-imgs">
                  {images.map((src, i) => (
                    <div key={i} className="pr-editor-imgchip">
                      <img src={src} alt="" />
                      <button type="button" className="pr-imgremove" title="Remover imagem" onClick={() => removeImage(i)}>
                        <Icon name="x" size={14} />
                      </button>
                    </div>
                  ))}
                </div>
              )}
              {images.length > 0 && <span className="pr-field-hint" style={{ marginTop: 4 }}>As imagens aparecem abaixo da mensagem, na ordem acima.</span>}
            </>
          ) : (
            <div className="pr-preview">
              <div className="pr-preview-tag"><CatChip cat={category} categories={cats} small /></div>
              <h2 className="pr-preview-title">{title || "Sem título"}</h2>
              <div className="pr-detail-body" dangerouslySetInnerHTML={richText(body || "_Sem conteúdo_", images, members)}></div>
              {images.length > 0 && (
                <div className="pr-detail-gallery">
                  {images.map((src, i) => <img key={i} className="pr-bodyimg" src={src} alt="" />)}
                </div>
              )}
            </div>
          )}
        </Field>
      </div>
    </Modal>
  );
}

/* ---------- Render de markdown estilo Discord ---------- */
function richText(s, images, members) {
  images = images || [];
  members = members || [];
  const escHtml = (t) => String(t).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");

  // 1. blocos de código ```...```
  const codeBlocks = [];
  s = s.replace(/```([\s\S]*?)```/g, (m, c) => {
    codeBlocks.push(c.replace(/^\n+/, "").replace(/\n+$/, ""));
    return `\uE000CB${codeBlocks.length - 1}\uE001`;
  });
  // 2. código inline `...`
  const inlineCodes = [];
  s = s.replace(/`([^`\n]+?)`/g, (m, c) => {
    inlineCodes.push(c);
    return `\uE000IC${inlineCodes.length - 1}\uE001`;
  });

  function inline(line) {
    let t = escHtml(line);
    t = t.replace(/\[\[img:\d+\]\]/g, "");
    // links mascarados [texto](url) + URLs soltas → guardados em tokens p/ não sofrerem outras formatações
    const linkStore = [];
    const mkLink = (text, url) => {
      const safe = /^(https?:|mailto:)/i.test(url) ? url : "https://" + url.replace(/^\/+/, "");
      linkStore.push(`<a class="pr-link" href="${safe}" target="_blank" rel="noopener noreferrer">${text}</a>`);
      return `\uE000LK${linkStore.length - 1}\uE001`;
    };
    t = t.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (m, text, url) => mkLink(text, url));
    t = t.replace(/(^|[\s(])((?:https?:\/\/)[^\s<]+)/g, (m, pre, url) => {
      const trail = (url.match(/[.,!?)]+$/) || [""])[0];
      const clean = url.slice(0, url.length - trail.length);
      return pre + mkLink(clean, clean) + trail;
    });
    // menções @usuario / @Nome / @equipe (grupo)
    t = t.replace(/@([\w.]+)/g, (m, name) => {
      const key = name.toLowerCase();
      if (PR.GROUP_KEYS.includes(key)) {
        const g = PR.MENTION_GROUPS.find((x) => x.key === key);
        const col = g.color || "var(--gold)";
        return `<span class="pr-mention pr-mention-all" style="color:${col};background:color-mix(in oklab, ${col} 18%, transparent)">@${g.label.toLowerCase()}</span>`;
      }
      const mem = members.find((x) => x.username.toLowerCase() === key || x.display.toLowerCase() === key);
      return mem ? `<span class="pr-mention">@${mem.display}</span>` : m;
    });
    // emoji por shortcode :nome:
    t = t.replace(/:([a-z0-9_+-]+):/gi, (m, code) => PR.EMOJI_MAP[code.toLowerCase()] || m);
    t = t.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
    t = t.replace(/__([^_]+)__/g, "<u>$1</u>");
    t = t.replace(/~~([^~]+)~~/g, "<s>$1</s>");
    t = t.replace(/\*([^*\n]+)\*/g, "<em>$1</em>");
    t = t.replace(/\uE000IC(\d+)\uE001/g, (m, idx) => `<code class="pr-code">${escHtml(inlineCodes[+idx])}</code>`);
    t = t.replace(/\uE000LK(\d+)\uE001/g, (m, idx) => linkStore[+idx]);
    return t;
  }

  const lines = s.split("\n");
  const out = [];
  let i = 0;
  while (i < lines.length) {
    const h = lines[i].match(/^(#{1,3})\s+(.*)$/);
    if (h) {
      const lvl = h[1].length;
      out.push(`<div class="pr-h pr-h${lvl}">${inline(h[2])}</div>`);
      i++;
    } else if (/^>\s?/.test(lines[i])) {
      const quote = [];
      while (i < lines.length && /^>\s?/.test(lines[i])) {
        quote.push(inline(lines[i].replace(/^>\s?/, "")));
        i++;
      }
      out.push(`<blockquote class="pr-quote">${quote.join("<br/>")}</blockquote>`);
    } else {
      out.push(inline(lines[i]));
      i++;
    }
  }
  let html = out.join("<br/>").replace(/<br\/>(<blockquote)/g, "$1").replace(/(<\/blockquote>)<br\/>/g, "$1");
  html = html.replace(/<br\/>(<div class="pr-h)/g, "$1").replace(/(<\/div>)<br\/>/g, "$1");

  // restaura blocos de código
  html = html.replace(/\uE000CB(\d+)\uE001/g, (m, idx) => `<pre class="pr-pre"><code>${escHtml(codeBlocks[+idx])}</code></pre>`);
  html = html.replace(/<br\/>(<pre)/g, "$1").replace(/(<\/pre>)<br\/>/g, "$1");
  return { __html: html };
}

/* ============================================================
   Item da lista de posts
   ============================================================ */
function PostListItem({ post, members, categories, currentUser, onOpen, onEdit, onTogglePin, onDelete, onToggleResolved }) {
  const author = memberById(members, post.authorId);
  const [menu, setMenu] = useState(false);
  const [menuPos, setMenuPos] = useState(null);
  const kebabRef = useRef(null);
  const isAuthor = post.authorId === currentUser.id;
  const canEdit = PR.can(currentUser.role, "post.editAny") || (isAuthor && PR.can(currentUser.role, "post.editOwn"));
  const canPin = PR.can(currentUser.role, "post.pin");
  const excerpt = post.body
    .replace(/```[\s\S]*?```/g, " ")
    .replace(/\[\[img:\d+\]\]/g, " ")
    .replace(/[*_~`>]/g, "")
    .split("\n").map((l) => l.trim()).filter(Boolean)[0] || "";

  function openMenu(e) {
    e.stopPropagation();
    if (menu) { setMenu(false); return; }
    const r = kebabRef.current.getBoundingClientRect();
    const W = 188, H = 224;
    let left = Math.max(12, Math.min(r.right - W, window.innerWidth - W - 12));
    let top = r.bottom + 6, flip = false;
    if (window.innerHeight - r.bottom < H && r.top > H) { top = r.top - 6; flip = true; }
    setMenuPos({ left, top, flip });
    setMenu(true);
  }
  function act(fn) { return (e) => { e.stopPropagation(); setMenu(false); fn(); }; }

  return (
    <article className="pr-postitem" onClick={() => onOpen(post.id)}>
      <div className="pr-postitem-av"><Avatar member={author} size={52} /></div>
      <div className="pr-postitem-content">
        <div className="pr-postitem-badges">
          {post.pinned && <span className="pr-flag pin"><Icon name="pin" size={12} /> FIXADO</span>}
          {post.resolved && <span className="pr-flag resolved"><Icon name="check" size={12} /> RESOLVIDO</span>}
          <CatChip cat={post.category} categories={categories} small />
        </div>
        <h3 className="pr-postitem-title">{post.title}</h3>
        {excerpt && <p className="pr-postitem-excerpt">{excerpt}</p>}
        <div className="pr-postitem-meta">
          <Medal role={author.role} size={15} />
          <span className="pr-postitem-name">{author.display}</span>
          <RoleBadge role={author.role} size="sm" />
          <span className="pr-meta-dot">·</span>
          <span className="pr-postitem-time">{PR.fmtRelative(post.createdAt)}</span>
        </div>
      </div>
      <div className="pr-postitem-side" onClick={(e) => e.stopPropagation()}>
        <div className="pr-kebab-wrap">
          <button ref={kebabRef} className="pr-iconbtn pr-kebab" title="Opções" onClick={openMenu}>
            <Icon name="dots" size={18} />
          </button>
          {menu && menuPos && (
            <Portal>
              <div className="pr-kebab-scrim" onClick={(e) => { e.stopPropagation(); setMenu(false); }}></div>
              <div className="pr-kebab-menu" style={{ position: "fixed", zIndex: 9999, left: menuPos.left, top: menuPos.top, transform: menuPos.flip ? "translateY(-100%)" : "none" }}>
                <button onClick={act(() => onOpen(post.id))}><Icon name="eye" size={16} /> Visualizar</button>
                {canEdit && <button onClick={act(() => onEdit(post))}><Icon name="edit" size={16} /> Editar</button>}
                {canPin && <button onClick={act(() => onTogglePin(post.id))}><Icon name="pin" size={16} /> {post.pinned ? "Desafixar" : "Fixar"}</button>}
                {canEdit && <button onClick={act(() => onToggleResolved(post.id))}><Icon name="check" size={16} /> {post.resolved ? "Reabrir" : "Marcar resolvido"}</button>}
                {canEdit && <button className="danger" onClick={act(() => onDelete(post.id))}><Icon name="trash" size={16} /> Remover</button>}
              </div>
            </Portal>
          )}
        </div>
        <div className="pr-postitem-stats">
          {post.reactions && Object.keys(post.reactions).filter((k) => post.reactions[k].length).length > 0 && (
            <span className="pr-postitem-react">{Object.keys(post.reactions).filter((k) => post.reactions[k].length).slice(0, 3).join("")} {Object.keys(post.reactions).reduce((n, k) => n + post.reactions[k].length, 0)}</span>
          )}
          <span className="pr-postitem-replies"><Icon name="reply" size={15} />{post.replies.length}</span>
        </div>
      </div>
    </article>
  );
}

/* ============================================================
   Feed do fórum
   ============================================================ */
function ForumView({ posts, members, categories, currentUser, onOpen, onNew, onEdit, onTogglePin, onDelete, onToggleResolved }) {
  const [cat, setCat] = useState("todos");
  const [q, setQ] = useState("");

  const viewable = categories.filter((c) => PR.canCat(currentUser.role, c, "view"));
  const viewableIds = viewable.map((c) => c.id);
  const canCreate = categories.some((c) => PR.canCat(currentUser.role, c, "post"));

  let list = posts.filter((p) => viewableIds.includes(p.category));
  if (cat !== "todos") list = list.filter((p) => p.category === cat);
  if (q.trim()) {
    const t = q.toLowerCase();
    list = list.filter((p) => p.title.toLowerCase().includes(t) || p.body.toLowerCase().includes(t));
  }
  list.sort((a, b) => (b.pinned - a.pinned) || (b.createdAt - a.createdAt));

  const tabs = [{ id: "todos", label: "Todos" }, ...viewable.map((c) => ({ id: c.id, label: c.label }))];

  return (
    <div className="pr-page">
      <div className="pr-pagehead">
        <div>
          <h2 className="pr-pagetitle">Fórum da Promotoria</h2>
          <p className="pr-pagesub">Comunicados, eventos e avisos internos da equipe.</p>
        </div>
        {canCreate && (
          <Button icon="plus" onClick={onNew}>Nova postagem</Button>
        )}
      </div>

      <div className="pr-forum-toolbar">
        <div className="pr-tabs">
          {tabs.map((t) => (
            <button key={t.id} className={"pr-tab" + (cat === t.id ? " active" : "")} onClick={() => setCat(t.id)}>
              {t.label}
            </button>
          ))}
        </div>
        <div className="pr-search">
          <Icon name="search" size={16} />
          <input placeholder="Buscar postagem..." value={q} onChange={(e) => setQ(e.target.value)} />
        </div>
      </div>

      {list.length === 0 ? (
        <div className="pr-empty"><Icon name="feed" size={28} /><p>Nenhuma postagem por aqui ainda.</p></div>
      ) : (
        <div className="pr-postlist">
          {list.map((p) => <PostListItem key={p.id} post={p} members={members} categories={categories} currentUser={currentUser}
            onOpen={onOpen} onEdit={onEdit} onTogglePin={onTogglePin} onDelete={onDelete} onToggleResolved={onToggleResolved} />)}
        </div>
      )}
    </div>
  );
}

/* ============================================================
   Detalhe do post + respostas
   ============================================================ */
function PostDetail({ post, members, categories, currentUser, onBack, onEdit, onDelete, onReply, onTogglePin, onDeleteReply, onToggleReaction, onToggleResolved }) {
  const [reply, setReply] = useState("");
  const [reactOpen, setReactOpen] = useState(false);
  const author = memberById(members, post.authorId);
  const cat = (categories || PR.CATEGORIES).find((c) => c.id === post.category);
  const isAuthor = post.authorId === currentUser.id;
  const canEdit = PR.can(currentUser.role, "post.editAny") || (isAuthor && PR.can(currentUser.role, "post.editOwn"));
  const canPin = PR.can(currentUser.role, "post.pin");
  const canReply = PR.canCat(currentUser.role, cat, "reply");
  const canReact = PR.canCat(currentUser.role, cat, "react");
  const reactions = post.reactions || {};
  const reactEmojis = ["👍","🔥","🎉","❤️","😂","👀","🏆","✅"];

  function sendReply() {
    if (!reply.trim()) return;
    onReply(post.id, reply.trim());
    setReply("");
  }

  return (
    <div className="pr-page pr-detail">
      <button className="pr-backlink" onClick={onBack}><Icon name="back" size={16} /> Voltar ao fórum</button>

      <div className="pr-detail-grid">
        <div className="pr-detail-main">
          <div className="pr-postcard">
            <div className="pr-postcard-head">
              {post.pinned && <span className="pr-flag pin"><Icon name="pin" size={12} /> FIXADO</span>}
              {post.resolved && <span className="pr-flag resolved"><Icon name="check" size={12} /> RESOLVIDO</span>}
              <CatChip cat={post.category} categories={categories} />
              <div style={{ flex: 1 }}></div>
              {canEdit && (
                <div className="pr-postcard-actions">
                  {canPin && <button className="pr-iconbtn" title={post.pinned ? "Desafixar" : "Fixar"} onClick={() => onTogglePin(post.id)}><Icon name="pin" size={17} style={{ opacity: post.pinned ? 1 : 0.5 }} /></button>}
                  <button className="pr-iconbtn" title={post.resolved ? "Reabrir" : "Marcar como resolvido"} onClick={() => onToggleResolved(post.id)}><Icon name="check" size={17} style={{ opacity: post.resolved ? 1 : 0.5 }} /></button>
                  <button className="pr-iconbtn" title="Editar" onClick={() => onEdit(post)}><Icon name="edit" size={17} /></button>
                  <button className="pr-iconbtn danger" title="Remover" onClick={() => onDelete(post.id)}><Icon name="trash" size={17} /></button>
                </div>
              )}
            </div>

            <h1 className="pr-detail-title">{post.title}</h1>

            <div className="pr-detail-author">
              <Avatar member={author} size={42} />
              <div>
                <div className="pr-detail-authorline">
                  <Medal role={author.role} size={16} />
                  <strong>{author.display}</strong>
                  <RoleBadge role={author.role} size="sm" />
                </div>
                <span className="pr-detail-meta">Post original · {PR.fmtRelative(post.createdAt)} · {PR.fmtDate(post.createdAt)}</span>
              </div>
            </div>

            <div className="pr-detail-body" dangerouslySetInnerHTML={richText(post.body, post.images, members)}></div>
            {post.images && post.images.length > 0 && (
              <div className="pr-detail-gallery">
                {post.images.map((src, i) => <img key={i} className="pr-bodyimg" src={src} alt="" />)}
              </div>
            )}

            <div className="pr-reactbar">
              {Object.keys(reactions).filter((e) => reactions[e] && reactions[e].length).map((e) => {
                const mine = reactions[e].includes(currentUser.id);
                return (
                  <button key={e} className={"pr-reactchip" + (mine ? " mine" : "")}
                    title={reactions[e].map((id) => memberById(members, id).display).join(", ")}
                    onClick={() => canReact && onToggleReaction(post.id, e)} disabled={!canReact}>
                    <span>{e}</span><b>{reactions[e].length}</b>
                  </button>
                );
              })}
              {canReact && (
                <div className="pr-react-add-wrap">
                  <button className="pr-reactchip add" title="Reagir" onClick={() => setReactOpen((o) => !o)}><Icon name="smile" size={16} /></button>
                  {reactOpen && (
                    <>
                      <div className="pr-kebab-scrim" onClick={() => setReactOpen(false)}></div>
                      <div className="pr-react-pop">
                        {reactEmojis.map((e) => (
                          <button key={e} onClick={() => { onToggleReaction(post.id, e); setReactOpen(false); }}>{e}</button>
                        ))}
                      </div>
                    </>
                  )}
                </div>
              )}
              {!canReact && Object.keys(reactions).filter((e) => reactions[e] && reactions[e].length).length === 0 && (
                <span className="pr-react-locked">Seu cargo não pode reagir nesta categoria.</span>
              )}
            </div>
          </div>

          <div className="pr-replies-head">
            <Icon name="reply" size={17} />
            <h3>Respostas</h3>
            <span className="pr-count">{post.replies.length}</span>
          </div>

          <div className="pr-replies">
            {post.replies.length === 0 && <div className="pr-noreplies">Seja o primeiro a responder.</div>}
            {post.replies.map((r, i) => {
              const ra = memberById(members, r.authorId);
              const canDelR = PR.can(currentUser.role, "post.editAny") || r.authorId === currentUser.id;
              return (
                <div className="pr-reply" key={r.id}>
                  <Avatar member={ra} size={36} />
                  <div className="pr-reply-body">
                    <div className="pr-reply-head">
                      <Medal role={ra.role} size={14} />
                      <strong>{ra.display}</strong>
                      <RoleBadge role={ra.role} size="sm" />
                      <span className="pr-reply-meta">#{i + 2} · {PR.fmtRelative(r.createdAt)}</span>
                      {canDelR && <button className="pr-iconbtn danger tiny" title="Remover resposta" onClick={() => onDeleteReply(post.id, r.id)}><Icon name="trash" size={14} /></button>}
                    </div>
                    <p dangerouslySetInnerHTML={richText(r.body, null, members)}></p>
                  </div>
                </div>
              );
            })}
          </div>

          <div className="pr-replybox">
            <Avatar member={currentUser} size={36} />
            <div className="pr-replybox-input">
              {canReply ? (
                <>
                  <MentionTextarea rows={2} placeholder="Escreva uma resposta... use @ para mencionar" members={members}
                    value={reply}
                    onChange={setReply}
                    onKeyDown={(e) => { if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) sendReply(); }} />
                  <div className="pr-replybox-foot">
                    <span className="pr-hint-mini">Ctrl/⌘ + Enter para enviar</span>
                    <Button size="sm" icon="reply" onClick={sendReply} disabled={!reply.trim()}>Responder</Button>
                  </div>
                </>
              ) : (
                <div className="pr-reply-locked"><Icon name="lock" size={15} /> Seu cargo não pode responder nesta categoria.</div>
              )}
            </div>
          </div>
        </div>

        <aside className="pr-detail-side">
          <div className="pr-sidecard">
            <div className="pr-sidecard-row"><span>Categoria</span><CatChip cat={post.category} categories={categories} small /></div>
            <div className="pr-sidecard-row"><span>Autor</span><strong>{author.display}</strong></div>
            <div className="pr-sidecard-row"><span>Criado</span><strong>{PR.fmtDate(post.createdAt)}</strong></div>
            <div className="pr-sidecard-row"><span>Respostas</span><strong>{post.replies.length}</strong></div>
            <div className="pr-sidecard-row"><span>Status</span><strong style={{ color: post.pinned ? "var(--gold)" : "var(--muted)" }}>{post.pinned ? "Fixado" : "Normal"}</strong></div>
          </div>
        </aside>
      </div>
    </div>
  );
}

Object.assign(window, { ForumView, PostDetail, PostEditor, CatChip, memberById, richText });
