/**
 * HTML Viewer Generator for Wiki
 *
 * Produces a self-contained index.html that embeds all markdown pages,
 * module tree, and metadata — viewable offline in any browser.
 */

import fs from 'fs/promises';
import path from 'path';

const MAX_EMBEDDED_WIKI_BYTES = (() => {
  const raw = Number.parseInt(process.env.ONTOINDEX_MAX_EMBEDDED_WIKI_BYTES ?? '', 10);
  return Number.isFinite(raw) ? Math.max(0, raw) : 10 * 1024 * 1024;
})();

interface ModuleTreeNode {
  name: string;
  slug: string;
  files: string[];
  children?: ModuleTreeNode[];
}

/**
 * Generate the wiki HTML viewer (index.html) from existing markdown pages.
 */
export async function generateHTMLViewer(wikiDir: string, projectName: string): Promise<string> {
  // Load module tree
  let moduleTree: ModuleTreeNode[] = [];
  try {
    const raw = await fs.readFile(path.join(wikiDir, 'module_tree.json'), 'utf-8');
    moduleTree = JSON.parse(raw);
  } catch {
    /* will show empty nav */
  }

  // Load meta
  let meta: Record<string, unknown> | null = null;
  try {
    const raw = await fs.readFile(path.join(wikiDir, 'meta.json'), 'utf-8');
    meta = JSON.parse(raw);
  } catch {
    /* no meta */
  }

  // Read all markdown files into a { slug: content } map
  const pages: Record<string, string> = {};
  const dirEntries = (await fs.readdir(wikiDir)).filter((f) => f.endsWith('.md'));
  let embeddedBytes = 0;
  for (const f of dirEntries) {
    const stat = await fs.stat(path.join(wikiDir, f));
    embeddedBytes += stat.size;
    if (embeddedBytes > MAX_EMBEDDED_WIKI_BYTES) {
      throw new Error(
        `Wiki HTML viewer would embed ${embeddedBytes} bytes of markdown; cap is ${MAX_EMBEDDED_WIKI_BYTES}. ` +
          'Use the markdown files directly or raise ONTOINDEX_MAX_EMBEDDED_WIKI_BYTES.',
      );
    }
  }
  for (const f of dirEntries) {
    const content = await fs.readFile(path.join(wikiDir, f), 'utf-8');
    pages[f.replace(/\.md$/, '')] = content;
  }

  let graphOverviewAvailable = false;
  try {
    await fs.access(path.join(wikiDir, 'graph-overview.html'));
    graphOverviewAvailable = true;
  } catch {
    /* optional artifact */
  }

  const html = buildHTML(projectName, moduleTree, pages, meta, graphOverviewAvailable);
  const outputPath = path.join(wikiDir, 'index.html');
  await fs.writeFile(outputPath, html, 'utf-8');
  return outputPath;
}

// ─── HTML Builder ───────────────────────────────────────────────────────

function esc(text: string): string {
  return text
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;');
}

function buildHTML(
  projectName: string,
  moduleTree: ModuleTreeNode[],
  pages: Record<string, string>,
  meta: Record<string, unknown> | null,
  graphOverviewAvailable: boolean,
): string {
  // Embed data as JSON inside the HTML.
  // Escape </script> sequences so they don't prematurely close the <script> tag.
  const escScript = (s: string) => s.replace(/<\//g, '<\\/');
  const pagesJSON = escScript(JSON.stringify(pages));
  const treeJSON = escScript(JSON.stringify(moduleTree));
  const metaJSON = escScript(JSON.stringify(meta));
  const graphOverviewJSON = graphOverviewAvailable ? 'true' : 'false';

  const parts: string[] = [];

  // ── Head ──
  parts.push('<!DOCTYPE html>');
  parts.push('<html lang="en">');
  parts.push('<head>');
  parts.push('<meta charset="UTF-8">');
  parts.push('<meta name="viewport" content="width=device-width, initial-scale=1.0">');
  parts.push('<title>' + esc(projectName) + ' — Wiki</title>');
  parts.push('<script src="https://cdn.jsdelivr.net/npm/marked@11.0.0/marked.min.js"><\/script>');
  parts.push(
    '<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"><\/script>',
  );
  parts.push(
    '<script src="https://cdn.jsdelivr.net/npm/dompurify@3/dist/purify.min.js"><\/script>',
  );
  parts.push('<style>');
  parts.push(CSS);
  parts.push('</style>');
  parts.push('</head>');

  // ── Body ──
  parts.push('<body>');
  parts.push(
    '<button class="menu-toggle" id="menu-toggle" aria-label="Toggle menu">&#9776;</button>',
  );
  parts.push('<div class="layout">');

  // Sidebar
  parts.push('<nav class="sidebar" id="sidebar">');
  parts.push('<div class="sidebar-header">');
  parts.push('<div class="sidebar-title">');
  parts.push(BOOK_SVG);
  parts.push(esc(projectName));
  parts.push('</div>');
  parts.push('<div class="sidebar-meta" id="meta-info"></div>');
  parts.push('</div>');
  parts.push('<div id="nav-tree"></div>');
  parts.push('<div class="sidebar-footer">Generated by OntoIndex</div>');
  parts.push('</nav>');

  // Content
  parts.push('<main class="content" id="content">');
  parts.push('<div class="empty-state"><h2>Loading…</h2></div>');
  parts.push('</main>');
  parts.push('</div>');

  // ── Script ──
  parts.push('<script>');
  parts.push('var PAGES = ' + pagesJSON + ';');
  parts.push('var TREE = ' + treeJSON + ';');
  parts.push('var META = ' + metaJSON + ';');
  parts.push('var GRAPH_OVERVIEW_AVAILABLE = ' + graphOverviewJSON + ';');
  parts.push(JS_APP);
  parts.push('<\/script>');

  parts.push('</body>');
  parts.push('</html>');

  return parts.join('\n');
}

// ─── Static Assets ────────────────────────────────────────────────────

const BOOK_SVG =
  '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">' +
  '<path d="M2 3h6a4 4 0 014 4v14a3 3 0 00-3-3H2z"/>' +
  '<path d="M22 3h-6a4 4 0 00-4 4v14a3 3 0 013-3h7z"/>' +
  '</svg>';

const CSS = `
*{margin:0;padding:0;box-sizing:border-box}
:root{
  --bg:#ffffff;--sidebar-bg:#f8f9fb;--border:#e5e7eb;
  --text:#1e293b;--text-muted:#64748b;--primary:#2563eb;
  --primary-soft:#eff6ff;--hover:#f1f5f9;--code-bg:#f1f5f9;
  --radius:8px;--shadow:0 1px 3px rgba(0,0,0,.08);
}
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
  line-height:1.65;color:var(--text);background:var(--bg)}

.layout{display:flex;min-height:100vh}
.sidebar{width:280px;background:var(--sidebar-bg);border-right:1px solid var(--border);
  position:fixed;top:0;left:0;bottom:0;overflow-y:auto;padding:24px 16px;
  display:flex;flex-direction:column;z-index:10}
.content{margin-left:280px;flex:1;padding:48px 64px;max-width:960px}

.sidebar-header{margin-bottom:20px;padding-bottom:16px;border-bottom:1px solid var(--border)}
.sidebar-title{font-size:16px;font-weight:700;color:var(--text);display:flex;align-items:center;gap:8px}
.sidebar-title svg{flex-shrink:0}
.sidebar-meta{font-size:11px;color:var(--text-muted);margin-top:6px}
.nav-section{margin-bottom:2px}
.nav-item{display:block;padding:7px 12px;border-radius:var(--radius);cursor:pointer;
  font-size:13px;color:var(--text);text-decoration:none;transition:all .15s;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.nav-item:hover{background:var(--hover)}
.nav-item.active{background:var(--primary-soft);color:var(--primary);font-weight:600}
.nav-item.overview{font-weight:600;margin-bottom:4px}
.nav-children{padding-left:14px;border-left:1px solid var(--border);margin-left:12px}
.nav-group-label{font-size:11px;font-weight:600;color:var(--text-muted);
  text-transform:uppercase;letter-spacing:.5px;padding:12px 12px 4px;user-select:none}
.sidebar-footer{margin-top:auto;padding-top:16px;border-top:1px solid var(--border);
  font-size:11px;color:var(--text-muted);text-align:center}

.content h1{font-size:28px;font-weight:700;margin-bottom:8px;line-height:1.3}
.content h2{font-size:22px;font-weight:600;margin:32px 0 12px;padding-bottom:6px;border-bottom:1px solid var(--border)}
.content h3{font-size:17px;font-weight:600;margin:24px 0 8px}
.content h4{font-size:15px;font-weight:600;margin:20px 0 6px}
.content p{margin:12px 0}
.content ul,.content ol{margin:12px 0 12px 24px}
.content li{margin:4px 0}
.content a{color:var(--primary);text-decoration:none}
.content a:hover{text-decoration:underline}
.content blockquote{border-left:3px solid var(--primary);padding:8px 16px;margin:16px 0;
  background:var(--primary-soft);border-radius:0 var(--radius) var(--radius) 0;
  color:var(--text-muted);font-size:14px}
.content code{font-family:'SF Mono',Consolas,'Courier New',monospace;font-size:13px;
  background:var(--code-bg);padding:2px 6px;border-radius:4px}
.content pre{background:#1e293b;color:#e2e8f0;border-radius:var(--radius);padding:16px;
  overflow-x:auto;margin:16px 0}
.content pre code{background:none;padding:0;font-size:13px;line-height:1.6;color:inherit}
.content table{border-collapse:collapse;width:100%;margin:16px 0}
.content th,.content td{border:1px solid var(--border);padding:8px 12px;text-align:left;font-size:14px}
.content th{background:var(--sidebar-bg);font-weight:600}
.content img{max-width:100%;border-radius:var(--radius)}
.content hr{border:none;border-top:1px solid var(--border);margin:32px 0}
.content .mermaid{margin:20px 0;text-align:center}

.menu-toggle{display:none;position:fixed;top:12px;left:12px;z-index:20;
  background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);
  padding:8px 12px;cursor:pointer;font-size:18px;box-shadow:var(--shadow)}
@media(max-width:768px){
  .sidebar{transform:translateX(-100%);transition:transform .2s}
  .sidebar.open{transform:translateX(0);box-shadow:2px 0 12px rgba(0,0,0,.1)}
  .content{margin-left:0;padding:24px 20px;padding-top:56px}
  .menu-toggle{display:block}
}
.empty-state{text-align:center;padding:80px 20px;color:var(--text-muted)}
.empty-state h2{font-size:20px;margin-bottom:8px;border:none}
`;

// The client-side JS is kept as a plain string to avoid template literal conflicts
const JS_APP = `
(function() {
  var activePage = 'overview';

  document.addEventListener('DOMContentLoaded', function() {
    mermaid.initialize({ startOnLoad: false, theme: 'neutral', securityLevel: 'strict' });
    renderMeta();
    renderNav();
    document.getElementById('menu-toggle').addEventListener('click', function() {
      document.getElementById('sidebar').classList.toggle('open');
    });
    if (location.hash && location.hash.length > 1) {
      activePage = decodeURIComponent(location.hash.slice(1));
    }
    navigateTo(activePage);
  });

  function renderMeta() {
    if (!META) return;
    var el = document.getElementById('meta-info');
    var parts = [];
    if (META.generatedAt) {
      parts.push(new Date(META.generatedAt).toLocaleDateString());
    }
    if (META.model) parts.push(META.model);
    if (META.fromCommit) parts.push(META.fromCommit.slice(0, 8));
    el.textContent = parts.join(' \\u00b7 ');
  }

  function renderNav() {
    var container = document.getElementById('nav-tree');
    var html = '<div class="nav-section">';
    html += '<a class="nav-item overview" data-page="overview" href="#overview">Overview</a>';
    html += '</div>';
    if (GRAPH_OVERVIEW_AVAILABLE) {
      html += '<div class="nav-section">';
      html += '<a class="nav-item overview" href="graph-overview.html" target="_blank" rel="noopener">Graph Overview</a>';
      html += '</div>';
    }
    if (TREE.length > 0) {
      html += '<div class="nav-group-label">Modules</div>';
      html += buildNavTree(TREE);
    }
    container.innerHTML = html;
    container.addEventListener('click', function(e) {
      var target = e.target;
      while (target && !target.dataset.page) { target = target.parentElement; }
      if (target && target.dataset.page) {
        e.preventDefault();
        navigateTo(target.dataset.page);
      }
    });
  }

  function buildNavTree(nodes) {
    var html = '';
    for (var i = 0; i < nodes.length; i++) {
      var node = nodes[i];
      html += '<div class="nav-section">';
      html += '<a class="nav-item" data-page="' + escH(node.slug) + '" href="#' + encodeURIComponent(node.slug) + '">' + escH(node.name) + '</a>';
      if (node.children && node.children.length > 0) {
        html += '<div class="nav-children">' + buildNavTree(node.children) + '</div>';
      }
      html += '</div>';
    }
    return html;
  }

  function escH(s) {
    var d = document.createElement('div');
    d.textContent = s;
    return d.innerHTML;
  }

  function navigateTo(page) {
    activePage = page;
    location.hash = encodeURIComponent(page);

    var items = document.querySelectorAll('.nav-item');
    for (var i = 0; i < items.length; i++) {
      if (items[i].dataset.page === page) {
        items[i].classList.add('active');
      } else {
        items[i].classList.remove('active');
      }
    }

    var contentEl = document.getElementById('content');
    var md = PAGES[page];

    if (!md) {
      contentEl.innerHTML = '<div class="empty-state"><h2>Page not found</h2><p>' + escH(page) + '.md does not exist.</p></div>';
      return;
    }

    contentEl.innerHTML = DOMPurify.sanitize(marked.parse(md));

    // Rewrite .md links to hash navigation
    var links = contentEl.querySelectorAll('a[href]');
    for (var i = 0; i < links.length; i++) {
      var href = links[i].getAttribute('href');
      if (href && href.endsWith('.md') && href.indexOf('://') === -1) {
        var slug = href.replace(/\\.md$/, '');
        links[i].setAttribute('href', '#' + encodeURIComponent(slug));
        (function(s) {
          links[i].addEventListener('click', function(e) {
            e.preventDefault();
            navigateTo(s);
          });
        })(slug);
      }
    }

    // Convert mermaid code blocks into mermaid divs
    var mermaidBlocks = contentEl.querySelectorAll('pre code.language-mermaid');
    for (var i = 0; i < mermaidBlocks.length; i++) {
      var pre = mermaidBlocks[i].parentElement;
      var div = document.createElement('div');
      div.className = 'mermaid';
      div.textContent = mermaidBlocks[i].textContent;
      pre.parentNode.replaceChild(div, pre);
    }
    try { mermaid.run({ querySelector: '.mermaid' }); } catch(e) {}

    window.scrollTo(0, 0);
    document.getElementById('sidebar').classList.remove('open');
  }
})();
`;
