import {marked}from'marked';// Cache for manifest
let cachedManifest = null;
let manifestLoadPromise = null;
// Function to load content manifest
async function loadContentManifest() {
  if (cachedManifest) {
    return cachedManifest;
  }
  // If already loading, return the existing promise
  if (manifestLoadPromise) {
    return manifestLoadPromise;
  }
  // Check if we're in a browser environment (window.location is only available in browsers)
  const isBrowser = typeof window !== 'undefined' && typeof window.location !== 'undefined';
  if (!isBrowser) {
    // In Node.js/build-time environment, we can't fetch, so return null
    // The sync functions will work with empty data during build
    return null;
  }
  // Create a promise for loading the manifest
  manifestLoadPromise = (async () => {
    try {
      // Try multiple possible paths where the manifest might be served
      const baseUrl = window.location.origin;
      const possiblePaths = [`${baseUrl}/content/content-manifest.json`, '/content/content-manifest.json', './content/content-manifest.json', '../content/content-manifest.json'];
      for (const path of possiblePaths) {
        try {
          const response = await fetch(path);
          if (response.ok) {
            const manifest = await response.json();
            cachedManifest = manifest;
            console.log('✅ Successfully loaded content manifest from:', path);
            return manifest;
          } else {
            console.log(`⚠️ Manifest not found at ${path} (status: ${response.status})`);
          }
        } catch (fetchError) {
          // Try next path
          continue;
        }
      }
      console.error('❌ Could not load content manifest from any path. Tried:', possiblePaths);
      console.error('💡 Make sure content-manifest.json is in the public directory and accessible.');
      return null;
    } catch (error) {
      console.error('❌ Error loading content manifest:', error);
      return null;
    } finally {
      manifestLoadPromise = null;
    }
  })();
  return manifestLoadPromise;
}
// Function to get content path map dynamically
async function getContentPathMap() {
  const manifest = await loadContentManifest();
  if (manifest && manifest.contentPathMap) {
    return manifest.contentPathMap;
  }
  // Fallback to empty map if manifest not available
  return {};
}
// Function to get category metadata dynamically
async function getCategoryMetadata() {
  const manifest = await loadContentManifest();
  if (manifest && manifest.categories) {
    return manifest.categories;
  }
  // Fallback to empty categories if manifest not available
  console.warn('Category metadata not available - manifest may not be loaded');
  return {};
}
// Configure marked options
marked.setOptions({
  breaks: true,
  gfm: true
});
// Custom renderer to add IDs to headings
const renderer = new marked.Renderer();
const normalizeStaticAssetHref = href => {
  // Convert markdown-relative references like "../assets/img/x.png" into absolute "/assets/img/x.png"
  // so they resolve correctly when the article is served under routes like "/help/...".
  if (!href) return href;
  if (href.startsWith('/')) return href;
  // Remove leading ./ and ../ segments
  const cleaned = href.replace(/^(\.\/)+/, '').replace(/^(\.\.\/)+/, '');
  if (cleaned.startsWith('assets/')) return `/${cleaned}`;
  if (cleaned.startsWith('content/')) return `/${cleaned}`;
  return href;
};
renderer.heading = function ({
  tokens,
  depth,
  raw
}) {
  const safeTokens = tokens ?? [];
  // Extract text from tokens for ID generation
  const extractText = tokenArray => {
    if (!tokenArray || !Array.isArray(tokenArray)) return '';
    return tokenArray.map(token => {
      if (token.type === 'text') {
        return token.raw || token.text || '';
      }
      if (token.tokens) {
        return extractText(token.tokens);
      }
      return token.raw || '';
    }).join('');
  };
  // Render tokens to HTML
  const renderInline = tokenArray => {
    if (!tokenArray || !Array.isArray(tokenArray)) return '';
    return tokenArray.map(token => {
      if (token.type === 'text') {
        // Escape HTML for text tokens
        const text = token.raw || token.text || '';
        return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
      }
      // Handle other inline token types
      if (token.type === 'strong' && token.tokens) {
        return `<strong>${renderInline(token.tokens)}</strong>`;
      }
      if (token.type === 'em' && token.tokens) {
        return `<em>${renderInline(token.tokens)}</em>`;
      }
      if (token.type === 'code') {
        const code = token.raw || token.text || '';
        return `<code>${code.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')}</code>`;
      }
      if (token.tokens) {
        return renderInline(token.tokens);
      }
      return token.raw || '';
    }).join('');
  };
  const text = extractText(safeTokens) || (raw ?? '').trim();
  const renderedText = renderInline(safeTokens) || (raw ?? '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
  const id = text.toLowerCase().replace(/[^\w\s-]/g, '') // Remove special characters
  .replace(/\s+/g, '-') // Replace spaces with hyphens
  .replace(/-+/g, '-') // Replace multiple hyphens with single hyphen
  .trim();
  return `<h${depth} id="${id}">${renderedText}</h${depth}>`;
};
renderer.image = function ({
  href,
  title,
  text
}) {
  const src = href ? normalizeStaticAssetHref(href) : '';
  const safeAlt = (text || '').replaceAll('"', '&quot;');
  const titleAttr = title ? ` title="${title.replaceAll('"', '&quot;')}"` : '';
  return `<img src="${src}" alt="${safeAlt}"${titleAttr} />`;
};
// Code block renderer with copy button
renderer.code = function ({
  text,
  lang
}) {
  // Escape HTML in code content
  const escapeHtml = str => {
    return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
  };
  const escapedCode = escapeHtml(text);
  const language = lang || 'plaintext';
  `copy-btn-${Math.random().toString(36).substring(2, 11)}`;
  const codeBlockId = `code-block-${Math.random().toString(36).substring(2, 11)}`;
  return `
        <div class="code-block-wrapper bg-background text-primary" style="position: relative; margin: 1em 0;">
            <button 
                class="copy-code-btn" 
                data-code-id="${codeBlockId}"
                style="
                    position: absolute;
                    top: 0.5em;
                    right: 0.5em;
                    padding: 0.25em 0.75em;
                    border: 1px solid #d1d5db;
                    border-radius: 8px;
                    cursor: pointer;
                    font-size: 0.875em;
                    z-index: 10;
                    transition: all 0.2s;
                "
                onmouseover="this.style.background='#e5e7eb'"
                onmouseout="this.style.background='#f3f4f6'"
                onclick="
                    const codeEl = document.getElementById('${codeBlockId}');
                    if (codeEl) {
                        const text = codeEl.textContent || codeEl.innerText || '';
                        navigator.clipboard.writeText(text).then(() => {
                            const btn = event.target;
                            const originalText = btn.textContent;
                            btn.textContent = 'Copied!';
                            setTimeout(() => {
                                btn.textContent = originalText;
                            }, 2000);
                        }).catch(() => {
                            alert('Failed to copy code');
                        });
                    }
                "
            >
                Copy
            </button>
            <pre class="bg-background text-primary border border-border rounded-lg overflow-x-auto" style="margin: 0; padding: 1em; overflow-x: auto;">
                <code id="${codeBlockId}" class="language-${language}" style="display: block; font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace; font-size: 0.875em; line-height: 1.5;">${escapedCode}</code>
            </pre>
        </div>
    `;
};
marked.setOptions({
  renderer: renderer
});
// Function to extract headings from markdown content
const extractHeadings = markdownContent => {
  const headingRegex = /^(#{1,6})\s+(.+)$/gm;
  const headings = [];
  let match;
  while ((match = headingRegex.exec(markdownContent)) !== null) {
    const level = match[1].length; // Number of # characters
    const text = match[2].trim();
    const id = text.toLowerCase().replace(/[^\w\s-]/g, '') // Remove special characters
    .replace(/\s+/g, '-') // Replace spaces with hyphens
    .replace(/-+/g, '-') // Replace multiple hyphens with single hyphen
    .trim();
    headings.push({
      id,
      text,
      level
    });
  }
  return headings;
};
// Function to load markdown content dynamically
const loadMarkdownContent = async contentId => {
  try {
    // Get content path map dynamically from manifest
    const contentPathMap = await getContentPathMap();
    const filePath = contentPathMap[contentId];
    if (!filePath) {
      console.warn(`No markdown file found for content ID: ${contentId}`);
      return '';
    }
    // Try to load actual markdown file first, fallback to sample content
    try {
      const actualContent = await loadActualMarkdownFile(filePath);
      if (actualContent) {
        return actualContent;
      }
    } catch (error) {
      console.log(`Could not load actual markdown file for ${contentId}, using sample content`);
    }
    // Fallback to sample content for development
    return getSampleContent(contentId);
  } catch (error) {
    console.error(`Error loading markdown content for ${contentId}:`, error);
    return '';
  }
};
// Function to load actual markdown files
const loadActualMarkdownFile = async filePath => {
  try {
    // Fetch from the app server. The help server should serve the repo's `src/content`
    // directory at `/content`, so these paths resolve in the browser.
    const response = await fetch(filePath);
    if (!response.ok) return null;
    return await response.text();
  } catch (error) {
    console.error(`Error loading markdown file ${filePath}:`, error);
    return null;
  }
};
// Sample content function for development
const getSampleContent = contentId => {
  return `# ${contentId.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}

This is sample content for ${contentId}. The actual markdown file content would be loaded here in a production environment.

## Overview

This article covers the topic of ${contentId.replace(/-/g, ' ')} and provides comprehensive information about this subject.

## Key Points

- **Feature 1**: Description of the first key feature
- **Feature 2**: Description of the second key feature
- **Feature 3**: Description of the third key feature

## Getting Started

To get started with this topic:

1. **Step 1**: First step description
2. **Step 2**: Second step description
3. **Step 3**: Third step description

## Advanced Usage

For advanced users, here are some additional tips and techniques:

- **Tip 1**: Advanced tip description
- **Tip 2**: Another advanced tip
- **Tip 3**: Final advanced tip

## Support

If you need help with this topic, please refer to our support resources or contact our team.`;
};
// Category metadata - dynamically loaded from manifest
// This will be populated from the manifest at runtime
let categoryMetadata = {};
// Initialize category metadata from manifest
async function initializeCategoryMetadata() {
  if (Object.keys(categoryMetadata).length > 0) {
    return; // Already initialized
  }
  const metadata = await getCategoryMetadata();
  categoryMetadata = metadata;
}
// Legacy hardcoded categoryMetadata removed - now loaded dynamically from manifest
// Process categories to include full article content and HTML
// This is now async and loads from manifest dynamically
let processedCategories = [];
async function initializeProcessedCategories() {
  await initializeCategoryMetadata();
  processedCategories = await Promise.all(Object.values(categoryMetadata).map(async category => ({
    ...category,
    articles: await Promise.all(category.articles.map(async articleMeta => {
      try {
        const contentId = articleMeta.id;
        const rawContent = await loadMarkdownContent(contentId);
        const {
          frontmatter,
          body
        } = parseFrontmatter(rawContent);
        const markdownBody = body || rawContent;
        const htmlContent = await marked.parse(markdownBody);
        const headings = extractHeadings(markdownBody);
        return {
          ...articleMeta,
          title: frontmatter.title || articleMeta.title,
          description: frontmatter.description || articleMeta.description,
          author: frontmatter.author || frontmatter.authors || articleMeta.author,
          updatedAt: frontmatter.updatedAt || frontmatter.date || articleMeta.updatedAt,
          content: rawContent,
          htmlContent: htmlContent,
          headings: headings
        };
      } catch (err) {
        console.warn(`Could not load article ${articleMeta.id}, using metadata only:`, err);
        return {
          ...articleMeta,
          content: '',
          htmlContent: '',
          headings: []
        };
      }
    }))
  })));
}
// Initialize on module load (only in browser, skip during build)
// During build time, the sync functions will return empty arrays
// At runtime, the manifest will be loaded and categories will be populated
if (typeof window !== 'undefined') {
  // Wait a bit for the page to fully load before initializing
  if (document.readyState === 'loading') {
    window.addEventListener('DOMContentLoaded', () => {
      initializeProcessedCategories().catch(error => {
        console.error('Error initializing processed categories:', error);
      });
    });
  } else {
    // DOM already loaded, initialize immediately
    initializeProcessedCategories().catch(error => {
      console.error('Error initializing processed categories:', error);
    });
  }
}
// Helper functions
const getAllCategoriesMetadata = async () => {
  if (processedCategories.length === 0) {
    await initializeProcessedCategories();
  }
  return processedCategories;
};
// Synchronous version for backward compatibility (returns empty array if not initialized)
const getAllCategoriesMetadataSync = () => {
  return processedCategories;
};
const getCategoryById = async categoryId => {
  const categories = await getAllCategoriesMetadata();
  return categories.find(cat => cat.id === categoryId) || null;
};
const getCategoryByIdSync = categoryId => {
  return processedCategories.find(cat => cat.id === categoryId) || null;
};
const getArticleBySlug = async articleSlug => {
  const categories = await getAllCategoriesMetadata();
  for (const category of categories) {
    const article = category.articles.find(art => art.slug === articleSlug);
    if (article) return article;
  }
  return null;
};
// Function to parse frontmatter from markdown content
function parseFrontmatter(content) {
  const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
  const match = content.match(frontmatterRegex);
  if (!match) {
    return {
      frontmatter: {},
      body: content
    };
  }
  const frontmatterText = match[1];
  const body = match[2];
  const frontmatter = {};
  frontmatterText.split('\n').forEach(line => {
    const colonIndex = line.indexOf(':');
    if (colonIndex > 0) {
      const key = line.substring(0, colonIndex).trim();
      let value = line.substring(colonIndex + 1).trim();
      // Remove quotes if present
      if (typeof value === 'string' && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) {
        value = value.slice(1, -1);
      }
      // Parse array values
      if (typeof value === 'string' && value.startsWith('[') && value.endsWith(']')) {
        value = value.slice(1, -1).split(',').map(item => item.trim().replace(/^["']|["']$/g, ''));
      }
      frontmatter[key] = value;
    }
  });
  return {
    frontmatter,
    body
  };
}
// Function to load article content dynamically (for help center articles)
const loadArticleContent = async articleSlug => {
  const article = await getArticleBySlug(articleSlug);
  if (!article) return null;
  // Load the actual markdown content
  const contentId = article.id;
  const rawContent = await loadMarkdownContent(contentId);
  const {
    frontmatter,
    body
  } = parseFrontmatter(rawContent);
  const markdownBody = body || rawContent;
  const htmlContent = await marked.parse(markdownBody);
  const headings = extractHeadings(markdownBody);
  return {
    ...article,
    // If metadata disagrees, prefer the markdown frontmatter (but never render it in the body)
    title: frontmatter.title || article.title,
    description: frontmatter.description || article.description,
    author: frontmatter.author || frontmatter.authors || article.author,
    updatedAt: frontmatter.updatedAt || frontmatter.date || article.updatedAt,
    content: rawContent,
    htmlContent: htmlContent,
    headings: headings
  };
};
// Search function to find articles matching a query
const searchArticles = async query => {
  if (!query || query.trim().length === 0) {
    return [];
  }
  const searchTerm = query.toLowerCase().trim();
  const results = [];
  const categories = await getAllCategoriesMetadata();
  // Safe string for matching (manifest may have empty or missing fields)
  const safeStr = s => (s ?? '').toString().toLowerCase();
  // Search through all categories and their articles
  for (const category of categories) {
    for (const article of category.articles) {
      const title = safeStr(article.title);
      const description = safeStr(article.description);
      const slug = safeStr(article.slug);
      const content = article.content != null ? String(article.content).toLowerCase() : '';
      const titleMatch = title.includes(searchTerm);
      const descriptionMatch = description.includes(searchTerm);
      const contentMatch = content.includes(searchTerm);
      const slugMatch = slug.includes(searchTerm);
      if (titleMatch || descriptionMatch || contentMatch || slugMatch) {
        results.push(article);
      }
    }
  }
  // Sort results by relevance (title matches first, then description, then content)
  results.sort((a, b) => {
    const aTitle = safeStr(a.title);
    const bTitle = safeStr(b.title);
    const aTitleMatch = aTitle.includes(searchTerm);
    const bTitleMatch = bTitle.includes(searchTerm);
    if (aTitleMatch && !bTitleMatch) return -1;
    if (!aTitleMatch && bTitleMatch) return 1;
    const aDescMatch = safeStr(a.description).includes(searchTerm);
    const bDescMatch = safeStr(b.description).includes(searchTerm);
    if (aDescMatch && !bDescMatch) return -1;
    if (!aDescMatch && bDescMatch) return 1;
    return 0;
  });
  return results;
};
const AUDIENCE_PREFIXES = {
  Hosts: 'docs/Hosts/',
  Guests: 'docs/Guests/',
  Neighbors: 'docs/Neighbors/',
  'Trust-Safety': 'docs/Trust-Safety/'
};
function formatSectionTitle(folderName) {
  return folderName.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(' ');
}
/**
 * Returns sections (title + link list) for a given audience (Hosts, Guests, Neighbors, Trust-Safety)
 * using the content manifest. Link names and URLs come from the manifest (title + /help/docs/{slug}).
 */
const getHelpSectionsByAudience = async audience => {
  const manifest = await loadContentManifest();
  if (!manifest?.files?.length) return [];
  const prefix = AUDIENCE_PREFIXES[audience];
  if (!prefix) return [];
  const sectionMap = new Map();
  for (const file of manifest.files) {
    const path = file.relativePath?.replace(/\\/g, '/') || '';
    if (!path.startsWith(prefix)) continue;
    const parts = path.replace(/\.md$/i, '').split('/');
    // parts[0]=docs, parts[1]=Hosts|Guests|..., parts[2]=section folder, parts[3+]=article file
    const sectionKey = parts.length > 2 ? parts[2] : '';
    const linkUrl = `/help/docs/${file.slug}`;
    const linkName = file.title || parts[parts.length - 1] || file.slug;
    if (!sectionMap.has(sectionKey)) {
      sectionMap.set(sectionKey, []);
    }
    sectionMap.get(sectionKey).push({
      linkName,
      linkUrl
    });
  }
  const orderedKeys = Array.from(sectionMap.keys()).filter(k => (sectionMap.get(k)?.length ?? 0) > 0).sort();
  return orderedKeys.map(sectionKey => ({
    title: sectionKey ? formatSectionTitle(sectionKey) : audience,
    titleUrl: '',
    dataBody: sectionMap.get(sectionKey)
  }));
};export{getAllCategoriesMetadata,getAllCategoriesMetadataSync,getArticleBySlug,getCategoryById,getCategoryByIdSync,getHelpSectionsByAudience,loadArticleContent,searchArticles};//# sourceMappingURL=markdownLoader.js.map
