import { readFile, writeFile, unlink } from 'fs/promises';
import { resolve as pathResolve } from 'path';
import { existsSync } from 'fs';
import { env } from '../../config/env';
import { logger } from '../../utils/logger';
import { headerConfigSchema } from './header-builder.schemas';
import type { HeaderConfig, HeaderNavItem } from './header-builder.schemas';

const OVERRIDES_DIR = pathResolve(env.MKDOCS_DOCS_PATH, 'overrides');
const CONFIG_PATH = pathResolve(OVERRIDES_DIR, 'header-config.json');
const MAIN_HTML_PATH = pathResolve(OVERRIDES_DIR, 'main.html');

/** Default built-in navigation items (pre-populated when no config exists) */
const DEFAULT_ITEMS: HeaderNavItem[] = [
  { id: 'campaigns', label: 'Campaigns', path: '/campaigns', icon: 'campaign', enabled: true, order: 0, type: 'builtin' },
  { id: 'map', label: 'Map', path: '/map', icon: 'map', enabled: true, order: 1, type: 'builtin' },
  { id: 'shifts', label: 'Volunteer', path: '/shifts', icon: 'groups', enabled: true, order: 2, type: 'builtin' },
  { id: 'gallery', label: 'Gallery', path: '/gallery', icon: 'play_circle', enabled: false, order: 3, type: 'builtin' },
  { id: 'responses', label: 'Responses', path: '/responses', icon: 'forum', enabled: false, order: 4, type: 'builtin' },
  { id: 'donate', label: 'Donate', path: '/donate', icon: 'favorite', enabled: false, order: 5, type: 'builtin' },
  { id: 'login', label: 'Sign In', path: '/login', icon: 'login', enabled: true, order: 6, type: 'builtin' },
];

const DEFAULT_CONFIG: HeaderConfig = {
  enabled: false,
  items: DEFAULT_ITEMS,
  style: {
    backgroundColor: '#6f42c1',
    textColor: '#ffffff',
    hoverColor: 'rgba(255,255,255,0.15)',
    height: '40px',
  },
};

/**
 * Escape a string for safe embedding inside a Jinja2/HTML template.
 * Prevents XSS if user-supplied labels or paths contain special chars.
 */
function escapeHtml(str: string): string {
  return str
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;');
}

/** Map Ant Design icon IDs to Material Icons Outlined names for MkDocs */
const ANT_ICON_TO_MATERIAL: Record<string, string> = {
  HomeOutlined: 'home',
  SendOutlined: 'send',
  EnvironmentOutlined: 'place',
  ScheduleOutlined: 'schedule',
  CalendarOutlined: 'event',
  BarChartOutlined: 'bar_chart',
  PlayCircleOutlined: 'play_circle',
  HeartOutlined: 'favorite_border',
  DollarOutlined: 'attach_money',
  ShoppingOutlined: 'shopping_bag',
  LinkOutlined: 'link',
  GlobalOutlined: 'language',
  BookOutlined: 'menu_book',
  TagOutlined: 'sell',
  VideoCameraOutlined: 'videocam',
  FileTextOutlined: 'description',
  TrophyOutlined: 'emoji_events',
  FolderOutlined: 'folder',
  AppstoreOutlined: 'apps',
  WalletOutlined: 'account_balance_wallet',
};

/**
 * Convert an Ant Design icon name to a Material Icons ligature name.
 * Uses the explicit mapping first, then falls back to stripping the
 * Outlined/Filled/TwoTone suffix and converting PascalCase to snake_case.
 */
function toMaterialIcon(antIcon: string): string {
  if (ANT_ICON_TO_MATERIAL[antIcon]) return ANT_ICON_TO_MATERIAL[antIcon];
  // Fallback: strip suffix, convert PascalCase → snake_case
  const base = antIcon.replace(/(Outlined|Filled|TwoTone)$/, '');
  return base.replace(/([a-z])([A-Z])/g, '$1_$2').toLowerCase();
}

interface NavConfigItem {
  id: string;
  label: string;
  path: string;
  icon: string;
  enabled: boolean;
  order: number;
  type: 'builtin' | 'custom' | 'group';
  featureFlag?: string;
  external?: boolean;
  children?: NavConfigItem[];
}

/** Extended nav item with rendering hints (not persisted in schema) */
interface RenderNavItem extends HeaderNavItem {
  /** When true, path is rendered as direct href (not rewritten via data-path JS) */
  isAbsoluteHref?: boolean;
}

/** A group of nav items rendered as a dropdown on desktop / expandable section on mobile */
interface RenderNavGroup {
  id: string;
  label: string;
  icon: string;
  type: 'group';
  children: RenderNavItem[];
}

/** A renderable item: either a single link or a group dropdown */
type RenderItem = RenderNavItem | RenderNavGroup;

class HeaderBuilderService {
  /**
   * Read the current header config from disk.
   * Returns defaults if no config file exists.
   */
  async readConfig(): Promise<HeaderConfig> {
    try {
      if (!existsSync(CONFIG_PATH)) {
        return { ...DEFAULT_CONFIG };
      }
      const raw = await readFile(CONFIG_PATH, 'utf-8');
      const parsed = JSON.parse(raw);
      const validated = headerConfigSchema.parse(parsed);
      return validated;
    } catch (err) {
      logger.warn('Failed to read header config, returning defaults', err);
      return { ...DEFAULT_CONFIG };
    }
  }

  /**
   * Validate, save config, and regenerate main.html.
   */
  async writeConfig(config: HeaderConfig): Promise<void> {
    // Validate with Zod
    const validated = headerConfigSchema.parse(config);

    // Write config JSON
    await writeFile(CONFIG_PATH, JSON.stringify(validated, null, 2), 'utf-8');
    logger.info('Header config saved');

    // Generate or remove main.html
    if (validated.enabled) {
      const html = this.generateMainHtml(validated);
      await writeFile(MAIN_HTML_PATH, html, 'utf-8');
      logger.info('Generated main.html with header nav bar');
    } else {
      // Write minimal passthrough so landing pages still extend main.html
      const passthrough = '{# Auto-generated by Changemaker Lite Header Builder — header disabled #}\n{% extends "base.html" %}\n';
      await writeFile(MAIN_HTML_PATH, passthrough, 'utf-8');
      logger.info('Generated passthrough main.html (header disabled)');
    }
  }

  /**
   * Reset to defaults: remove config file and main.html.
   */
  async resetToDefaults(): Promise<void> {
    try { await unlink(CONFIG_PATH); } catch { /* file may not exist */ }
    try { await unlink(MAIN_HTML_PATH); } catch { /* file may not exist */ }
    logger.info('Header config reset to defaults');
  }

  /**
   * Generate the Jinja2 main.html template from config.
   * Mirrors the PublicNavBar style: 56px gradient bar, left brand, right nav links.
   */
  generateMainHtml(config: HeaderConfig | { enabled: boolean; items: RenderNavItem[]; style: HeaderConfig['style'] & { colorBgBase?: string; colorBgContainer?: string } }): string {
    const enabledItems = config.items
      .filter((item) => item.enabled)
      .sort((a, b) => a.order - b.order);

    const links = enabledItems.map((item) => this.renderNavLink(item)).join('\n      ');

    const { backgroundColor, textColor } = config.style;
    const colorBgBase = ('colorBgBase' in config.style ? config.style.colorBgBase : undefined) || '#0d1b2a';
    const colorBgContainer = ('colorBgContainer' in config.style ? config.style.colorBgContainer : undefined) || '#1b2838';

    return `{# Auto-generated by Changemaker Lite Header Builder — do not edit manually #}
{% extends "base.html" %}

{% block announce %}
<link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet">
<nav class="cm-header-nav" role="navigation" aria-label="Application">
  <div class="cm-header-nav__brand">
    <a href="#" data-path="/home" class="cm-header-nav__brand-link">
      <span class="cm-header-nav__brand-text">{{ config.site_name }}</span>
    </a>
  </div>
  <div class="cm-header-nav__links">
    <div class="cm-header-nav__links-inner">
      ${links}
      <a href="#" data-path="/app" class="cm-header-nav__link">
        <span class="material-icons-outlined">dashboard</span>
        <span class="cm-header-nav__label">Admin</span>
      </a>
    </div>
    <button class="cm-header-nav__hamburger" aria-label="Open navigation menu">
      <span class="material-icons-outlined">menu</span>
    </button>
  </div>
</nav>
<div class="cm-header-nav__mobile-drawer" id="cm-mobile-drawer">
  <div class="cm-header-nav__mobile-header">
    <span class="cm-header-nav__brand-text">{{ config.site_name }}</span>
    <button class="cm-header-nav__mobile-close" aria-label="Close navigation menu">
      <span class="material-icons-outlined">close</span>
    </button>
  </div>
  <div class="cm-header-nav__mobile-links">
    ${enabledItems.map((item) => this.renderMobileNavLink(item)).join('\n    ')}
    <a href="#" data-path="/app" class="cm-header-nav__mobile-link">
      <span class="material-icons-outlined">dashboard</span>
      <span>Admin</span>
    </a>
  </div>
</div>
<div class="cm-header-nav__mobile-overlay" id="cm-mobile-overlay"></div>
<script>
(function() {
  var h = location.hostname;
  var base;
  if (h === 'localhost' || h === '127.0.0.1') {
    base = location.protocol + '//localhost:' + ({{ config.extra.admin_port | default(0) }} || 3000);
  } else {
    var parts = h.split('.');
    if (parts.length >= 3) { parts[0] = 'app'; }
    else { parts.unshift('app'); }
    base = location.protocol + '//' + parts.join('.');
  }
  var links = document.querySelectorAll('[data-path]');
  for (var i = 0; i < links.length; i++) {
    links[i].setAttribute('href', base + links[i].getAttribute('data-path'));
  }
  // Highlight active nav link based on current path
  var path = location.pathname;
  var activeLink = null;
  if (path.indexOf('/docs') === 0) activeLink = 'docs';
  document.querySelectorAll('.cm-header-nav__link[data-nav-id], .cm-header-nav__mobile-link[data-nav-id]').forEach(function(el) {
    if (el.getAttribute('data-nav-id') === activeLink) {
      el.classList.add('cm-header-nav__link--active');
    }
  });
  // Hamburger toggle
  var hamburger = document.querySelector('.cm-header-nav__hamburger');
  var drawer = document.getElementById('cm-mobile-drawer');
  var overlay = document.getElementById('cm-mobile-overlay');
  var closeBtn = document.querySelector('.cm-header-nav__mobile-close');
  function openDrawer() { drawer.classList.add('open'); overlay.classList.add('open'); }
  function closeDrawer() { drawer.classList.remove('open'); overlay.classList.remove('open'); }
  if (hamburger) hamburger.addEventListener('click', openDrawer);
  if (closeBtn) closeBtn.addEventListener('click', closeDrawer);
  if (overlay) overlay.addEventListener('click', closeDrawer);
})();
</script>
<style>
.md-banner {
  background: ${escapeHtml(backgroundColor)} !important;
  color: ${escapeHtml(textColor)} !important;
  padding: 0 !important;
}
.md-banner__button {
  display: none !important;
}
.cm-header-nav {
  background: ${escapeHtml(backgroundColor)};
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 24px;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  z-index: 10;
  box-sizing: border-box;
}
.cm-header-nav a {
  color: rgba(255, 255, 255, 0.85) !important;
}
.cm-header-nav__brand-link {
  display: flex;
  align-items: center;
  gap: 10px;
  text-decoration: none !important;
  color: #fff !important;
}
.cm-header-nav__brand-text {
  font-size: 18px;
  font-weight: 600;
  color: #fff !important;
}
.cm-header-nav__links {
  display: flex;
  align-items: center;
}
.cm-header-nav__links-inner {
  display: flex;
  align-items: center;
  gap: 16px;
}
.cm-header-nav__link {
  color: rgba(255, 255, 255, 0.85) !important;
  text-decoration: none !important;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 14px;
  transition: color 0.2s, border-color 0.2s;
  white-space: nowrap;
  padding-bottom: 2px;
  border-bottom: 2px solid transparent;
}
.cm-header-nav__link:hover {
  color: #fff !important;
  text-decoration: none !important;
}
.cm-header-nav__link--active,
.cm-header-nav__link--active:hover {
  color: #fff !important;
  font-weight: 600;
  border-bottom-color: #fff;
}
.cm-header-nav__link .material-icons-outlined {
  font-size: 16px;
}
.cm-header-nav__hamburger {
  display: none;
  background: none;
  border: none;
  cursor: pointer;
  padding: 4px 8px;
  color: #fff;
}
.cm-header-nav__hamburger .material-icons-outlined {
  font-size: 24px;
}
/* Mobile drawer */
.cm-header-nav__mobile-drawer {
  position: fixed;
  top: 0;
  right: -280px;
  width: 280px;
  height: 100vh;
  background: ${escapeHtml(colorBgBase)};
  z-index: 10001;
  transition: right 0.3s ease;
  display: flex;
  flex-direction: column;
}
.cm-header-nav__mobile-drawer.open {
  right: 0;
}
.cm-header-nav__mobile-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 24px;
  border-bottom: 1px solid rgba(255,255,255,0.1);
  background: ${escapeHtml(colorBgContainer)};
}
.cm-header-nav__mobile-close {
  background: none;
  border: none;
  cursor: pointer;
  color: rgba(255,255,255,0.85);
  padding: 4px;
}
.cm-header-nav__mobile-links {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 16px 0;
}
.cm-header-nav__mobile-link {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 24px;
  color: rgba(255,255,255,0.85) !important;
  text-decoration: none !important;
  font-size: 15px;
  border-radius: 4px;
}
.cm-header-nav__mobile-link:hover {
  background: rgba(255,255,255,0.1);
  color: #fff !important;
  text-decoration: none !important;
}
.cm-header-nav__mobile-link--active {
  color: #fff !important;
  font-weight: 600;
  background: rgba(255,255,255,0.1);
}
.cm-header-nav__mobile-link .material-icons-outlined {
  font-size: 18px;
}
.cm-header-nav__mobile-overlay {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0,0,0,0.5);
  z-index: 10000;
}
.cm-header-nav__mobile-overlay.open {
  display: block;
}
@media (max-width: 768px) {
  .cm-header-nav { padding: 0 16px; }
  .cm-header-nav__links-inner { display: none; }
  .cm-header-nav__hamburger { display: block; }
}
</style>
{% endblock %}
`;
  }

  /**
   * Regenerate main.html from the centralized navConfig stored in SiteSettings.
   * Called when navConfig changes via the settings API.
   */
  async regenerateFromNavConfig(
    navConfigItems: NavConfigItem[],
    settings?: {
      publicHeaderGradient?: string;
      publicColorBgBase?: string;
      publicColorBgContainer?: string;
      // Feature flags
      enableInfluence?: boolean | null;
      enableMap?: boolean | null;
      enableMediaFeatures?: boolean | null;
      enablePayments?: boolean | null;
      enableEvents?: boolean | null;
      enableMeetingPlanner?: boolean | null;
      enableTicketedEvents?: boolean | null;
      enableSocial?: boolean | null;
      enableMeet?: boolean | null;
      enableLandingPages?: boolean | null;
    },
  ): Promise<void> {
    try {
      // Opt-out flags: visible by default, hidden only when explicitly false
      const OPT_OUT_FLAGS = new Set(['enableInfluence', 'enableMap', 'enableMediaFeatures', 'enableEvents']);

      const featureFlagMap: Record<string, boolean | null | undefined> = {
        enableInfluence: settings?.enableInfluence,
        enableMap: settings?.enableMap,
        enableMediaFeatures: settings?.enableMediaFeatures,
        enablePayments: settings?.enablePayments,
        enableEvents: settings?.enableEvents,
        enableMeetingPlanner: settings?.enableMeetingPlanner,
        enableTicketedEvents: settings?.enableTicketedEvents,
        enableSocial: settings?.enableSocial,
        enableMeet: settings?.enableMeet,
        enableLandingPages: settings?.enableLandingPages,
      };

      /** Check if an item passes its feature flag filter */
      const passesFeatureFlag = (item: NavConfigItem): boolean => {
        if (!item.featureFlag) return true;
        if (OPT_OUT_FLAGS.has(item.featureFlag)) {
          return featureFlagMap[item.featureFlag] !== false;
        }
        return featureFlagMap[item.featureFlag] === true;
      };

      /** Convert a NavConfigItem to a RenderNavItem */
      const toRenderItem = (item: NavConfigItem): RenderNavItem => {
        let resolvedPath = item.path;
        if (item.path === '$landing') resolvedPath = '/';
        else if (item.path === '$docs') resolvedPath = '/docs/';

        return {
          id: item.id,
          label: item.label,
          path: resolvedPath,
          icon: toMaterialIcon(item.icon),
          enabled: true,
          order: item.order,
          type: item.type as 'builtin' | 'custom',
          openInNewTab: item.path.startsWith('$') ? false : item.external,
          isAbsoluteHref: item.path.startsWith('$'),
        };
      };

      // Build group-aware RenderItem[] preserving dropdown structure
      const renderItems: RenderItem[] = [];
      const sortedTopLevel = [...navConfigItems].sort((a, b) => a.order - b.order);

      for (const item of sortedTopLevel) {
        if (!item.enabled) continue;

        if (item.type === 'group' && item.children) {
          // Check group-level feature flag
          if (!passesFeatureFlag(item)) continue;

          // Filter children individually
          const enabledChildren = item.children
            .filter((child) => child.enabled && passesFeatureFlag(child))
            .sort((a, b) => a.order - b.order)
            .map(toRenderItem);

          if (enabledChildren.length === 0) continue;

          // If only one child survives, render it as a flat item instead of a dropdown
          if (enabledChildren.length === 1) {
            renderItems.push(enabledChildren[0]);
          } else {
            renderItems.push({
              id: item.id,
              label: item.label,
              icon: toMaterialIcon(item.icon),
              type: 'group',
              children: enabledChildren,
            });
          }
        } else {
          if (!passesFeatureFlag(item)) continue;
          renderItems.push(toRenderItem(item));
        }
      }

      if (renderItems.length === 0) {
        const passthrough = '{# Auto-generated by Changemaker Lite Header Builder — header disabled #}\n{% extends "base.html" %}\n';
        await writeFile(MAIN_HTML_PATH, passthrough, 'utf-8');
        logger.info('Generated passthrough main.html (no nav items enabled)');
        return;
      }

      const backgroundColor = settings?.publicHeaderGradient || 'linear-gradient(135deg, #005a9c 0%, #007acc 100%)';
      const colorBgBase = settings?.publicColorBgBase || '#0d1b2a';
      const colorBgContainer = settings?.publicColorBgContainer || '#1b2838';

      const html = this.generateMainHtmlV2(renderItems, {
        backgroundColor,
        textColor: '#ffffff',
        colorBgBase,
        colorBgContainer,
      });
      await writeFile(MAIN_HTML_PATH, html, 'utf-8');
      logger.info('Regenerated main.html from navConfig');
    } catch (err) {
      logger.error('Failed to regenerate main.html from navConfig', err);
    }
  }

  /**
   * Generate main.html from a mixed RenderItem[] (groups + flat items).
   * Groups render as CSS hover dropdowns on desktop and expandable sections on mobile.
   */
  private generateMainHtmlV2(
    items: RenderItem[],
    style: { backgroundColor: string; textColor: string; colorBgBase: string; colorBgContainer: string },
  ): string {
    const { backgroundColor, textColor, colorBgBase, colorBgContainer } = style;

    const desktopLinks = items
      .map((item) =>
        item.type === 'group'
          ? this.renderNavDropdown(item as RenderNavGroup)
          : this.renderNavLink(item as RenderNavItem),
      )
      .join('\n      ');

    const mobileLinks = items
      .map((item) =>
        item.type === 'group'
          ? this.renderMobileGroup(item as RenderNavGroup)
          : this.renderMobileNavLink(item as RenderNavItem),
      )
      .join('\n    ');

    return `{# Auto-generated by Changemaker Lite Header Builder — do not edit manually #}
{% extends "base.html" %}

{% block announce %}
<link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet">
<nav class="cm-header-nav" role="navigation" aria-label="Application">
  <div class="cm-header-nav__brand">
    <a href="#" data-path="/home" class="cm-header-nav__brand-link">
      <span class="cm-header-nav__brand-text">{{ config.site_name }}</span>
    </a>
  </div>
  <div class="cm-header-nav__links">
    <div class="cm-header-nav__links-inner">
      ${desktopLinks}
      <label for="__search" class="cm-header-nav__utility" title="Search">
        <span class="material-icons-outlined">search</span>
      </label>
      <button class="cm-header-nav__utility" id="cm-palette-toggle" title="Toggle dark mode" type="button">
        <span class="material-icons-outlined">dark_mode</span>
      </button>
      <a href="#" data-path="/login" class="cm-header-nav__link" id="cm-signin-link">
        <span class="material-icons-outlined">login</span>
        <span class="cm-header-nav__label">Sign In</span>
      </a>
      <div class="cm-header-nav__dropdown" id="cm-admin-dropdown" style="display:none">
        <span class="cm-header-nav__link cm-header-nav__dropdown-trigger">
          <span class="material-icons-outlined">person</span>
          <span class="cm-header-nav__label">Admin</span>
          <span class="material-icons-outlined cm-header-nav__chevron">expand_more</span>
        </span>
        <div class="cm-header-nav__dropdown-menu cm-header-nav__dropdown-menu--right">
          <a href="#" data-path="/app" class="cm-header-nav__dropdown-item"><span class="material-icons-outlined">dashboard</span><span>Admin Panel</span></a>
          <a href="#" data-path="/volunteer" class="cm-header-nav__dropdown-item"><span class="material-icons-outlined">volunteer_activism</span><span>Volunteer Portal</span></a>
          <a href="#" data-path="/volunteer/profile" class="cm-header-nav__dropdown-item"><span class="material-icons-outlined">account_circle</span><span>My Profile</span></a>
          <a href="#" data-path="/logout" class="cm-header-nav__dropdown-item"><span class="material-icons-outlined">logout</span><span>Logout</span></a>
        </div>
      </div>
    </div>
    <button class="cm-header-nav__hamburger" aria-label="Open navigation menu">
      <span class="material-icons-outlined">menu</span>
    </button>
  </div>
</nav>
<div class="cm-header-nav__mobile-drawer" id="cm-mobile-drawer">
  <div class="cm-header-nav__mobile-header">
    <span class="cm-header-nav__brand-text">{{ config.site_name }}</span>
    <button class="cm-header-nav__mobile-close" aria-label="Close navigation menu">
      <span class="material-icons-outlined">close</span>
    </button>
  </div>
  <div class="cm-header-nav__mobile-links">
    <label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
      <span class="material-icons-outlined">search</span>
      <span>Search</span>
    </label>
    <button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-mobile-palette-toggle" type="button">
      <span class="material-icons-outlined">dark_mode</span>
      <span>Dark Mode</span>
    </button>
    <button class="cm-header-nav__mobile-link cm-header-nav__utility-btn" id="cm-docs-sidebar-toggle" type="button">
      <span class="material-icons-outlined">menu_book</span>
      <span>Docs Navigation</span>
    </button>
    <div class="cm-header-nav__mobile-divider"></div>
    ${mobileLinks}
    <div class="cm-header-nav__mobile-divider"></div>
    <a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
      <span class="material-icons-outlined">login</span>
      <span>Sign In</span>
    </a>
    <div class="cm-header-nav__mobile-group" data-group-id="admin" id="cm-mobile-admin-group" style="display:none">
      <span class="cm-header-nav__mobile-link cm-header-nav__mobile-group-trigger" role="button">
        <span class="material-icons-outlined">person</span>
        <span style="flex:1">Admin</span>
        <span class="material-icons-outlined cm-header-nav__mobile-chevron">expand_more</span>
      </span>
      <div class="cm-header-nav__mobile-group-children">
        <a href="#" data-path="/app" class="cm-header-nav__mobile-link" style="padding-left:48px"><span class="material-icons-outlined">dashboard</span><span>Admin Panel</span></a>
        <a href="#" data-path="/volunteer" class="cm-header-nav__mobile-link" style="padding-left:48px"><span class="material-icons-outlined">volunteer_activism</span><span>Volunteer Portal</span></a>
        <a href="#" data-path="/volunteer/profile" class="cm-header-nav__mobile-link" style="padding-left:48px"><span class="material-icons-outlined">account_circle</span><span>My Profile</span></a>
        <a href="#" data-path="/logout" class="cm-header-nav__mobile-link" style="padding-left:48px"><span class="material-icons-outlined">logout</span><span>Logout</span></a>
      </div>
    </div>
  </div>
</div>
<div class="cm-header-nav__mobile-overlay" id="cm-mobile-overlay"></div>
<script>
(function() {
  var h = location.hostname;
  var base;
  if (h === 'localhost' || h === '127.0.0.1') {
    base = location.protocol + '//localhost:' + ({{ config.extra.admin_port | default(0) }} || 3000);
  } else {
    var parts = h.split('.');
    if (parts.length >= 3) { parts[0] = 'app'; }
    else { parts.unshift('app'); }
    base = location.protocol + '//' + parts.join('.');
  }
  var links = document.querySelectorAll('[data-path]');
  for (var i = 0; i < links.length; i++) {
    links[i].setAttribute('href', base + links[i].getAttribute('data-path'));
  }
  // Highlight active nav link based on current path
  var path = location.pathname;
  var activeLink = null;
  if (path.indexOf('/docs') === 0) activeLink = 'docs';
  document.querySelectorAll('.cm-header-nav__link[data-nav-id], .cm-header-nav__mobile-link[data-nav-id]').forEach(function(el) {
    if (el.getAttribute('data-nav-id') === activeLink) {
      el.classList.add('cm-header-nav__link--active');
    }
  });
  // Hamburger toggle
  var hamburger = document.querySelector('.cm-header-nav__hamburger');
  var drawer = document.getElementById('cm-mobile-drawer');
  var overlay = document.getElementById('cm-mobile-overlay');
  var closeBtn = document.querySelector('.cm-header-nav__mobile-close');
  function openDrawer() { drawer.classList.add('open'); overlay.classList.add('open'); }
  function closeDrawer() { drawer.classList.remove('open'); overlay.classList.remove('open'); }
  if (hamburger) hamburger.addEventListener('click', openDrawer);
  if (closeBtn) closeBtn.addEventListener('click', closeDrawer);
  if (overlay) overlay.addEventListener('click', closeDrawer);
  // Mobile group expand/collapse toggles
  document.querySelectorAll('.cm-header-nav__mobile-group-trigger').forEach(function(trigger) {
    trigger.addEventListener('click', function() {
      var group = this.closest('.cm-header-nav__mobile-group');
      var children = group.querySelector('.cm-header-nav__mobile-group-children');
      var isExpanded = group.classList.contains('expanded');
      if (isExpanded) {
        group.classList.remove('expanded');
        children.style.display = 'none';
      } else {
        group.classList.add('expanded');
        children.style.display = 'block';
      }
    });
  });
  // Auth-aware: show Admin dropdown for logged-in users, Sign In for guests.
  // Uses hidden iframe + postMessage to read auth state from the app's origin.
  function showAdminMenu() {
    var s1 = document.getElementById('cm-signin-link');
    var s2 = document.getElementById('cm-mobile-signin-link');
    var a1 = document.getElementById('cm-admin-dropdown');
    var a2 = document.getElementById('cm-mobile-admin-group');
    if (s1) s1.style.display = 'none';
    if (s2) s2.style.display = 'none';
    if (a1) a1.style.display = '';
    if (a2) a2.style.display = '';
  }
  // 1. Same-origin check (works when MkDocs served from same origin as app)
  try {
    var stored = localStorage.getItem('cml-auth');
    if (stored) {
      var parsed = JSON.parse(stored);
      if (parsed && parsed.state && parsed.state.accessToken) {
        showAdminMenu();
      }
    }
  } catch(e) {}
  // 2. Cross-origin check via hidden iframe + postMessage
  var iframe = document.createElement('iframe');
  iframe.style.display = 'none';
  iframe.src = base + '/auth-check.html?origin=' + encodeURIComponent(location.origin);
  window.addEventListener('message', function(event) {
    if (event.origin !== base) return;
    if (event.data && event.data.type === 'cml-auth-status' && event.data.authenticated) {
      showAdminMenu();
    }
  });
  document.body.appendChild(iframe);
  // Palette toggle (dark/light mode)
  function togglePalette() {
    var inputs = document.querySelectorAll('.cm-palette-container input[name="__palette"]');
    for (var i = 0; i < inputs.length; i++) {
      if (!inputs[i].checked) { inputs[i].click(); break; }
    }
    setTimeout(updatePaletteIcon, 50);
  }
  function updatePaletteIcon() {
    var scheme = document.body.getAttribute('data-md-color-scheme') || 'default';
    var isDark = scheme === 'slate';
    var icon = isDark ? 'light_mode' : 'dark_mode';
    document.querySelectorAll('#cm-palette-toggle .material-icons-outlined, #cm-mobile-palette-toggle .material-icons-outlined').forEach(function(el) {
      el.textContent = icon;
    });
    var ml = document.querySelector('#cm-mobile-palette-toggle span:not(.material-icons-outlined)');
    if (ml) ml.textContent = isDark ? 'Light Mode' : 'Dark Mode';
  }
  var ptBtn = document.getElementById('cm-palette-toggle');
  var ptBtnM = document.getElementById('cm-mobile-palette-toggle');
  if (ptBtn) ptBtn.addEventListener('click', togglePalette);
  if (ptBtnM) ptBtnM.addEventListener('click', function() { togglePalette(); closeDrawer(); });
  // Docs sidebar toggle (opens Material's docs navigation drawer)
  var docsSidebarBtn = document.getElementById('cm-docs-sidebar-toggle');
  if (docsSidebarBtn) {
    docsSidebarBtn.addEventListener('click', function() {
      closeDrawer();
      var dt = document.getElementById('__drawer');
      if (dt) { dt.checked = !dt.checked; dt.dispatchEvent(new Event('change')); }
    });
  }
  // Close custom drawer when search label is clicked on mobile + auto-focus input
  document.querySelectorAll('label[for="__search"]').forEach(function(el) {
    el.addEventListener('click', function() {
      if (el.classList.contains('md-search__overlay')) return; // overlay has its own handler
      closeDrawer();
      setTimeout(function() {
        var input = document.querySelector('.md-search__input');
        if (input) input.focus();
      }, 150);
    });
  });
  // Search activation: Material may open search via checkbox OR by focusing the
  // input directly (varies by version). Detect both and mirror as body class.
  // NOTE: search DOM elements render AFTER the announce block in the template,
  // so we must defer element queries until DOMContentLoaded.
  var searchToggle = null;
  var searchInput = null;
  // Apply search layout inline styles (CSS-in-stylesheet is unreliable due to
  // cross-origin Material stylesheets overriding !important rules)
  function applySearchLayout(active) {
    var inner = document.querySelector('.md-search__inner');
    var output = document.querySelector('.md-search__output');
    var scrollwrap = document.querySelector('.md-search__scrollwrap');
    if (!inner) return;
    var isDesktop = window.matchMedia('(min-width: 60em)').matches;
    if (active) {
      inner.style.setProperty('display', 'flex', 'important');
      inner.style.setProperty('flex-direction', 'column', 'important');
      inner.style.setProperty('overflow', 'hidden', 'important');
      // Firefox needs explicit height (not just max-height) for flex children to grow
      if (isDesktop) {
        inner.style.setProperty('height', 'calc(100vh - 64px)', 'important');
      }
      if (output) {
        output.style.setProperty('position', 'relative', 'important');
        output.style.setProperty('flex', '1 1 0px', 'important');
        output.style.setProperty('min-height', '0', 'important');
        output.style.setProperty('display', 'flex', 'important');
        output.style.setProperty('flex-direction', 'column', 'important');
        output.style.setProperty('overflow', 'hidden', 'important');
        output.style.setProperty('width', '100%', 'important');
      }
      if (scrollwrap) {
        scrollwrap.style.setProperty('max-height', 'none', 'important');
        scrollwrap.style.setProperty('flex', '1 1 0px', 'important');
        scrollwrap.style.setProperty('min-height', '0', 'important');
        scrollwrap.style.setProperty('overflow-y', 'auto', 'important');
      }
      // Force search result elements visible + ensure proper stacking (Firefox)
      var resultList = document.querySelector('.md-search-result__list');
      if (resultList) {
        resultList.style.setProperty('display', 'block', 'important');
        resultList.style.setProperty('visibility', 'visible', 'important');
        resultList.style.setProperty('opacity', '1', 'important');
        resultList.style.setProperty('max-height', 'none', 'important');
        resultList.style.setProperty('overflow', 'visible', 'important');
        resultList.style.setProperty('color', 'var(--md-default-fg-color)', 'important');
      }
      var resultContainer = document.querySelector('.md-search-result');
      if (resultContainer) {
        resultContainer.style.setProperty('display', 'block', 'important');
        resultContainer.style.setProperty('visibility', 'visible', 'important');
        resultContainer.style.setProperty('opacity', '1', 'important');
      }
      // Ensure scrollwrap has z-index above overlay
      if (scrollwrap) {
        scrollwrap.style.setProperty('position', 'relative', 'important');
        scrollwrap.style.setProperty('z-index', '1', 'important');
        scrollwrap.style.setProperty('background', 'var(--md-default-bg-color)', 'important');
      }
    } else {
      inner.style.removeProperty('display');
      inner.style.removeProperty('flex-direction');
      inner.style.removeProperty('overflow');
      inner.style.removeProperty('height');
      if (output) {
        output.style.removeProperty('position');
        output.style.removeProperty('flex');
        output.style.removeProperty('min-height');
        output.style.removeProperty('display');
        output.style.removeProperty('flex-direction');
        output.style.removeProperty('overflow');
        output.style.removeProperty('width');
      }
      if (scrollwrap) {
        scrollwrap.style.removeProperty('max-height');
        scrollwrap.style.removeProperty('flex');
        scrollwrap.style.removeProperty('min-height');
        scrollwrap.style.removeProperty('overflow-y');
        scrollwrap.style.removeProperty('position');
        scrollwrap.style.removeProperty('z-index');
        scrollwrap.style.removeProperty('background');
      }
      var resultList = document.querySelector('.md-search-result__list');
      if (resultList) resultList.removeAttribute('style');
      var resultContainer = document.querySelector('.md-search-result');
      if (resultContainer) resultContainer.removeAttribute('style');
    }
  }
  function activateSearch() {
    document.body.classList.add('cm-search-active');
    if (searchToggle) searchToggle.checked = true;
    applySearchLayout(true);
  }
  function deactivateSearch() {
    document.body.classList.remove('cm-search-active');
    if (searchToggle) searchToggle.checked = false;
    if (searchInput) searchInput.blur();
    applySearchLayout(false);
  }
  function isSearchActive() {
    return document.body.classList.contains('cm-search-active');
  }
  // Custom search labels in the cm-header-nav (these exist now, in announce block)
  document.querySelectorAll('label[for="__search"]').forEach(function(lbl) {
    lbl.addEventListener('click', function() {
      if (lbl.classList.contains('md-search__overlay')) return;
      setTimeout(function() { activateSearch(); if (searchInput) searchInput.focus(); }, 50);
    });
  });
  // Deferred bindings: attach handlers to search elements once they exist in the DOM
  document.addEventListener('DOMContentLoaded', function() {
    searchToggle = document.getElementById('__search');
    searchInput = document.querySelector('.md-search__input');
    // Detect search open via input focus
    if (searchInput) {
      searchInput.addEventListener('focus', activateSearch);
    }
    // Detect search open via checkbox
    if (searchToggle) {
      searchToggle.addEventListener('change', function() {
        if (searchToggle.checked) activateSearch(); else deactivateSearch();
      });
    }
    // Click on overlay (md-search__overlay label) to dismiss search
    var searchOverlay = document.querySelector('.md-search__overlay');
    if (searchOverlay) {
      searchOverlay.addEventListener('click', function(e) {
        e.preventDefault();
        e.stopPropagation();
        if (isSearchActive()) deactivateSearch();
      });
    }
  });
  // Click-outside to dismiss search (on document, works immediately)
  document.addEventListener('mousedown', function(e) {
    if (!isSearchActive()) return;
    var panel = document.querySelector('.md-search__inner');
    if (panel && panel.contains(e.target)) return;
    // Let the overlay's own click handler deal with it
    if (e.target.closest && e.target.closest('.md-search__overlay')) return;
    if (e.target.closest && e.target.closest('label[for="__search"]')) return;
    if (e.target.closest && e.target.closest('.cm-header-nav__utility')) return;
    deactivateSearch();
  });
  // Escape key to dismiss
  document.addEventListener('keydown', function(e) {
    if (e.key === 'Escape' && isSearchActive()) setTimeout(deactivateSearch, 50);
  });
  // Init palette icon + observe changes
  setTimeout(updatePaletteIcon, 100);
  new MutationObserver(function() { updatePaletteIcon(); })
    .observe(document.body, { attributes: true, attributeFilter: ['data-md-color-scheme'] });
})();
</script>
<style>
.md-banner {
  background: transparent !important;
  color: ${escapeHtml(textColor)} !important;
  padding: 0 !important;
  margin: 0 !important;
  overflow: visible !important;
  border: none !important;
  box-shadow: none !important;
  position: relative;
  z-index: 301;
}
.md-banner__inner {
  overflow: visible !important;
  margin: 0 !important;
  padding: 0 !important;
  max-width: 100% !important;
}
.md-banner__button {
  display: none !important;
}
.cm-header-nav {
  background: ${escapeHtml(backgroundColor)};
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 24px;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  position: relative;
  z-index: 100;
  box-sizing: border-box;
}
.cm-header-nav a {
  color: rgba(255, 255, 255, 0.85) !important;
}
.cm-header-nav__brand-link {
  display: flex;
  align-items: center;
  gap: 10px;
  text-decoration: none !important;
  color: #fff !important;
}
.cm-header-nav__brand-text {
  font-size: 18px;
  font-weight: 600;
  color: #fff !important;
}
.cm-header-nav__links {
  display: flex;
  align-items: center;
}
.cm-header-nav__links-inner {
  display: flex;
  align-items: center;
  gap: 16px;
}
.cm-header-nav__link {
  color: rgba(255, 255, 255, 0.85) !important;
  text-decoration: none !important;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 14px;
  transition: color 0.2s, border-color 0.2s;
  white-space: nowrap;
  padding-bottom: 2px;
  border-bottom: 2px solid transparent;
}
.cm-header-nav__link:hover {
  color: #fff !important;
  text-decoration: none !important;
}
.cm-header-nav__link--active,
.cm-header-nav__link--active:hover {
  color: #fff !important;
  font-weight: 600;
  border-bottom-color: #fff;
}
.cm-header-nav__link .material-icons-outlined {
  font-size: 16px;
}
.cm-header-nav__hamburger {
  display: none;
  background: none;
  border: none;
  cursor: pointer;
  padding: 4px 8px;
  color: #fff;
}
.cm-header-nav__hamburger .material-icons-outlined {
  font-size: 24px;
}
/* Desktop dropdown menus */
.cm-header-nav__dropdown {
  position: relative;
  display: inline-flex;
  align-items: center;
}
.cm-header-nav__dropdown-trigger {
  cursor: pointer;
  user-select: none;
}
.cm-header-nav__dropdown-trigger .cm-header-nav__chevron {
  font-size: 14px;
  transition: transform 0.2s;
}
.cm-header-nav__dropdown:hover .cm-header-nav__chevron {
  transform: rotate(180deg);
}
.cm-header-nav__dropdown-menu {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  min-width: 180px;
  background: ${escapeHtml(colorBgContainer)};
  border-radius: 8px;
  padding: 6px 0;
  box-shadow: 0 6px 16px rgba(0,0,0,0.3);
  z-index: 100;
  margin-top: 4px;
}
.cm-header-nav__dropdown:hover .cm-header-nav__dropdown-menu {
  display: block;
}
.cm-header-nav__dropdown-menu--right {
  left: auto;
  right: 0;
}
.cm-header-nav__dropdown-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  color: rgba(255, 255, 255, 0.85) !important;
  text-decoration: none !important;
  font-size: 14px;
  white-space: nowrap;
  transition: background 0.15s;
}
.cm-header-nav__dropdown-item:hover {
  background: rgba(255,255,255,0.1);
  color: #fff !important;
  text-decoration: none !important;
}
.cm-header-nav__dropdown-item .material-icons-outlined {
  font-size: 16px;
}
/* Mobile drawer */
.cm-header-nav__mobile-drawer {
  position: fixed;
  top: 0;
  right: -280px;
  width: 280px;
  height: 100vh;
  background: ${escapeHtml(colorBgBase)};
  z-index: 10001;
  transition: right 0.3s ease;
  display: flex;
  flex-direction: column;
}
.cm-header-nav__mobile-drawer.open {
  right: 0;
}
.cm-header-nav__mobile-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 24px;
  border-bottom: 1px solid rgba(255,255,255,0.1);
  background: ${escapeHtml(colorBgContainer)};
}
.cm-header-nav__mobile-close {
  background: none;
  border: none;
  cursor: pointer;
  color: rgba(255,255,255,0.85);
  padding: 4px;
}
.cm-header-nav__mobile-links {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 16px 0;
}
.cm-header-nav__mobile-link {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 24px;
  color: rgba(255,255,255,0.85) !important;
  text-decoration: none !important;
  font-size: 15px;
  border-radius: 4px;
}
.cm-header-nav__mobile-link:hover {
  background: rgba(255,255,255,0.1);
  color: #fff !important;
  text-decoration: none !important;
}
.cm-header-nav__mobile-link--active {
  color: #fff !important;
  font-weight: 600;
  background: rgba(255,255,255,0.1);
}
.cm-header-nav__mobile-link .material-icons-outlined {
  font-size: 18px;
}
/* Mobile group expand/collapse */
.cm-header-nav__mobile-group-trigger {
  cursor: pointer;
  user-select: none;
}
.cm-header-nav__mobile-chevron {
  font-size: 14px !important;
  transition: transform 0.2s;
}
.cm-header-nav__mobile-group.expanded .cm-header-nav__mobile-chevron {
  transform: rotate(180deg);
}
.cm-header-nav__mobile-group-children {
  display: none;
}
.cm-header-nav__mobile-overlay {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0,0,0,0.5);
  z-index: 10000;
}
.cm-header-nav__mobile-overlay.open {
  display: block;
}
@media (max-width: 768px) {
  .cm-header-nav { padding: 0 16px; }
  .cm-header-nav__links-inner { display: none; }
  .cm-header-nav__hamburger { display: block; }
  .cm-header-nav__dropdown-menu { display: none !important; }
}
/* Sidebar sticky offset = 0 since blue header scrolls away */
:root {
  --md-header-height: 0px;
}
/* Hidden Material header — keeps search anchored near tabs */
.md-header--cm-hidden {
  height: 0 !important;
  min-height: 0 !important;
  padding: 0 !important;
  margin: 0 !important;
  border: 0 !important;
  overflow: visible !important;
  background: transparent !important;
  box-shadow: none !important;
  position: sticky;
  top: 0;
  z-index: 200;
}

/* === DESKTOP SEARCH (>= 60em / 960px) === */
@media screen and (min-width: 60em) {
  /* Fixed dropdown panel — layout (flex) applied via JS inline styles */
  body.cm-search-active .md-header--cm-hidden .md-search__inner {
    position: fixed !important;
    top: 48px !important;
    right: 16px !important;
    left: auto !important;
    width: min(34rem, calc(100vw - 32px)) !important;
    max-height: calc(100vh - 64px) !important;
    background: var(--md-default-bg-color) !important;
    border-radius: 0 0 8px 8px !important;
    box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
    z-index: 300 !important;
    opacity: 1 !important;
    transform: none !important;
    visibility: visible !important;
    pointer-events: auto !important;
    clip-path: none !important;
  }

  /* Dark overlay behind search panel — catches clicks to dismiss */
  body.cm-search-active .md-header--cm-hidden .md-search__overlay {
    position: fixed !important;
    top: 0 !important;
    left: 0 !important;
    width: 100vw !important;
    height: 100vh !important;
    background: rgba(0,0,0,0.54) !important;
    opacity: 1 !important;
    z-index: 299 !important;
    border-radius: 0 !important;
    transform: none !important;
    cursor: default !important;
    pointer-events: auto !important;
  }
}

/* === MOBILE SEARCH (< 60em / 960px) === */
@media screen and (max-width: 59.984375em) {
  /* Full-screen search takeover — layout (flex) applied via JS inline styles */
  body.cm-search-active .md-header--cm-hidden .md-search__inner {
    position: fixed !important;
    top: 0 !important;
    left: 0 !important;
    right: 0 !important;
    bottom: 0 !important;
    width: 100% !important;
    height: 100% !important;
    opacity: 1 !important;
    transform: none !important;
    visibility: visible !important;
    pointer-events: auto !important;
    z-index: 300 !important;
    background: var(--md-default-bg-color) !important;
    clip-path: none !important;
  }
}

/* Force search elements visible when active (layout handled by JS inline styles) */
body.cm-search-active .md-header--cm-hidden .md-search {
  display: block !important;
  visibility: visible !important;
  opacity: 1 !important;
  overflow: visible !important;
}
body.cm-search-active .md-header--cm-hidden .md-search__output {
  opacity: 1 !important;
  visibility: visible !important;
  clip-path: none !important;
  transform: none !important;
}
.cm-palette-container {
  height: 0 !important;
  overflow: hidden !important;
}
/* Material tabs: sticky at viewport top when blue header scrolls away */
.md-tabs {
  position: sticky;
  top: 0;
  z-index: 99;
}
/* On mobile, hide tabs (sidebar provides navigation) */
@media (max-width: 768px) {
  .md-tabs { display: none; }
}
/* Utility icon styling */
.cm-header-nav__utility {
  background: none;
  border: none;
  color: rgba(255, 255, 255, 0.7);
  cursor: pointer;
  padding: 4px;
  display: inline-flex;
  align-items: center;
  transition: color 0.2s;
}
.cm-header-nav__utility:hover { color: #fff; }
.cm-header-nav__utility .material-icons-outlined { font-size: 20px; }
.cm-header-nav__utility-btn {
  background: none;
  border: none;
  color: rgba(255,255,255,0.85);
  cursor: pointer;
  font-size: 15px;
  font-family: inherit;
  width: 100%;
  text-align: left;
}
.cm-header-nav__mobile-divider {
  height: 1px;
  background: rgba(255,255,255,0.1);
  margin: 8px 24px;
}
</style>
{% endblock %}

{% block header %}
<header class="md-header md-header--cm-hidden" data-md-component="header">
  <div class="cm-palette-container">
    {% if config.theme.palette %}
      {% if not config.theme.palette is mapping %}
        {% include "partials/palette.html" %}
      {% endif %}
    {% endif %}
  </div>
  {% if "material/search" in config.plugins %}
    {% include "partials/search.html" %}
  {% endif %}
</header>
{% endblock %}

{% block tabs %}
{% if "navigation.tabs" in config.theme.features %}
  {% include "partials/tabs.html" %}
{% endif %}
{% endblock %}
`;
  }

  /**
   * Render a desktop dropdown menu for a nav group.
   * Uses pure CSS :hover — no JavaScript needed for desktop.
   */
  private renderNavDropdown(group: RenderNavGroup): string {
    const iconHtml = group.icon
      ? `<span class="material-icons-outlined">${escapeHtml(group.icon)}</span>`
      : '';

    const childLinks = group.children
      .map((child) => {
        const isAbsolute = child.isAbsoluteHref || child.path.startsWith('http://') || child.path.startsWith('https://');
        const target = child.openInNewTab ? ' target="_blank" rel="noopener noreferrer"' : '';
        const navId = child.id ? ` data-nav-id="${escapeHtml(child.id)}"` : '';
        const childIcon = child.icon
          ? `<span class="material-icons-outlined">${escapeHtml(child.icon)}</span>`
          : '';
        const href = isAbsolute
          ? `href="${escapeHtml(child.path)}"`
          : `href="#" data-path="${escapeHtml(child.path)}"`;
        return `      <a ${href} class="cm-header-nav__dropdown-item"${navId}${target}>${childIcon}<span>${escapeHtml(child.label)}</span></a>`;
      })
      .join('\n');

    return `<div class="cm-header-nav__dropdown">
        <span class="cm-header-nav__link cm-header-nav__dropdown-trigger">
          ${iconHtml}
          <span class="cm-header-nav__label">${escapeHtml(group.label)}</span>
          <span class="material-icons-outlined cm-header-nav__chevron">expand_more</span>
        </span>
        <div class="cm-header-nav__dropdown-menu">
${childLinks}
        </div>
      </div>`;
  }

  /**
   * Render a mobile expandable group section.
   * Uses JS click handler to toggle visibility.
   */
  private renderMobileGroup(group: RenderNavGroup): string {
    const iconHtml = group.icon
      ? `<span class="material-icons-outlined">${escapeHtml(group.icon)}</span>`
      : '';

    const childLinks = group.children
      .map((child) => {
        const isAbsolute = child.isAbsoluteHref || child.path.startsWith('http://') || child.path.startsWith('https://');
        const target = child.openInNewTab ? ' target="_blank" rel="noopener noreferrer"' : '';
        const navId = child.id ? ` data-nav-id="${escapeHtml(child.id)}"` : '';
        const childIcon = child.icon
          ? `<span class="material-icons-outlined">${escapeHtml(child.icon)}</span>`
          : '';
        const href = isAbsolute
          ? `href="${escapeHtml(child.path)}"`
          : `href="#" data-path="${escapeHtml(child.path)}"`;
        return `      <a ${href} class="cm-header-nav__mobile-link"${navId}${target} style="padding-left:48px">${childIcon}<span>${escapeHtml(child.label)}</span></a>`;
      })
      .join('\n');

    return `<div class="cm-header-nav__mobile-group" data-group-id="${escapeHtml(group.id)}">
      <span class="cm-header-nav__mobile-link cm-header-nav__mobile-group-trigger" role="button">
        ${iconHtml}
        <span style="flex:1">${escapeHtml(group.label)}</span>
        <span class="material-icons-outlined cm-header-nav__mobile-chevron">expand_more</span>
      </span>
      <div class="cm-header-nav__mobile-group-children">
${childLinks}
      </div>
    </div>`;
  }

  /**
   * Render a single nav link element.
   * Items with isAbsoluteHref use direct href (e.g. $token-resolved paths like / or /docs/).
   */
  private renderNavLink(item: RenderNavItem): string {
    const isAbsolute = item.isAbsoluteHref || item.path.startsWith('http://') || item.path.startsWith('https://');
    const target = item.openInNewTab ? ' target="_blank" rel="noopener noreferrer"' : '';
    const navId = item.id ? ` data-nav-id="${escapeHtml(item.id)}"` : '';
    const iconHtml = item.icon
      ? `<span class="material-icons-outlined">${escapeHtml(item.icon)}</span>`
      : '';

    if (isAbsolute) {
      return `<a href="${escapeHtml(item.path)}" class="cm-header-nav__link"${navId}${target}>${iconHtml}<span class="cm-header-nav__label">${escapeHtml(item.label)}</span></a>`;
    }

    return `<a href="#" data-path="${escapeHtml(item.path)}" class="cm-header-nav__link"${navId}${target}>${iconHtml}<span class="cm-header-nav__label">${escapeHtml(item.label)}</span></a>`;
  }

  /**
   * Render a single mobile drawer nav link.
   */
  private renderMobileNavLink(item: RenderNavItem): string {
    const isAbsolute = item.isAbsoluteHref || item.path.startsWith('http://') || item.path.startsWith('https://');
    const target = item.openInNewTab ? ' target="_blank" rel="noopener noreferrer"' : '';
    const navId = item.id ? ` data-nav-id="${escapeHtml(item.id)}"` : '';
    const iconHtml = item.icon
      ? `<span class="material-icons-outlined">${escapeHtml(item.icon)}</span>`
      : '';

    if (isAbsolute) {
      return `<a href="${escapeHtml(item.path)}" class="cm-header-nav__mobile-link"${navId}${target}>${iconHtml}<span>${escapeHtml(item.label)}</span></a>`;
    }

    return `<a href="#" data-path="${escapeHtml(item.path)}" class="cm-header-nav__mobile-link"${navId}${target}>${iconHtml}<span>${escapeHtml(item.label)}</span></a>`;
  }
}

export const headerBuilderService = new HeaderBuilderService();
