#!/usr/bin/env node
/*
 * Balinyaar product-docs generator (zero dependency).
 *   cd product && node build-docs.mjs
 * Walks every .md file, renders a brand-styled, cross-linked .html beside it,
 * builds the sidebar from NAV below, and rewrites internal `…/foo.md` links to `…/foo.html`.
 * Markdown is the source of truth — never hand-edit the generated .html.
 */
import { readFileSync, writeFileSync, readdirSync, statSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join, relative, posix } from 'node:path';

const ROOT = dirname(fileURLToPath(import.meta.url));
const SEP = String.fromCharCode(0); // collision-proof placeholder delimiter for code spans

/* ---- Sidebar manifest. Add/rename .md files here so they show in the nav. ---- */
const NAV = [
  { label: 'Start here', items: [
    { path: 'index.md', title: 'Docs home' },
    { path: 'overview/platform-summary.md', title: 'Platform summary & ground truths' },
  ]},
  { label: 'Business requirements', items: [
    { path: 'business/index.md', title: 'Overview & MVP scope' },
    { path: 'business/01-actors-and-onboarding.md', title: '1. Actors & onboarding' },
    { path: 'business/02-nurse-verification.md', title: '2. Nurse verification' },
    { path: 'business/03-service-catalog-and-pricing.md', title: '3. Service catalog & pricing' },
    { path: 'business/04-search-and-matching.md', title: '4. Search & matching' },
    { path: 'business/05-booking-and-scheduling.md', title: '5. Booking & scheduling' },
    { path: 'business/06-evv-and-service-delivery.md', title: '6. EVV / service delivery' },
    { path: 'business/07-cancellation-and-refunds.md', title: '7. Cancellation & refunds' },
    { path: 'business/08-payments-and-escrow.md', title: '8. Payments & escrow' },
    { path: 'business/09-installments-bnpl.md', title: '9. Installments / BNPL' },
    { path: 'business/10-payouts.md', title: '10. Payouts to nurses' },
    { path: 'business/11-reviews-trust-and-safety.md', title: '11. Reviews, trust & safety' },
    { path: 'business/12-messaging-and-emergencies.md', title: '12. Messaging & emergencies' },
    { path: 'business/13-tax-invoicing-and-legal.md', title: '13. Tax, invoicing & legal' },
    { path: 'business/14-notifications-and-admin.md', title: '14. Notifications & admin' },
  ]},
  { label: 'Database model', items: [
    { path: 'data-model/index.md', title: 'Overview & decisions' },
    { path: 'data-model/diagrams.md', title: 'Diagrams' },
    { path: 'data-model/01-identity-and-access.md', title: '1. Identity & access' },
    { path: 'data-model/02-geography.md', title: '2. Geography' },
    { path: 'data-model/03-services-and-pricing.md', title: '3. Services & pricing' },
    { path: 'data-model/04-verification-and-credentials.md', title: '4. Verification & credentials' },
    { path: 'data-model/05-booking-and-scheduling.md', title: '5. Booking & scheduling' },
    { path: 'data-model/06-payments-ledger-and-refunds.md', title: '6. Payments, ledger & refunds' },
    { path: 'data-model/07-payouts.md', title: '7. Payouts' },
    { path: 'data-model/08-bnpl.md', title: '8. BNPL / installments' },
    { path: 'data-model/09-messaging.md', title: '9. Messaging' },
    { path: 'data-model/10-reviews-and-records.md', title: '10. Reviews & records' },
    { path: 'data-model/11-notifications.md', title: '11. Notifications' },
    { path: 'data-model/12-audit-config-and-reference.md', title: '12. Audit, config & reference' },
    { path: 'data-model/13-partner-centers-and-future.md', title: '13. Partner centers & future' },
  ]},
  { label: 'Payments deep-dive', items: [
    { path: 'payments/index.md', title: 'Overview & exec summary' },
    { path: 'payments/iranian-payment-reality.md', title: 'Iranian payment reality' },
    { path: 'payments/escrow-ledger.md', title: 'Escrow as a ledger' },
    { path: 'payments/bnpl-landscape.md', title: 'BNPL landscape & finding' },
    { path: 'payments/cancellation-and-payout.md', title: 'Cancellation & nurse payout' },
    { path: 'payments/integration-notes.md', title: 'Integration & schema touchpoints' },
    { path: 'payments/sources.md', title: 'Recommendations & sources' },
  ]},
  { label: 'Research & strategy', items: [
    { path: 'research/index.md', title: 'Overview & exec summary' },
    { path: 'research/market-and-competitors.md', title: 'Market & competitors' },
    { path: 'research/problems-and-risks.md', title: 'Problems & risks' },
    { path: 'research/verification.md', title: 'Verification (research)' },
    { path: 'research/legal-landscape.md', title: 'Legal landscape' },
    { path: 'research/go-to-market.md', title: 'Go-to-market & sources' },
  ]},
  { label: 'Notes & more', items: [
    { path: 'notes/open-questions.md', title: 'Open questions' },
    { path: 'notes/future-ideas.md', title: 'Future ideas' },
    { path: 'wireframes/index.html', title: 'Wireframes' },
    { path: 'fa/index.html', title: 'Farsi documents' },
  ]},
];

/* ---------------------------- tiny markdown renderer ---------------------------- */
const esc = (s) => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
const escAttr = (s) => esc(s).replace(/"/g, '&quot;');

function slug(text) {
  return text.toLowerCase().trim()
    .replace(/[`*~]/g, '')
    .replace(/[^\w؀-ۿ\s-]/g, '')
    .replace(/\s+/g, '-').replace(/-+/g, '-');
}

function rewriteHref(url) {
  if (/^(https?:|mailto:|#|\/\/)/.test(url)) return url;
  return url.replace(/\.md(#.*)?$/i, '.html$1');
}

// Inline: protect code spans with NUL-delimited placeholders, escape, then links/emphasis/strike.
function inline(text) {
  const codes = [];
  text = text.replace(/`([^`]+)`/g, (_, c) => { codes.push(c); return SEP + (codes.length - 1) + SEP; });
  text = esc(text);
  text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, label, url) =>
    '<a href="' + escAttr(rewriteHref(url.trim())) + '">' + label + '</a>');
  text = text.replace(/\*\*([^*]+?)\*\*/g, '<strong>$1</strong>');
  text = text.replace(/(^|[^*])\*([^*\n]+?)\*(?!\*)/g, '$1<em>$2</em>');
  text = text.replace(/~~([^~]+?)~~/g, '<del>$1</del>');
  const restore = new RegExp(SEP + '(\\d+)' + SEP, 'g');
  text = text.replace(restore, (_, i) => '<code>' + esc(codes[+i]) + '</code>');
  return text;
}

function renderList(lines, i, indent) {
  const reItem = /^(\s*)([-*+]|\d+[.)])\s+(.*)$/;
  let html = '';
  let ordered = null;
  while (i < lines.length) {
    const m = lines[i].match(reItem);
    if (!m) break;
    const ind = m[1].length;
    if (ind < indent) break;
    if (ind > indent) break;
    const thisOrdered = /\d/.test(m[2]);
    if (ordered === null) ordered = thisOrdered;
    let itemHtml = inline(m[3]);
    i++;
    if (i < lines.length) {
      const mn = lines[i].match(reItem);
      if (mn && mn[1].length > indent) {
        const [sub, ni] = renderList(lines, i, mn[1].length);
        itemHtml += sub;
        i = ni;
      }
    }
    html += '<li>' + itemHtml + '</li>\n';
  }
  const tag = ordered ? 'ol' : 'ul';
  return ['<' + tag + '>\n' + html + '</' + tag + '>\n', i];
}

function mdToHtml(md) {
  const lines = md.replace(/\r\n/g, '\n').split('\n');
  let out = '';
  let i = 0;
  const reItem = /^(\s*)([-*+]|\d+[.)])\s+(.*)$/;
  while (i < lines.length) {
    let line = lines[i];
    if (line.trim() === '') { i++; continue; }

    const fence = line.match(/^```\s*(\w+)?\s*$/);
    if (fence) {
      const lang = (fence[1] || '').toLowerCase();
      const buf = [];
      i++;
      while (i < lines.length && !/^```\s*$/.test(lines[i])) { buf.push(lines[i]); i++; }
      i++;
      const body = esc(buf.join('\n'));
      out += lang === 'mermaid'
        ? '<pre class="mermaid">' + body + '</pre>\n'
        : '<pre><code>' + body + '</code></pre>\n';
      continue;
    }

    const h = line.match(/^(#{1,6})\s+(.*)$/);
    if (h) {
      const lvl = h[1].length;
      const txt = inline(h[2].replace(/\s+#+\s*$/, ''));
      const id = slug(h[2]);
      const anchor = lvl >= 2 && lvl <= 3 ? ' <a class="anchor" href="#' + id + '" aria-hidden="true">#</a>' : '';
      out += '<h' + lvl + ' id="' + id + '">' + txt + anchor + '</h' + lvl + '>\n';
      i++;
      continue;
    }

    if (/^(\s*)(-{3,}|\*{3,}|_{3,})\s*$/.test(line)) { out += '<hr>\n'; i++; continue; }

    if (line.includes('|') && i + 1 < lines.length && /^\s*\|?\s*:?-{2,}/.test(lines[i + 1]) && lines[i + 1].includes('-')) {
      const splitRow = (r) => {
        let s = r.trim().replace(/^\|/, '').replace(/\|$/, '');
        const cells = []; let cur = ''; let escd = false;
        for (const ch of s) {
          if (escd) { cur += ch; escd = false; }
          else if (ch === '\\') { escd = true; cur += ch; }
          else if (ch === '|') { cells.push(cur); cur = ''; }
          else cur += ch;
        }
        cells.push(cur);
        return cells.map((c) => c.trim());
      };
      const headers = splitRow(line);
      i += 2;
      let body = '';
      while (i < lines.length && lines[i].includes('|') && lines[i].trim() !== '') {
        const cells = splitRow(lines[i]);
        body += '<tr>' + cells.map((c) => '<td>' + inline(c.replace(/\\\|/g, '|')) + '</td>').join('') + '</tr>\n';
        i++;
      }
      const head = '<tr>' + headers.map((c) => '<th>' + inline(c.replace(/\\\|/g, '|')) + '</th>').join('') + '</tr>';
      out += '<div class="table-wrap"><table><thead>' + head + '</thead><tbody>\n' + body + '</tbody></table></div>\n';
      continue;
    }

    if (/^\s*>/.test(line)) {
      const buf = [];
      while (i < lines.length && /^\s*>/.test(lines[i])) { buf.push(lines[i].replace(/^\s*>\s?/, '')); i++; }
      out += '<blockquote>' + mdToHtml(buf.join('\n')) + '</blockquote>\n';
      continue;
    }

    if (reItem.test(line)) {
      const ind = line.match(reItem)[1].length;
      const [html, ni] = renderList(lines, i, ind);
      out += html;
      i = ni;
      continue;
    }

    const para = [];
    while (i < lines.length && lines[i].trim() !== '' &&
           !/^(#{1,6}\s|```|\s*>|\s*(-{3,}|\*{3,}|_{3,})\s*$)/.test(lines[i]) &&
           !reItem.test(lines[i]) &&
           !(lines[i].includes('|') && i + 1 < lines.length && /^\s*\|?\s*:?-{2,}/.test(lines[i + 1]))) {
      para.push(lines[i]); i++;
    }
    if (para.length) out += '<p>' + inline(para.join(' ')) + '</p>\n';
  }
  return out;
}

/* ---------------------------- page template ---------------------------- */
function relHref(fromPath, toPath) {
  const rel = posix.relative(posix.dirname(fromPath), toPath);
  return rel || posix.basename(toPath);
}

function firstH1(md) {
  const m = md.match(/^#\s+(.+)$/m);
  return m ? m[1].replace(/[`*~]/g, '').trim() : 'Balinyaar';
}

function sidebar(currentMdPath) {
  const currentHtml = currentMdPath.replace(/\.md$/, '.html');
  let html = '';
  for (const group of NAV) {
    html += '<div class="group"><div class="label">' + esc(group.label) + '</div><ul>';
    for (const it of group.items) {
      const targetHtml = it.path.replace(/\.md$/, '.html');
      const active = targetHtml === currentHtml ? ' class="active"' : '';
      html += '<li><a' + active + ' href="' + escAttr(relHref(currentHtml, targetHtml)) + '">' + esc(it.title) + '</a></li>';
    }
    html += '</ul></div>';
  }
  return html;
}

function page(mdPath, md) {
  const htmlPath = mdPath.replace(/\.md$/, '.html');
  const title = firstH1(md);
  const cssHref = escAttr(relHref(htmlPath, 'assets/doc.css'));
  const homeHref = escAttr(relHref(htmlPath, 'index.html'));
  const body = mdToHtml(md);
  const mermaidScript = body.includes('class="mermaid"')
    ? '<script type="module">\n'
      + "  import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';\n"
      + "  const dark = document.documentElement.getAttribute('data-theme') === 'dark';\n"
      + "  mermaid.initialize({ startOnLoad: true, theme: dark ? 'dark' : 'neutral' });\n"
      + '</script>'
    : '';
  return '<!DOCTYPE html>\n'
    + '<html lang="en">\n<head>\n'
    + '<meta charset="utf-8">\n'
    + '<meta name="viewport" content="width=device-width, initial-scale=1">\n'
    + '<title>' + esc(title) + ' — Balinyaar docs</title>\n'
    + '<link rel="stylesheet" href="' + cssHref + '">\n'
    + '</head>\n<body>\n'
    + '<div class="layout">\n'
    + '<aside class="sidebar">\n'
    + '  <a class="brand" href="' + homeHref + '"><span class="dot"></span> Balinyaar docs</a>\n'
    + '  <p class="tagline">Trust-first home-nursing marketplace · Iran</p>\n'
    + '  <nav>' + sidebar(mdPath) + '</nav>\n'
    + '</aside>\n'
    + '<main class="main"><div class="content">\n'
    + '  <div class="topbar"><button class="theme-toggle" type="button" onclick="__t()">theme</button></div>\n'
    + body
    + '  <a class="back-to-top" href="#">↑ Back to top</a>\n'
    + '</div></main>\n</div>\n'
    + '<script>\n'
    + "  (function(){var k='balinyaar-docs-theme';var s=localStorage.getItem(k);\n"
    + "    if(s)document.documentElement.setAttribute('data-theme',s);\n"
    + "    else if(matchMedia('(prefers-color-scheme: dark)').matches)document.documentElement.setAttribute('data-theme','dark');})();\n"
    + "  function __t(){var d=document.documentElement;var n=d.getAttribute('data-theme')==='dark'?'light':'dark';\n"
    + "    d.setAttribute('data-theme',n);localStorage.setItem('balinyaar-docs-theme',n);}\n"
    + '</script>\n'
    + mermaidScript + '\n'
    + '</body>\n</html>\n';
}

/* ---------------------------- walk + build ---------------------------- */
function walk(dir, acc = []) {
  for (const name of readdirSync(dir)) {
    const full = join(dir, name);
    if (statSync(full).isDirectory()) {
      if (name === 'assets' || name === 'node_modules' || name.startsWith('.')) continue;
      walk(full, acc);
    } else if (name.endsWith('.md')) {
      acc.push(full);
    }
  }
  return acc;
}

const files = walk(ROOT);
let n = 0;
for (const full of files) {
  const rel = relative(ROOT, full).split('\\').join('/');
  const md = readFileSync(full, 'utf8');
  writeFileSync(full.replace(/\.md$/, '.html'), page(rel, md), 'utf8');
  n++;
}
console.log('Generated ' + n + ' HTML pages from Markdown.');
