import { spawnSync } from 'node:child_process'
import { rmSync, mkdirSync, readFileSync, writeFileSync, existsSync, statSync } from 'node:fs'
import { resolve, dirname, relative } from 'node:path'
import { fileURLToPath } from 'node:url'

import { copyFilesFlat } from './assets.js'
import { processDir } from './excalidraw/stages.js'
import { buildConfig } from './config.js'
import { expandBuildMarkers } from './build-unroll.js'
import { renderNotes } from './render-notes.js'
import { splitFrontmatter, parseFrontmatter } from './frontmatter.js'
import { firstHeading } from './heading.js'
import { countSlides } from './slides.js'
import { injectOutline } from './inject-outline.js'
import { resolveSlug } from './resolve.js'
import { expandMorphSpans } from './morph-spans.js'
import { injectTransition } from './inject-transition.js'

const _pkgDir = dirname(dirname(fileURLToPath(import.meta.url)))

export function finalizeHtml(html, { root, expanded, outline }) {
  return injectTransition(
    injectOutline(
      renderNotes(html, expanded),
      outline
    ),
    { contentRoot: root }
  )
}

export async function composeItem(slug, _marpArgs = [], { root = process.cwd() } = {}) {
  const resolved = resolveSlug(slug, { root })
  const srcDir = dirname(resolved.path)
  const rel = relative(resolve(root, 'content'), srcDir)
  const destDir = resolve(root, 'decks', rel)
  const outPath = resolve(destDir, 'index.html')

  if (existsSync(outPath) && statSync(outPath).mtimeMs >= statSync(resolved.path).mtimeMs) {
    return 0
  }

  const marpBin = resolve(_pkgDir, 'node_modules/.bin/marp')
  const config = buildConfig(root)

  rmSync(destDir, { recursive: true, force: true })
  mkdirSync(destDir, { recursive: true })

  copyFilesFlat(srcDir, destDir)
  await processDir(destDir)

  const sourceMd = readFileSync(resolved.path, 'utf8')
  const expanded = expandMorphSpans(expandBuildMarkers(sourceMd, destDir))
  writeFileSync(resolve(destDir, 'index-built.md'), expanded)

  const { status } = spawnSync(marpBin, ['--no-stdin', '--config', config, '--output', 'index.html', 'index-built.md'], {
    cwd: destDir,
    stdio: 'inherit',
  })
  if (status !== 0) return status ?? 1

  const { frontmatter, body } = splitFrontmatter(expanded)
  const fm = parseFrontmatter(frontmatter)
  const title = fm.title ?? firstHeading(body) ?? slug
  const slideCount = countSlides(body)
  const outline = { slug, slide_count: slideCount, title }

  const html = readFileSync(outPath, 'utf8')
  writeFileSync(outPath, finalizeHtml(html, { root, expanded, outline }))
  process.stdout.write(`→ ${relative(root, outPath)}\n`)

  return 0
}
