#!/bin/zsh
#
# md2pdf — convert Markdown files to PDF.
#
# Pipeline: pandoc (md -> self-contained styled HTML) -> Chrome headless
# (HTML -> PDF). Uses only pandoc + Google Chrome, both assumed installed.
#
# Usage:
#   md2pdf FILE.md [FILE2.md ...]        convert each; PDF written next to source
#   md2pdf FILE.md -o OUT.pdf            explicit output (single input only)
#   md2pdf FILE.md --css STYLE.css       use a custom stylesheet
#   md2pdf -h | --help
#
# Output defaults to the same directory and basename as the input
# (report.md -> report.pdf). An existing PDF is overwritten.

emulate -L zsh
set -o pipefail

prog=${0:t}

usage() {
  cat <<EOF
$prog — convert Markdown files to PDF

Usage:
  $prog FILE.md [FILE2.md ...]     Convert each file; PDF written beside the source
  $prog FILE.md -o OUT.pdf         Write to an explicit path (single input only)
  $prog FILE.md --css STYLE.css    Render with a custom stylesheet
  $prog -h | --help                Show this help

Notes:
  - Output defaults to the source directory with a .pdf extension
    (e.g. notes.md -> notes.pdf) and overwrites any existing PDF.
  - Requires pandoc and Google Chrome.
EOF
}

die() { print -u2 "$prog: $*"; exit 1; }

# --- locate Google Chrome ---------------------------------------------------
find_chrome() {
  local candidates=(
    "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
    "$HOME/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
    "/Applications/Chromium.app/Contents/MacOS/Chromium"
  )
  local c
  for c in $candidates; do
    [[ -x $c ]] && { print -r -- "$c"; return 0; }
  done
  # Fall back to Spotlight lookup by bundle id.
  local app
  app=$(mdfind "kMDItemCFBundleIdentifier == 'com.google.Chrome'" 2>/dev/null | head -1)
  if [[ -n $app && -x "$app/Contents/MacOS/Google Chrome" ]]; then
    print -r -- "$app/Contents/MacOS/Google Chrome"
    return 0
  fi
  return 1
}

# --- parse arguments --------------------------------------------------------
typeset -a inputs
local out="" css=""

while (( $# )); do
  case $1 in
    -h|--help) usage; exit 0 ;;
    -o|--output)
      [[ -n $2 ]] || die "option $1 requires a path"
      out=$2; shift 2 ;;
    --output=*) out=${1#*=}; shift ;;
    --css)
      [[ -n $2 ]] || die "option $1 requires a path"
      css=$2; shift 2 ;;
    --css=*) css=${1#*=}; shift ;;
    --) shift; inputs+=("$@"); break ;;
    -*) die "unknown option: $1 (try --help)" ;;
    *) inputs+=("$1"); shift ;;
  esac
done

(( ${#inputs} )) || { usage; exit 1; }
[[ -n $out && ${#inputs} -gt 1 ]] && die "-o/--output cannot be used with multiple input files"

# --- resolve dependencies once ----------------------------------------------
command -v pandoc >/dev/null 2>&1 || die "pandoc not found (install with: brew install pandoc)"

local chrome
chrome=$(find_chrome) || die "Google Chrome not found — install it or use a Chromium build"

# Default stylesheet lives next to the real script (resolve through symlinks).
local default_css="${0:A:h}/md2pdf.css"
if [[ -n $css ]]; then
  [[ -f $css ]] || die "stylesheet not found: $css"
elif [[ -f $default_css ]]; then
  css=$default_css
fi

# --- per-run temp workspace, cleaned on exit --------------------------------
local workdir
workdir=$(mktemp -d "${TMPDIR:-/tmp}/md2pdf.XXXXXX") || die "could not create temp dir"
trap 'rm -rf -- "$workdir"' EXIT INT TERM

# --- convert one file -------------------------------------------------------
convert_one() {
  local in=$1 dest=$2
  [[ -f $in ]] || { print -u2 "✗ $in: no such file"; return 1; }

  local html="$workdir/${in:t:r}.html"
  # --standalone gives a full HTML doc (head + linked CSS); an empty title
  # metadata suppresses pandoc's "please specify a title" warning without
  # emitting a visible title block above the document's own H1.
  local -a pandoc_args=(
    "$in" -f markdown -t html5 --standalone --embed-resources
    --metadata "title=" -o "$html"
  )
  [[ -n $css ]] && pandoc_args+=(--css "$css")

  if ! pandoc $pandoc_args 2>"$workdir/pandoc.err"; then
    print -u2 "✗ $in: pandoc failed"
    [[ -s "$workdir/pandoc.err" ]] && print -u2 -- "$(<"$workdir/pandoc.err")"
    return 1
  fi

  # Chrome's headless PDF mode runs an independent instance and does not
  # disturb a running Chrome profile, so no --user-data-dir is needed. (An
  # isolated --user-data-dir actually makes headless Chrome hang on exit.)
  if ! "$chrome" --headless=new --disable-gpu --no-pdf-header-footer \
        --print-to-pdf="$dest" "file://$html" >/dev/null 2>"$workdir/chrome.err"; then
    print -u2 "✗ $in: Chrome failed to render PDF"
    [[ -s "$workdir/chrome.err" ]] && print -u2 -- "$(<"$workdir/chrome.err")"
    return 1
  fi

  [[ -s $dest ]] || { print -u2 "✗ $in: no PDF produced"; return 1; }
  print -- "✓ wrote ${dest}"
  return 0
}

# --- drive all inputs -------------------------------------------------------
local failures=0 f dest
for f in $inputs; do
  if [[ -n $out ]]; then
    dest=$out
  else
    dest="${f:h}/${f:t:r}.pdf"
  fi
  convert_one "$f" "$dest" || (( failures++ ))
done

(( failures == 0 )) || exit 1
