const CODE_FENCE_RE = /```[\s\S]*?```|`[^`\n]+`/g;
const TAG_RE = /(?<!\w)(?<!&)#([\w][\w-]*(?:\/[\w][\w-]*)*)/g;

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

// Decode HTML entities that marked introduces before we re-escape for our own output.
// Order matters: &amp; must be last to avoid double-decoding.
function decodeHtmlEntities(s: string): string {
  return s
    .replace(/&quot;/g, '"')
    .replace(/&#39;/g, "'")
    .replace(/&lt;/g, "<")
    .replace(/&gt;/g, ">")
    .replace(/&amp;/g, "&");
}

export function extractTags(body: string): string[] {
  const cleaned = body.replace(CODE_FENCE_RE, "");
  const tags = new Set<string>();
  let match;
  while ((match = TAG_RE.exec(cleaned)) !== null) {
    tags.add(match[1]);
  }
  TAG_RE.lastIndex = 0;
  return [...tags].sort();
}

export function linkifyTags(html: string): string {
  // Split on code/pre blocks to avoid linkifying inside them
  const parts = html.split(/(<code[\s\S]*?<\/code>|<pre[\s\S]*?<\/pre>)/gi);
  return parts
    .map((part, i) => {
      // Odd indices are code/pre blocks, skip them
      if (i % 2 === 1) return part;
      return part.replace(TAG_RE, (full, tag) => {
        const encoded = encodeURIComponent(tag);
        return `<a class="inline-tag" data-tag="${escapeHtmlAttr(tag)}" href="/notes?tag=${encoded}">${full}</a>`;
      });
    })
    .join("");
}

const WIKILINK_RE = /\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g;

export function linkifyWikilinks(html: string): string {
  const parts = html.split(/(<code[\s\S]*?<\/code>|<pre[\s\S]*?<\/pre>)/gi);
  return parts
    .map((part, i) => {
      if (i % 2 === 1) return part;
      return part.replace(WIKILINK_RE, (_full, title: string, display?: string) => {
        const trimmed = decodeHtmlEntities(title.trim());
        const label = display ? decodeHtmlEntities(display) : trimmed;
        const encoded = encodeURIComponent(trimmed);
        return `<a class="wikilink" data-title="${escapeHtmlAttr(trimmed)}" href="/notes/by-title?title=${encoded}">${escapeHtmlAttr(label)}</a>`;
      });
    })
    .join("");
}
