/* OPENBUREAU Dialog — Foren, Threads & flache Wortmeldungen.
   Lesen öffentlich; Mitreden nach Login. Kein Build nötig.
   Routen über Query-Parameter:
     /dialog/                  → Übersicht (Split-View: letzte Beiträge | Foren)
     /dialog/?forum=<slug>     → ein Forum mit seinen Threads
     /dialog/?thread=<key>     → ein Thread mit Wortmeldungen
*/
(function () {
  const root = document.getElementById('ob-dialog');
  if (!root) return;
  const ctxEl = document.getElementById('dialog-context');

  const TKEY = 'ob_dialog_token', NKEY = 'ob_dialog_name', RKEY = 'ob_dialog_role';
  let token = localStorage.getItem(TKEY);
  let myName = localStorage.getItem(NKEY);
  let myRole = localStorage.getItem(RKEY) || 'user';
  const canModerate = () => token && (myRole === 'admin' || myRole === 'editor');

  const params = new URLSearchParams(location.search);
  const threadKey = params.get('thread');
  const forumSlug = params.get('forum');

  const el = (tag, cls, txt) => { const e = document.createElement(tag); if (cls) e.className = cls; if (txt != null) e.textContent = txt; return e; };
  function fmt(ts) {
    const d = new Date(ts), s = (Date.now() - d) / 1000;
    if (s < 60) return 'gerade eben';
    if (s < 3600) return Math.floor(s / 60) + ' Min.';
    if (s < 86400) return Math.floor(s / 3600) + ' Std.';
    return d.toLocaleDateString('de-CH');
  }
  // Volles Datum + Uhrzeit (für die Wortmeldungen in der Thread-Ansicht).
  function fmtFull(ts) {
    const d = new Date(ts);
    return d.toLocaleDateString('de-CH') + ' · ' + d.toLocaleTimeString('de-CH', { hour: '2-digit', minute: '2-digit' });
  }
  const api = (p, opt) => fetch(p, opt).then(async (r) => ({ ok: r.ok, status: r.status, body: await r.json().catch(() => ({})) }));
  const authHdr = () => ({ Authorization: 'Bearer ' + token });

  // ── UX-Helfer ─────────────────────────────────────────────────────────────
  // Deterministische, dezente Avatar-Farbe aus dem Namen (wenn kein Bild).
  function hashHue(s) { let h = 0; for (let i = 0; i < (s || '').length; i++) h = (h * 31 + s.charCodeAt(i)) | 0; return Math.abs(h) % 360; }
  function paintAvatar(av, name, avatarUrl) {
    if (avatarUrl) { av.style.backgroundImage = 'url(' + avatarUrl + ')'; av.textContent = ''; return; }
    const hue = hashHue(name || '?');
    av.style.background = 'hsl(' + hue + ' 36% 82%)';
    av.style.color = 'hsl(' + hue + ' 30% 28%)';
    av.textContent = (name || '?').trim().slice(0, 1).toUpperCase();
  }
  // URLs im Text klickbar machen — sicher: nur Text- + Anchor-Knoten, kein innerHTML.
  function linkify(container, text) {
    const re = /(https?:\/\/[^\s<]+)/g; let last = 0, m;
    while ((m = re.exec(text))) {
      if (m.index > last) container.appendChild(document.createTextNode(text.slice(last, m.index)));
      const a = el('a', 'dialog-link', m[0].replace(/[.,;:)]+$/, ''));
      a.href = a.textContent; a.target = '_blank'; a.rel = 'noopener noreferrer';
      container.appendChild(a);
      last = m.index + a.textContent.length;
    }
    if (last < text.length) container.appendChild(document.createTextNode(text.slice(last)));
  }
  // Dezente Lade-Platzhalter (schimmernd).
  function skeleton(container, n) {
    container.innerHTML = '';
    const w = el('div', 'dialog-skel');
    for (let i = 0; i < (n || 3); i++) w.appendChild(el('div', 'dialog-skel-line'));
    container.appendChild(w);
  }
  // ⌘/Ctrl + Enter sendet ab.
  function sendOnCmdEnter(ta, fn) {
    ta.addEventListener('keydown', (e) => { if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') { e.preventDefault(); fn(); } });
  }
  // Textarea wächst mit dem Inhalt mit (bis zu einer Grenze).
  function autoGrow(ta, max) {
    const fit = () => { ta.style.height = 'auto'; ta.style.height = Math.min(ta.scrollHeight, max || 320) + 'px'; };
    ta.addEventListener('input', fit); requestAnimationFrame(fit);
  }

  // ── Auth ────────────────────────────────────────────────────────────────
  async function doLogin(email, password, after) {
    const r = await api('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) });
    if (!r.ok) { alert(r.body.error || 'Login fehlgeschlagen'); return; }
    token = r.body.access_token; myName = r.body.name || ''; myRole = r.body.role || 'user';
    localStorage.setItem(TKEY, token); localStorage.setItem(NKEY, myName); localStorage.setItem(RKEY, myRole);
    if (after) after();
  }
  function logout(after) {
    token = null; myName = null; myRole = 'user';
    localStorage.removeItem(TKEY); localStorage.removeItem(NKEY); localStorage.removeItem(RKEY);
    if (after) after();
  }
  // Subtiles Login: dezente Zeile, klappt zum kompakten Formular auf.
  function loginInline(container, after, label) {
    let open = false;
    function paint() {
      container.innerHTML = '';
      if (!open) {
        const link = el('button', 'dialog-loginlink', label || 'Zum Mitreden anmelden');
        link.onclick = () => { open = true; paint(); };
        container.appendChild(link);
      } else {
        const form = el('div', 'dialog-loginform');
        const em = el('input', 'dialog-input'); em.type = 'email'; em.placeholder = 'E-Mail';
        const pw = el('input', 'dialog-input'); pw.type = 'password'; pw.placeholder = 'Passwort';
        const btn = el('button', 'dialog-send', 'Anmelden');
        btn.onclick = () => doLogin(em.value, pw.value, after);
        pw.onkeydown = (e) => { if (e.key === 'Enter') doLogin(em.value, pw.value, after); };
        const cancel = el('button', 'dialog-loginlink dialog-logincancel', 'Abbrechen');
        cancel.onclick = () => { open = false; paint(); };
        form.append(em, pw, btn, cancel); container.appendChild(form); em.focus();
      }
    }
    paint();
  }

  // ── Übersicht: Split-View ─────────────────────────────────────────────────
  function renderOverview() {
    if (ctxEl) ctxEl.textContent = '';
    root.innerHTML = '';
    const grid = el('div', 'dialog-split');
    const left = el('div', 'dialog-recent');
    const right = el('div', 'dialog-forums');
    left.appendChild(el('h2', 'dialog-title', 'Letzte Wortmeldungen'));
    right.appendChild(el('h2', 'dialog-title', 'Foren'));
    grid.append(left, right); root.appendChild(grid);

    const recentSkel = el('div'); left.appendChild(recentSkel); skeleton(recentSkel, 5);
    const forumSkel = el('div'); right.appendChild(forumSkel); skeleton(forumSkel, 4);

    api('/api/recent?limit=15').then((r) => {
      recentSkel.remove();
      const rows = r.body || [];
      if (!rows.length) { left.appendChild(el('p', 'dialog-empty', 'Noch keine Wortmeldungen.')); return; }
      const list = el('div', 'dialog-recent-list');
      rows.forEach((c) => {
        const a = el('a', 'dialog-recent-item'); a.href = c.thread_url;
        const top = el('div', 'dialog-recent-top');
        top.append(el('span', 'dialog-recent-author', c.author_name || 'Unbekannt'),
          el('span', 'dialog-recent-meta', (c.forum_name ? c.forum_name + ' · ' : '') + fmt(c.created_at)));
        const tt = el('div', 'dialog-recent-thread', c.thread_title);
        const bd = el('div', 'dialog-recent-body', c.body);
        a.append(top, tt, bd); list.appendChild(a);
      });
      left.appendChild(list);
    });

    api('/api/forums').then((r) => {
      forumSkel.remove();
      const rows = r.body || [];
      const list = el('div', 'dialog-forum-list');
      rows.forEach((f) => {
        const a = el('a', 'dialog-forum-item'); a.href = '/dialog/?forum=' + encodeURIComponent(f.slug);
        if (f.color) a.style.setProperty('--forum-accent', f.color);
        const nm = el('span', 'dialog-forum-name', f.name);
        const mt = el('span', 'dialog-forum-meta',
          f.thread_count + (f.thread_count === 1 ? ' Thread' : ' Threads') + ' · ' + f.post_count + ' Beiträge');
        a.append(nm, mt);
        if (f.description) a.appendChild(el('span', 'dialog-forum-desc', f.description));
        list.appendChild(a);
      });
      right.appendChild(list);
    });
  }

  // ── Forum-Ansicht: Threads + neuer Thread ─────────────────────────────────
  function renderForum(slug) {
    root.innerHTML = '';
    if (ctxEl) { ctxEl.innerHTML = ''; const c = el('nav', 'dialog-crumb'); const b = el('a', null, '← Dialoge'); b.href = '/dialog/'; c.appendChild(b); ctxEl.appendChild(c); }
    const loadHost = el('div'); skeleton(loadHost, 4); root.appendChild(loadHost);
    api('/api/forums/' + encodeURIComponent(slug)).then((r) => {
      loadHost.remove();
      if (!r.ok) { root.appendChild(el('p', 'dialog-empty', 'Forum nicht gefunden.')); return; }
      const { forum, threads } = r.body;
      const head = el('div', 'dialog-forum-head');
      head.appendChild(el('h2', 'dialog-title', forum.name));
      if (forum.color) head.style.setProperty('--forum-accent', forum.color);
      root.appendChild(head);
      if (forum.description) root.appendChild(el('p', 'dialog-forum-desc', forum.description));

      // Neuer Thread (nur Forum-Kategorien, nicht „Beiträge").
      if (forum.kind !== 'library') {
        const newBox = el('div', 'dialog-newthread');
        root.appendChild(newBox);
        renderNewThread(newBox, forum.slug);
      }

      const list = el('div', 'dialog-thread-list');
      if (!threads.length) list.appendChild(el('p', 'dialog-empty', 'Noch keine Threads — beginne einen.'));
      threads.forEach((t) => {
        const a = el('a', 'dialog-thread-item');
        a.href = t.kind === 'library' ? t.url : '/dialog/?thread=' + encodeURIComponent(t.key);
        const tt = el('span', 'dialog-thread-title', t.title);
        if (t.locked) tt.appendChild(el('span', 'dialog-lock', ' 🔒'));
        const mt = el('span', 'dialog-thread-meta',
          (t.author_name ? t.author_name + ' · ' : '') + t.count + (t.count === 1 ? ' Wortmeldung' : ' Wortmeldungen') + ' · ' + fmt(t.last));
        a.append(tt, mt); list.appendChild(a);
      });
      root.appendChild(list);
    });
  }

  function renderNewThread(box, slug) {
    box.innerHTML = '';
    if (!token) { const c = el('div'); box.appendChild(c); loginInline(c, () => renderNewThread(box, slug), 'Anmelden, um einen Thread zu starten'); return; }
    let open = false;
    function paint() {
      box.innerHTML = '';
      if (!open) {
        const b = el('button', 'dialog-newbtn', '＋ Neuer Thread');
        b.onclick = () => { open = true; paint(); };
        box.appendChild(b);
        return;
      }
      const ti = el('input', 'dialog-input'); ti.placeholder = 'Titel des Threads';
      const ta = el('textarea', 'dialog-textarea'); ta.placeholder = 'Erster Beitrag …';
      const row = el('div', 'dialog-row');
      const send = el('button', 'dialog-send', 'Thread starten');
      const cancel = el('button', 'dialog-loginlink', 'Abbrechen'); cancel.onclick = () => { open = false; paint(); };
      send.onclick = async () => {
        if (!ti.value.trim() || !ta.value.trim()) return;
        const r = await api('/api/threads', { method: 'POST', headers: { 'Content-Type': 'application/json', ...authHdr() }, body: JSON.stringify({ forum_slug: slug, title: ti.value, body: ta.value }) });
        if (r.status === 401) { logout(); alert('Sitzung abgelaufen — bitte neu anmelden.'); paint(); return; }
        if (!r.ok) { alert(r.body.error || 'Konnte Thread nicht anlegen'); return; }
        location.href = '/dialog/?thread=' + encodeURIComponent(r.body.key);
      };
      autoGrow(ta); sendOnCmdEnter(ta, () => send.click());
      ti.addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); ta.focus(); } });
      row.append(send, cancel); box.append(ti, ta, row); ti.focus();
    }
    paint();
  }

  // ── Thread-Ansicht: Wortmeldungen ─────────────────────────────────────────
  function renderThread(key) {
    root.innerHTML = '';
    const title = el('h2', 'dialog-title', 'Dialog');
    const modbar = el('div', 'dialog-modbar');
    const list = el('div', 'dialog-list');
    const composer = el('div', 'dialog-composer');
    root.append(title, modbar, list, composer);
    skeleton(list, 3);
    let replyTo = null, textarea = null, locked = false;

    // Kontext: Rücklink + Titel.
    api('/api/thread?key=' + encodeURIComponent(key)).then((r) => {
      if (!r.ok) return;
      const m = r.body; title.textContent = m.title || 'Dialog'; locked = m.locked;
      if (ctxEl) {
        ctxEl.innerHTML = '';
        if (m.kind === 'library') {
          const back = el('a', null, '← zum Beitrag'); back.href = m.url; ctxEl.appendChild(back);
        } else {
          // Breadcrumb-Navigation oben: Dialoge › Forum (beide anklickbar).
          const crumb = el('nav', 'dialog-crumb');
          const a0 = el('a', null, 'Dialoge'); a0.href = '/dialog/'; crumb.appendChild(a0);
          if (m.forum) {
            crumb.appendChild(el('span', 'dialog-crumb-sep', '›'));
            const a1 = el('a', null, m.forum.name); a1.href = '/dialog/?forum=' + encodeURIComponent(m.forum.slug);
            crumb.appendChild(a1);
          }
          ctxEl.appendChild(crumb);
        }
      }
      renderModbar(); renderComposer();
    });

    function renderModbar() {
      modbar.innerHTML = '';
      if (!canModerate()) return;
      const lock = el('button', 'dialog-modbtn', locked ? 'Entsperren' : 'Sperren');
      lock.onclick = async () => {
        await api('/api/mod/thread-lock', { method: 'POST', headers: { 'Content-Type': 'application/json', ...authHdr() }, body: JSON.stringify({ key, locked: !locked }) });
        locked = !locked; renderModbar(); renderComposer();
      };
      const del = el('button', 'dialog-modbtn', 'Thread ausblenden');
      del.onclick = async () => {
        if (!confirm('Thread ausblenden?')) return;
        await api('/api/mod/thread-delete', { method: 'POST', headers: { 'Content-Type': 'application/json', ...authHdr() }, body: JSON.stringify({ key }) });
        location.href = '/dialog/';
      };
      modbar.append(el('span', 'dialog-modlabel', 'Moderation:'), lock, del);
    }

    let lastSig = '';
    async function load() {
      const r = await api('/api/comments?thread=' + encodeURIComponent(key));
      if (!r.ok) return;
      const data = r.body;
      const sig = (token ? 'in:' : 'out:') + (canModerate() ? 'm' : '') + data.map((c) => c.id + (c.deleted ? 'd' : '')).join(',');
      if (sig !== lastSig) { lastSig = sig; render(data); }
    }

    function render(data) {
      list.innerHTML = '';
      const names = {}; data.forEach((c) => { names[c.id] = c.author_name; });
      if (!data.length) { list.appendChild(el('p', 'dialog-empty', 'Noch keine Wortmeldungen — beginne den Dialog.')); return; }
      data.forEach((c) => {
        // Nüchtern: Avatar · Name (+ Position) · darunter Datum/Uhrzeit · Text.
        const post = el('article', 'dialog-post');
        const head = el('header', 'dialog-post-head');
        const av = el('span', 'dialog-avatar');
        paintAvatar(av, c.author_name, c.author_avatar);
        const ident = el('div', 'dialog-ident');
        const nameline = el('div', 'dialog-nameline');
        nameline.appendChild(el('span', 'dialog-name', c.author_name || 'Unbekannt'));
        if (c.author_role) nameline.appendChild(el('span', 'dialog-pos', c.author_role));
        if (c.parent_id && names[c.parent_id]) nameline.appendChild(el('span', 'dialog-replyto', '↳ ' + names[c.parent_id]));
        ident.append(nameline, el('time', 'dialog-time', fmtFull(c.created_at)));
        head.append(av, ident); post.appendChild(head);
        const bodyEl = el('div', 'dialog-body'); linkify(bodyEl, c.body || ''); post.appendChild(bodyEl);
        if (token && !c.deleted) {
          const actions = el('div', 'dialog-actions');
          if (!locked) { const rep = el('button', null, 'Antworten'); rep.onclick = () => { replyTo = { id: c.id, name: c.author_name }; renderComposer(); if (textarea) textarea.focus(); }; actions.appendChild(rep); }
          const del = el('button', null, 'Löschen'); del.onclick = () => remove(c.id); actions.appendChild(del);
          post.appendChild(actions);
        }
        list.appendChild(post);
      });
    }

    function renderComposer() {
      composer.innerHTML = '';
      // Gesperrt: niemand schreibt (auch Moderation nicht — erst entsperren).
      if (locked) {
        composer.appendChild(el('p', 'dialog-locked', canModerate()
          ? '🔒 Gesperrt — zum Schreiben oben entsperren.'
          : '🔒 Dieser Thread ist gesperrt.'));
        return;
      }
      if (!token) { loginInline(composer, () => { renderModbar(); renderComposer(); load(); }); return; }
      if (replyTo) {
        const r = el('button', 'dialog-replychip', 'Antwort auf ' + replyTo.name + '  ✕');
        r.onclick = () => { replyTo = null; renderComposer(); };
        composer.appendChild(r);
      }
      textarea = el('textarea', 'dialog-textarea'); textarea.placeholder = locked ? 'Thread gesperrt — nur Moderation …' : 'Deine Wortmeldung …';
      autoGrow(textarea); sendOnCmdEnter(textarea, submit);
      const row = el('div', 'dialog-row');
      const send = el('button', 'dialog-send', 'Senden'); send.onclick = submit;
      const hint = el('span', 'dialog-hint', '⌘ + ↵');
      const out = el('button', 'dialog-logout', 'Abmelden' + (myName ? ' · ' + myName : '')); out.onclick = () => logout(() => { renderModbar(); renderComposer(); load(); });
      row.append(send, hint, el('span', 'dialog-spacer'), out); composer.append(textarea, row);
    }

    async function submit() {
      const body = textarea.value.trim(); if (!body) return;
      const r = await api('/api/comments', { method: 'POST', headers: { 'Content-Type': 'application/json', ...authHdr() }, body: JSON.stringify({ thread: key, body, parent_id: replyTo ? replyTo.id : null }) });
      if (r.status === 401) { logout(); alert('Sitzung abgelaufen — bitte neu anmelden.'); renderComposer(); return; }
      if (!r.ok) { alert(r.body.error || 'Senden fehlgeschlagen'); return; }
      textarea.value = ''; replyTo = null; renderComposer(); load();
    }
    async function remove(id) {
      if (!confirm('Wortmeldung löschen?')) return;
      const r = await api('/api/comments/' + id, { method: 'DELETE', headers: authHdr() });
      if (!r.ok) { alert(r.body.error || 'Löschen fehlgeschlagen'); return; }
      load();
    }

    load();
    setInterval(() => { if (!document.hidden) load(); }, 10000);
  }

  // ── Router ────────────────────────────────────────────────────────────────
  if (threadKey) renderThread(threadKey);
  else if (forumSlug) renderForum(forumSlug);
  else renderOverview();
})();
