import fs from 'node:fs'
import path from 'node:path'
import { fontStyleBlock } from './excalidraw/font-style-block.js'

// Each inlined SVG carries internal IDs (masks, clipPaths, gradients) derived
// from stable Excalidraw element ids — so multiple stages of the same diagram
// on one Marp page produce colliding ids. url(#id) resolves to the first
// match in the document, making masks reference the wrong slide's geometry.
// Prefix every id and url(#...) reference with a per-instance counter.
export function uniquifyIds(svg, prefix) {
  return svg
    .replace(/(?<![a-zA-Z0-9_-])id="([^"]+)"/g, `id="${prefix}-$1"`)
    .replace(/\burl\(#([^)]+)\)/g, `url(#${prefix}-$1)`)
    .replace(/\bhref="#([^"]+)"/g, `href="#${prefix}-$1"`)
}

export function inlineSvg(md) {
  const defaultImage = md.renderer.rules.image
  let counter = 0

  md.renderer.rules.image = (tokens, idx, options, env, self) => {
    const fallthrough = () => defaultImage
      ? defaultImage(tokens, idx, options, env, self)
      : self.renderToken(tokens, idx, options)

    const token = tokens[idx]
    const src = token.attrGet('src')

    if (!src?.endsWith('.svg')) return fallthrough()

    // marp-cli does not pass the source markdown path to plugins, so we
    // resolve relative to process.cwd(). Authors are expected to run marp
    // from the markdown's directory (see the bin/slides wrapper pattern).
    const svgPath = path.resolve(process.cwd(), src)
    let svg
    try {
      svg = fs.readFileSync(svgPath, 'utf8')
    } catch {
      return fallthrough()
    }

    // Wrap in a block-level <div> so the HTML parser pops it out of the
    // <p> that markdown-it adds around images; the div is then a direct
    // flex child of <section> and can claim the remaining slide height.
    // Strip the SVG-native width/height — viewBox preserves aspect ratio.
    svg = svg.replace(/<svg([^>]*?)\s+width="[^"]*"/, '<svg$1')
             .replace(/<svg([^>]*?)\s+height="[^"]*"/, '<svg$1')
    svg = uniquifyIds(svg, `s${counter++}`)
    return `<div class="excalidraw-stage">${svg}</div>`
  }

  // Append the @font-face <style> block after all rendered slides. Injecting
  // it as a token would turn into its own slide under headingDivider, so we
  // wrap the renderer instead — the block lands at the end of <body>.
  const defaultRender = md.renderer.render.bind(md.renderer)
  md.renderer.render = (tokens, options, env) =>
    defaultRender(tokens, options, env) + fontStyleBlock() + '\n'
}
