// ============================================================
// admin-docs.js — admin-only docs viewer.
//
// Lazy-loads /api/admin/docs/tree once on first tab activation,
// renders a collapsible file tree in the sidebar. Clicking a .md
// node fetches /api/admin/docs/file?path=X and renders the
// pre-sanitised HTML returned by the server (server uses `marked`).
//
// The route is gated to role=admin server-side. The sidebar tab
// button (#docs-tab-btn) is hidden by default and revealed by
// auth.js after login when user.role === 'admin'.
// ============================================================

  var _inited = false;
  var _treeLoaded = false;
  var _tree = [];
  var _flatFiles = [];   // flat list of {name, path, parent} for filter
  var _expanded = {};    // map of dir-path → bool, persisted in UIState
  var _currentPath = '';

  function $(id) { return document.getElementById(id); }
  function escHtml(s) { return String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;'); }

  // Pretty label from a file/dir basename. Drops .md, replaces dashes
  // with spaces, title-cases the first letter of each word. README is
  // shown as "Index" so the entry-point doc is more obvious in the tree.
  function prettyName(name, isDir) {
    if (!isDir && /^readme\.md$/i.test(name)) return 'Index';
    var base = name.replace(/\.md$/i, '');
    return base.replace(/-/g, ' ').replace(/\b\w/g, function (c) { return c.toUpperCase(); });
  }

  // Recursively render the tree into HTML.
  function renderNode(node, depth) {
    if (node.type === 'dir') {
      var open = _expanded[node.path] !== false; // default open
      var arrow = open ? 'fa-chevron-down' : 'fa-chevron-right';
      var label = prettyName(node.name, true);
      var html =
        '<div class="docs-node docs-dir" data-path="' + escHtml(node.path) + '" style="--depth:' + depth + ';">' +
          '<button type="button" class="docs-dir-toggle" data-toggle="' + escHtml(node.path) + '">' +
            '<i class="fas ' + arrow + ' docs-arrow"></i>' +
            '<i class="fas fa-folder docs-icon"></i>' +
            '<span class="docs-label">' + escHtml(label) + '</span>' +
          '</button>' +
          '<div class="docs-children" ' + (open ? '' : 'hidden') + '>' +
            (node.children || []).map(function (c) { return renderNode(c, depth + 1); }).join('') +
          '</div>' +
        '</div>';
      return html;
    }
    // File node
    return '<div class="docs-node docs-file" data-path="' + escHtml(node.path) + '" style="--depth:' + depth + ';">' +
      '<button type="button" class="docs-file-btn" data-file="' + escHtml(node.path) + '" title="' + escHtml(node.path) + '">' +
        '<i class="fas fa-file-lines docs-icon"></i>' +
        '<span class="docs-label">' + escHtml(prettyName(node.name, false)) + '</span>' +
      '</button>' +
    '</div>';
  }

  // Flatten the tree into _flatFiles for the filter to search across.
  function flatten(nodes, parentLabel) {
    nodes.forEach(function (n) {
      if (n.type === 'dir') {
        flatten(n.children || [], (parentLabel ? parentLabel + ' / ' : '') + prettyName(n.name, true));
      } else {
        _flatFiles.push({
          name: prettyName(n.name, false),
          path: n.path,
          parent: parentLabel || ''
        });
      }
    });
  }

  function renderTree() {
    var host = $('docs-tree');
    if (!host) return;
    if (!_tree.length) {
      host.innerHTML = '<div class="docs-empty">No docs found.</div>';
      return;
    }
    host.innerHTML = _tree.map(function (n) { return renderNode(n, 0); }).join('');
  }

  function renderFilterResults(q) {
    var host = $('docs-tree');
    if (!host) return;
    var lc = q.toLowerCase();
    var matches = _flatFiles.filter(function (f) {
      return f.name.toLowerCase().indexOf(lc) !== -1
          || f.path.toLowerCase().indexOf(lc) !== -1
          || f.parent.toLowerCase().indexOf(lc) !== -1;
    });
    if (matches.length === 0) {
      host.innerHTML = '<div class="docs-empty">No matches for "' + escHtml(q) + '".</div>';
      return;
    }
    host.innerHTML = matches.map(function (f) {
      var parent = f.parent ? '<span class="docs-file-parent">' + escHtml(f.parent) + '</span>' : '';
      return '<div class="docs-node docs-file" data-path="' + escHtml(f.path) + '" style="--depth:0;">' +
        '<button type="button" class="docs-file-btn" data-file="' + escHtml(f.path) + '" title="' + escHtml(f.path) + '">' +
          '<i class="fas fa-file-lines docs-icon"></i>' +
          '<span class="docs-label">' + escHtml(f.name) + '</span>' +
          parent +
        '</button>' +
      '</div>';
    }).join('');
  }

  // marked no longer guarantees heading ids across versions, and the docs
  // reader is an internal scroll container. Add stable GitHub-style ids and
  // handle same-page #toc links by scrolling the reader, not the window.
  function slugHeading(text) {
    return String(text || '')
      .trim()
      .toLowerCase()
      .replace(/\s/g, '-')
      .replace(/[^a-z0-9_-]/g, '');
  }

  function prepareDocAnchors(body) {
    if (!body) return;
    var seen = {};
    body.querySelectorAll('h1,h2,h3,h4,h5,h6').forEach(function (h) {
      var base = slugHeading(h.textContent || '') || h.id;
      if (!base) return;
      var id = base;
      var n = seen[base] || 0;
      if (n) id = base + '-' + n;
      seen[base] = n + 1;
      h.id = id;
    });
  }

  function prepareDocLinks(body) {
    if (!body) return;
    body.querySelectorAll('a[href]').forEach(function (link) {
      var href = link.getAttribute('href') || '';
      if (href.charAt(0) === '#') {
        link.dataset.docHash = href;
        link.classList.add('docs-anchor-link');
        return;
      }
      if (/\.md(#.*)?$/i.test(href)) {
        var parts = href.split('#');
        link.dataset.docFile = resolveDocPath(parts[0] || _currentPath);
        if (parts[1]) link.dataset.docHash = '#' + parts[1];
        link.classList.add('docs-anchor-link');
        return;
      }
      if (link.hash) {
        try {
          var u = new URL(link.href, window.location.href);
          if (u.origin !== window.location.origin || u.pathname !== window.location.pathname) return;
          link.dataset.docHash = u.hash;
          link.classList.add('docs-anchor-link');
        } catch (_) { return; }
      }
    });
  }

  function resolveDocPath(href) {
    if (!href) return _currentPath;
    if (href.charAt(0) === '/') return href.replace(/^\/+/, '');
    var base = _currentPath.split('/');
    base.pop();
    href.split('/').forEach(function (part) {
      if (!part || part === '.') return;
      if (part === '..') base.pop();
      else base.push(part);
    });
    return base.join('/');
  }

  function scrollReaderToHash(hash) {
    var body = $('docs-reader-body');
    var reader = $('docs-reader');
    if (!body || !reader || !hash) return false;
    var rawId = String(hash).replace(/^#/, '');
    var id;
    try { id = decodeURIComponent(rawId); } catch (_) { id = rawId; }
    if (!id) return false;
    var target = document.getElementById(id);
    if (!target || !body.contains(target)) {
      target = Array.prototype.slice.call(body.querySelectorAll('h1,h2,h3,h4,h5,h6')).find(function (h) {
        return slugHeading(h.textContent || '') === id;
      });
    }
    if (!target || !body.contains(target)) return false;
    var readerBox = reader.getBoundingClientRect();
    var targetBox = target.getBoundingClientRect();
    reader.scrollTop += targetBox.top - readerBox.top - 8;
    return true;
  }

  function handleDocAnchorClick(e) {
    var body = $('docs-reader-body');
    var link = e.target.closest('a, [data-doc-hash], [data-doc-file]');
    if (!link || !body || !body.contains(link)) return false;
    var href = link.dataset.docHash || link.getAttribute('href') || '';
    var file = link.dataset.docFile || '';
    if (!file && href && /\.md(#.*)?$/i.test(href)) {
      var parts = href.split('#');
      file = resolveDocPath(parts[0] || _currentPath);
      href = parts[1] ? '#' + parts[1] : '';
    }
    if (!file && (!href || href.charAt(0) !== '#')) return false;
    e.preventDefault();
    if (file && file !== _currentPath) {
      loadFile(file, href);
      markActive(file);
      return true;
    }
    requestAnimationFrame(function () {
      if (scrollReaderToHash(href)) {
        try { history.replaceState(null, '', href); } catch (_) {}
      }
    });
    return true;
  }

  function handleDocAnchorKeydown(e) {
    if (e.key !== 'Enter' && e.key !== ' ') return;
    if (handleDocAnchorClick(e)) e.preventDefault();
  }

  function loadTree() {
    if (_treeLoaded) return;
    fetch('/api/admin/docs/tree', { headers: getAuthHeaders() })
      .then(function (r) { return r.json(); })
      .then(function (data) {
        if (!data || !data.success) {
          $('docs-tree').innerHTML = '<div class="docs-empty">' + escHtml((data && data.error) || 'Tree load failed') + '</div>';
          return;
        }
        _tree = data.tree || [];
        _flatFiles = [];
        flatten(_tree, '');
        // Restore expanded state from UIState before render
        try {
          var saved = window.UIState && window.UIState.get('docs.expanded');
          if (saved) _expanded = JSON.parse(saved);
        } catch (e) {}
        renderTree();
        _treeLoaded = true;
        // Auto-load the top README if available, else the first file
        var first = _flatFiles[0];
        if (first) {
          var savedPath = (window.UIState && window.UIState.get('docs.lastPath')) || first.path;
          loadFile(savedPath);
          markActive(savedPath);
        }
      })
      .catch(function (err) {
        $('docs-tree').innerHTML = '<div class="docs-empty">Tree load failed: ' + escHtml(err.message || String(err)) + '</div>';
      });
  }

  function loadFile(relPath, hash) {
    var body = $('docs-reader-body');
    var meta = $('docs-reader-meta');
    if (!body) return;
    _currentPath = relPath;
    body.innerHTML = '<div class="docs-loading">Loading…</div>';
    fetch('/api/admin/docs/file?path=' + encodeURIComponent(relPath), { headers: getAuthHeaders() })
      .then(function (r) { return r.json(); })
      .then(function (data) {
        if (!data || !data.success) {
          body.innerHTML = '<p style="color:var(--red);">' + escHtml((data && data.error) || 'Load failed') + '</p>';
          return;
        }
        body.innerHTML = data.html || '';
        prepareDocAnchors(body);
        prepareDocLinks(body);
        if (meta) {
          meta.textContent = relPath + '   •   ' + (data.bytes != null ? (data.bytes + ' bytes') : '');
        }
        // Persist last opened
        if (window.UIState) window.UIState.set('docs.lastPath', relPath);
        // Scroll content area to top so deep-link readers don't land mid-doc
        var reader = $('docs-reader');
        if (reader) reader.scrollTop = 0;
        var targetHash = hash || window.location.hash;
        if (targetHash) setTimeout(function () { scrollReaderToHash(targetHash); }, 0);
      })
      .catch(function (err) {
        body.innerHTML = '<p style="color:var(--red);">' + escHtml(err.message || String(err)) + '</p>';
      });
  }

  function markActive(relPath) {
    document.querySelectorAll('.docs-file-btn').forEach(function (b) {
      b.classList.toggle('active', b.dataset.file === relPath);
    });
  }

  function persistExpanded() {
    if (!window.UIState) return;
    try { window.UIState.set('docs.expanded', JSON.stringify(_expanded)); } catch (e) {}
  }

  // ── Wire events ────────────────────────────────────────────────────
  function init() {
    document.addEventListener('click', function (e) {
      if (handleDocAnchorClick(e)) return;
      var fileBtn = e.target.closest('.docs-file-btn');
      if (fileBtn) {
        var p = fileBtn.dataset.file;
        if (p) { loadFile(p); markActive(p); }
        return;
      }
      var dirToggle = e.target.closest('.docs-dir-toggle');
      if (dirToggle) {
        var dir = dirToggle.dataset.toggle;
        var node = dirToggle.closest('.docs-dir');
        var children = node && node.querySelector('.docs-children');
        var arrow = dirToggle.querySelector('.docs-arrow');
        var willOpen = !(_expanded[dir] !== false); // toggle
        _expanded[dir] = willOpen;
        if (children) {
          if (willOpen) children.removeAttribute('hidden');
          else children.setAttribute('hidden', '');
        }
        if (arrow) {
          arrow.classList.toggle('fa-chevron-down', willOpen);
          arrow.classList.toggle('fa-chevron-right', !willOpen);
        }
        persistExpanded();
        return;
      }
    });

    var body = $('docs-reader-body');
    if (body && !body.dataset.anchorsWired) {
      body.dataset.anchorsWired = '1';
      body.addEventListener('click', handleDocAnchorClick);
      body.addEventListener('keydown', handleDocAnchorKeydown);
    }

    var filter = $('docs-filter');
    if (filter) {
      var t = null;
      filter.addEventListener('input', function () {
        clearTimeout(t);
        t = setTimeout(function () {
          var q = filter.value.trim();
          if (!q) renderTree();
          else renderFilterResults(q);
        }, 120);
      });
    }
  }

  document.addEventListener('tabChanged', function (e) {
    if (!e.detail || e.detail.tab !== 'docs') return;
    if (!_inited) { _inited = true; init(); }
    loadTree();
  });

  console.log('Admin docs viewer loaded');
