#!/usr/bin/env bash
#
# meetings — audio → transcript → summary + action items, all local
#
#   Transcribes audio using whisper.cpp or parakeet.cpp (GGUF/GGML models),
#   then uses Ollama to summarize and extract action items.
#
#   Usage:
#     ./meetings <audio_file>          # full pipeline
#     ./meetings setup                 # install deps + download model
#     ./meetings doctor                # check dependencies
#     ./meetings config                # show current config
#
set -euo pipefail

# ─── colours ────────────────────────────────────────────────────────
RED='\033[0;31m'; GRN='\033[0;32m'; YEL='\033[1;33m'
BLU='\033[0;34m'; CYN='\033[0;36m'; RST='\033[0m'
BOLD='\033[1m'

# ─── paths ──────────────────────────────────────────────────────────
MEETINGS_DIR="${MEETINGS_DIR:-$HOME/.meetings}"
CONFIG_FILE="$MEETINGS_DIR/config"
MODELS_DIR="$MEETINGS_DIR/models"

# ─── helpers ───────────────────────────────────────────────────────
die()   { printf "${RED}error: %s${RST}\n" "$*" >&2; exit 1; }
info()  { printf "${BLU}▸ %s${RST}\n" "$*" >&2; }
ok()    { printf "${GRN}✓ %s${RST}\n" "$*" >&2; }
warn()  { printf "${YEL}⚠ %s${RST}\n" "$*" >&2; }
step()  { printf "\n${BOLD}${CYN}── %s ──${RST}\n" "$*" >&2; }

# Format seconds into human-readable time (e.g., "1m 23s" or "45s")
format_time() {
  local secs="$1" minutes seconds
  minutes=$((secs / 60))
  seconds=$((secs % 60))
  if [[ $minutes -gt 0 ]]; then
    printf "%dm %ds" "$minutes" "$seconds"
  else
    printf "%ds" "$seconds"
  fi
}

banner() {
  cat <<'BAN'

  ┌─────────────────────────────────────────────┐
  │          🎤  M E E T I N G S               │
  │   audio → transcript → summary + actions   │
  │       whisper.cpp · parakeet.cpp · ollama   │
  └─────────────────────────────────────────────┘

BAN
}

# ─── load config: env vars > config file > defaults ────────────────
load_config() {
  # defaults
  STT_ENGINE=""
  STT_MODEL=""
  OLLAMA_MODEL="llama3.1:8b"
  OLLAMA_HOST="http://localhost:11434"
  THREADS="4"
  LANGUAGE="en"
  OUTPUT_DIR="."
  # config file overrides defaults
  [[ -f "$CONFIG_FILE" ]] && source "$CONFIG_FILE"
  # env vars override everything (|| true prevents set -e exit on empty vars)
  [[ -n "${MEETINGS_STT:-}" ]]        && STT_ENGINE="$MEETINGS_STT" || true
  [[ -n "${MEETINGS_STT_MODEL:-}" ]]  && STT_MODEL="$MEETINGS_STT_MODEL" || true
  [[ -n "${MEETINGS_LLM:-}" ]]       && OLLAMA_MODEL="$MEETINGS_LLM" || true
  [[ -n "${MEETINGS_THREADS:-}" ]]   && THREADS="$MEETINGS_THREADS" || true
  [[ -n "${MEETINGS_LANG:-}" ]]       && LANGUAGE="$MEETINGS_LANG" || true
  [[ -n "${MEETINGS_OUTPUT:-}" ]]    && OUTPUT_DIR="$MEETINGS_OUTPUT" || true
}

# ─── detect STT engine ──────────────────────────────────────────────
detect_stt() {
  if command -v parakeet-cli &>/dev/null; then
    echo "parakeet"
  elif command -v whisper-cli &>/dev/null; then
    echo "whisper"
  else
    echo ""
  fi
}

get_stt_binary() {
  local engine="${1:-$STT_ENGINE}"
  case "$engine" in
    whisper)  command -v whisper-cli 2>/dev/null || echo "" ;;
    parakeet) command -v parakeet-cli 2>/dev/null || echo "" ;;
    *)        echo "" ;;
  esac
}

find_stt_model() {
  local engine="${1:-whisper}"
  if [[ -n "${STT_MODEL:-}" && -f "${STT_MODEL}" ]]; then
    echo "$STT_MODEL"; return 0
  fi
  case "$engine" in
    whisper)
      for f in "$MODELS_DIR"/ggml-*.bin "$MODELS_DIR"/ggml-*.gguf; do
        [[ -f "$f" ]] && echo "$f" && return 0
      done
      ;;
    parakeet)
      for f in "$MODELS_DIR"/*parakeet*.gguf "$MODELS_DIR"/*tdt*.gguf; do
        [[ -f "$f" ]] && echo "$f" && return 0
      done
      ;;
  esac
  return 1
}

# ─── doctor ─────────────────────────────────────────────────────────
cmd_doctor() {
  banner >&2
  local ok_count=0 total=0

  total=$((total+1))
  if command -v ffmpeg &>/dev/null; then
    ok "ffmpeg: $(command -v ffmpeg)"; ok_count=$((ok_count+1))
  else
    warn "ffmpeg: not found"
  fi

  total=$((total+1))
  if command -v ollama &>/dev/null; then
    ok "ollama: $(command -v ollama)"; ok_count=$((ok_count+1))
    if curl -sf "${OLLAMA_HOST:-http://localhost:11434}/api/tags" &>/dev/null; then
      ok "  server: running at ${OLLAMA_HOST:-http://localhost:11434}"
    else
      warn "  server: not responding (run: ollama serve)"
    fi
  else
    warn "ollama: not found"
  fi

  total=$((total+1))
  local engine="${STT_ENGINE:-$(detect_stt)}"
  local bin
  bin="$(get_stt_binary "$engine")"
  if [[ -n "$bin" ]]; then
    ok "STT engine: $engine ($bin)"; ok_count=$((ok_count+1))
  else
    warn "STT engine: not found (run: ./meetings setup)"
  fi

  total=$((total+1))
  local model
  model="$(find_stt_model "$engine")" || true
  if [[ -n "$model" ]]; then
    ok "STT model:  $model"; ok_count=$((ok_count+1))
  else
    warn "STT model:  not found (run: ./meetings setup)"
  fi

  total=$((total+1))
  local ollama_model="${OLLAMA_MODEL:-llama3.1:8b}"
  local model_name
  model_name="$(echo "$ollama_model" | cut -d: -f1)"
  if ollama list 2>/dev/null | awk '{print $1}' | grep -qF "$model_name"; then
    ok "LLM model:  $ollama_model (pulled)"; ok_count=$((ok_count+1))
  else
    warn "LLM model:  $ollama_model (not pulled — run: ollama pull $ollama_model)"
  fi

  echo "" >&2
  if [[ $ok_count -eq $total ]]; then
    ok "All good ($ok_count/$total)"
  else
    warn "Ready ($ok_count/$total). Run './meetings setup' to install missing pieces."
  fi
}

# ─── setup ──────────────────────────────────────────────────────────
cmd_setup() {
  load_config
  banner >&2
  mkdir -p "$MEETINGS_DIR" "$MODELS_DIR"

  # ── ffmpeg ──
  step "Checking ffmpeg"
  if command -v ffmpeg &>/dev/null; then
    ok "ffmpeg already installed"
  else
    info "Installing ffmpeg..."
    if [[ "$(uname)" == "Darwin" ]]; then
      brew install ffmpeg || die "Could not install ffmpeg"
    elif command -v apt-get &>/dev/null; then
      sudo apt-get update && sudo apt-get install -y ffmpeg
    elif command -v dnf &>/dev/null; then
      sudo dnf install -y ffmpeg
    else
      die "Please install ffmpeg manually: https://ffmpeg.org/download.html"
    fi
  fi

  # ── ollama ──
  step "Checking Ollama"
  if command -v ollama &>/dev/null; then
    ok "ollama already installed"
  else
    info "Installing Ollama..."
    curl -fsSL https://ollama.com/install.sh | sh || die "Could not install Ollama"
  fi

  if ! curl -sf "${OLLAMA_HOST:-http://localhost:11434}/api/tags" &>/dev/null; then
    info "Starting Ollama server..."
    ollama serve &>/dev/null &
    sleep 3
  fi

  # ── choose STT engine ──
  step "Choosing STT engine"
  echo "" >&2
  echo "  1) whisper.cpp   — battle-tested, many languages, brew install" >&2
  echo "  2) parakeet.cpp  — faster, great English, smaller footprint" >&2
  echo "" >&2
  local choice
  if [[ -n "${STT_ENGINE:-}" ]]; then
    choice="$STT_ENGINE"
    info "Using pre-configured engine: $choice"
  else
    read -rp "  Choose [1/2, default=1]: " choice
    case "${choice:-1}" in
      2|parakeet) choice="parakeet" ;;
      *)          choice="whisper" ;;
    esac
  fi

  case "$choice" in
    whisper)  setup_whisper ;;
    parakeet) setup_parakeet ;;
  esac

  STT_ENGINE="$choice"
  grep -q "^STT_ENGINE=" "$CONFIG_FILE" 2>/dev/null \
    && sed -i '' "s/^STT_ENGINE=.*/STT_ENGINE=$choice/" "$CONFIG_FILE" 2>/dev/null \
    || echo "STT_ENGINE=$choice" >> "$CONFIG_FILE"

  # ── pull LLM ──
  step "Pulling LLM: ${OLLAMA_MODEL:-llama3.1:8b}"
  local ollama_model="${OLLAMA_MODEL:-llama3.1:8b}"
  if ollama list 2>/dev/null | awk '{print $1}' | grep -qF "$(echo "$ollama_model" | cut -d: -f1)"; then
    ok "$ollama_model already pulled"
  else
    ollama pull "$ollama_model" || warn "Could not pull $ollama_model. Run: ollama pull $ollama_model"
  fi

  echo "" >&2
  ok "Setup complete! Run './meetings doctor' to verify, then './meetings <audio_file>' to process."
}

setup_whisper() {
  step "Installing whisper.cpp"
  if command -v whisper-cli &>/dev/null; then
    ok "whisper-cli already available at $(command -v whisper-cli)"
  else
    info "Installing via package manager..."
    if [[ "$(uname)" == "Darwin" ]]; then
      brew install whisper-cpp || die "Could not install whisper-cpp via brew"
    else
      die "Please install whisper-cli manually: https://github.com/ggml-org/whisper.cpp
  On macOS: brew install whisper-cpp
  Or build from source: git clone https://github.com/ggml-org/whisper.cpp && cd whisper.cpp && cmake -B build && cmake --build build -j"
    fi
  fi

  step "Downloading Whisper model"
  local model_choice
  echo "" >&2
  echo "  Available models (from HuggingFace ggerganov/whisper.cpp):" >&2
  echo "    tiny.en         —  75 MB  (fastest, English only)" >&2
  echo "    base.en         — 142 MB  (good for quick tests)" >&2
  echo "    small.en        — 466 MB  (recommended for English) ★" >&2
  echo "    medium.en       — 1.5 GB  (high accuracy, English only)" >&2
  echo "    large-v3-turbo  — 809 MB  (best multilingual, fast)" >&2
  echo "    large-v3        — 2.9 GB  (best accuracy, any language)" >&2
  echo "" >&2
  if [[ -n "${STT_MODEL:-}" && -f "${STT_MODEL}" ]]; then
    info "Using existing model: $STT_MODEL"
  else
    read -rp "  Choose model [default=small.en]: " model_choice
    model_choice="${model_choice:-small.en}"
    local model_file="$MODELS_DIR/ggml-${model_choice}.bin"
    if [[ -f "$model_file" ]]; then
      ok "Model already downloaded: $model_file"
    else
      info "Downloading ggml-${model_choice}.bin (this may take a moment)..."
      curl -L --progress-bar \
        -o "$model_file" \
        "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-${model_choice}.bin" \
        || die "Failed to download model"
      ok "Downloaded: $model_file"
    fi
    STT_MODEL="$model_file"
    echo "STT_MODEL=$model_file" >> "$CONFIG_FILE"
  fi
}

setup_parakeet() {
  step "Installing parakeet.cpp"
  if command -v parakeet-cli &>/dev/null; then
    ok "parakeet-cli already available at $(command -v parakeet-cli)"
  else
    info "Checking for pre-built binary or Docker..."
    if command -v docker &>/dev/null; then
      ok "Docker available — parakeet.cpp will run via container"
    else
      die "parakeet-cli not found and Docker not installed. Please either:
  - Build parakeet.cpp: https://github.com/mudler/parakeet.cpp
  - Or install Docker for the container approach"
    fi
  fi

  step "Downloading Parakeet model"
  local model_choice
  echo "" >&2
  echo "  Available models (from HuggingFace mudler/parakeet-cpp-gguf):" >&2
  echo "    tdt_ctc-110m-q8_0 — 178 MB  (fast, good English, smallest) ★" >&2
  echo "    tdt_ctc-110m-f16  — 268 MB  (fast, lossless English)" >&2
  echo "    tdt-0.6b-v3-f16   — 1.4 GB  (multilingual, recommended)" >&2
  echo "" >&2
  if [[ -n "${STT_MODEL:-}" && -f "${STT_MODEL}" ]]; then
    info "Using existing model: $STT_MODEL"
  else
    read -rp "  Choose model [default=tdt_ctc-110m-q8_0]: " model_choice
    model_choice="${model_choice:-tdt_ctc-110m-q8_0}"
    local model_file="$MODELS_DIR/${model_choice}.gguf"
    if [[ -f "$model_file" ]]; then
      ok "Model already downloaded: $model_file"
    else
      info "Downloading ${model_choice}.gguf..."
      curl -L --progress-bar \
        -o "$model_file" \
        "https://huggingface.co/mudler/parakeet-cpp-gguf/resolve/main/${model_choice}.gguf" \
        || die "Failed to download model"
      ok "Downloaded: $model_file"
    fi
    STT_MODEL="$model_file"
    echo "STT_MODEL=$model_file" >> "$CONFIG_FILE"
  fi
}

# ─── config display ─────────────────────────────────────────────────
cmd_config() {
  load_config
  banner >&2
  echo "" >&2
  if [[ -f "$CONFIG_FILE" ]]; then
    echo "  Saved config ($CONFIG_FILE):" >&2
    echo "" >&2
    while IFS='=' read -r key val; do
      printf "    %-16s %s\n" "$key" "$val" >&2
    done < "$CONFIG_FILE"
  else
    echo "  (no saved config — run: ./meetings setup)" >&2
  fi
  echo "" >&2
  echo "  Effective settings:" >&2
  local stt_display="${STT_ENGINE:-$(detect_stt)}"
  stt_display="${stt_display:-(not set)}"
  echo "    STT engine:  $stt_display" >&2
  echo "    STT model:   ${STT_MODEL:-(auto-detect)}" >&2
  echo "    LLM model:   $OLLAMA_MODEL" >&2
  echo "    Ollama host:  $OLLAMA_HOST" >&2
  echo "    Threads:     $THREADS" >&2
  echo "    Language:    $LANGUAGE" >&2
  echo "    Output dir:  $OUTPUT_DIR" >&2
  echo "" >&2
}

# ─── convert audio ──────────────────────────────────────────────────
convert_audio() {
  local input="$1" output="$2"
  info "Converting audio to 16kHz mono WAV..."
  ffmpeg -y -i "$input" -ar 16000 -ac 1 -c:a pcm_s16le "$output" \
    -loglevel warning 2>/dev/null \
    || die "ffmpeg conversion failed for $input"
  ok "Audio converted: $(du -h "$output" | cut -f1)"
}

# ─── transcribe with whisper ───────────────────────────────────────
transcribe_whisper() {
  local wav_file="$1" model="$2" binary="$3"
  info "Transcribing with whisper.cpp..."
  local lang_flag=""
  [[ "$LANGUAGE" != "auto" ]] && lang_flag="-l $LANGUAGE"

  local transcript
  transcript=$("$binary" \
    -m "$model" \
    -f "$wav_file" \
    -t "$THREADS" \
    $lang_flag \
    -nt \
    --no-prints \
    2>/dev/null) || die "Whisper transcription failed"

  echo "$transcript"
}

# ─── transcribe with parakeet ──────────────────────────────────────
transcribe_parakeet() {
  local wav_file="$1" model="$2" binary="$3"
  info "Transcribing with parakeet.cpp..."

  local decoder_flag=""
  if echo "$model" | grep -qi "ctc"; then
    decoder_flag="--decoder ctc"
  elif echo "$model" | grep -qi "tdt"; then
    decoder_flag="--decoder tdt"
  fi

  if [[ -z "$binary" ]] && command -v docker &>/dev/null; then
    info "Using Docker for parakeet.cpp..."
    docker run --rm \
      -v "$model:/model.gguf:ro" \
      -v "$wav_file:/audio.wav:ro" \
      ghcr.io/mudler/parakeet.cpp-cli:latest \
      transcribe --model /model.gguf --input /audio.wav $decoder_flag 2>/dev/null \
      || die "Docker parakeet transcription failed"
  else
    "$binary" transcribe --model "$model" --input "$wav_file" $decoder_flag 2>/dev/null \
      || die "Parakeet transcription failed"
  fi
}

# ─── call Ollama ───────────────────────────────────────────────────
ollama_generate() {
  local prompt="$1" system="${2:-}"
  local json_payload

  if [[ -n "$system" ]]; then
    json_payload=$(jq -n \
      --arg model "$OLLAMA_MODEL" \
      --arg system "$system" \
      --arg prompt "$prompt" \
      '{model: $model, system: $system, prompt: $prompt, stream: false}')
  else
    json_payload=$(jq -n \
      --arg model "$OLLAMA_MODEL" \
      --arg prompt "$prompt" \
      '{model: $model, prompt: $prompt, stream: false}')
  fi

  curl -sf "$OLLAMA_HOST/api/generate" \
    -d "$json_payload" 2>/dev/null \
    | jq -r '.response // empty' \
    || die "Ollama request failed. Is the server running? (ollama serve)"
}

# ─── main pipeline ──────────────────────────────────────────────────
cmd_process() {
  local input_file="$1"
  shift || true

  # Load config BEFORE flag parsing, so flags can override
  load_config

  # Start timer
  local start_time
  start_time=$(date +%s)

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --stt)      STT_ENGINE="$2"; shift 2 ;;
      --model)    STT_MODEL="$2"; shift 2 ;;
      --llm)      OLLAMA_MODEL="$2"; shift 2 ;;
      --lang)     LANGUAGE="$2"; shift 2 ;;
      --threads)  THREADS="$2"; shift 2 ;;
      --output)   OUTPUT_DIR="$2"; shift 2 ;;
      *)          die "Unknown option: $1. Run: ./meetings help" ;;
    esac
  done

  [[ -z "$input_file" ]] && die "Usage: ./meetings <audio_file>"
  [[ ! -f "$input_file" ]] && die "File not found: $input_file"

  STT_ENGINE="${STT_ENGINE:-$(detect_stt)}"
  [[ -z "$STT_ENGINE" ]] && die "No STT engine found. Run: ./meetings setup"

  local binary
  binary="$(get_stt_binary "$STT_ENGINE")"
  [[ -z "$binary" ]] && die "Could not find $STT_ENGINE binary. Run: ./meetings setup"

  local model
  if [[ -n "${STT_MODEL:-}" ]] && [[ -f "$STT_MODEL" ]]; then
    model="$STT_MODEL"
  else
    model="$(find_stt_model "$STT_ENGINE")" \
      || die "No STT model found. Run: ./meetings setup"
  fi
  [[ ! -f "$model" ]] && die "Model file not found: $model"

  command -v ffmpeg &>/dev/null || die "ffmpeg not found. Install it first."
  curl -sf "$OLLAMA_HOST/api/tags" &>/dev/null \
    || die "Ollama server not responding at $OLLAMA_HOST. Run: ollama serve"

  # ── create output directory ──
  local basename
  basename="$(date +%Y-%m-%d_%H%M)_$(basename "${input_file%.*}" | tr ' ' '_')"
  local outdir="$OUTPUT_DIR/$basename"
  mkdir -p "$outdir"

  banner >&2
  echo "  Input:       $input_file" >&2
  echo "  STT engine:  $STT_ENGINE" >&2
  echo "  STT model:   $(basename "$model")" >&2
  echo "  LLM model:   $OLLAMA_MODEL" >&2
  echo "  Language:    $LANGUAGE" >&2
  echo "  Output:      $outdir/" >&2
  echo "" >&2

  # ── step 1: convert ──
  step "Step 1/4 — Converting audio"
  local step_start step_end step_elapsed
  step_start=$(date +%s)
  local wav_file="$outdir/audio_16k.wav"
  convert_audio "$input_file" "$wav_file"
  step_end=$(date +%s)
  step_elapsed=$((step_end - step_start))
  local time1
  time1=$(format_time "$step_elapsed")
  ok "Completed in ${time1}"

  # ── step 2: transcribe ──
  step "Step 2/4 — Transcribing with $STT_ENGINE"
  step_start=$(date +%s)
  local transcript
  case "$STT_ENGINE" in
    whisper)   transcript="$(transcribe_whisper "$wav_file" "$model" "$binary")" ;;
    parakeet)  transcript="$(transcribe_parakeet "$wav_file" "$model" "$binary")" ;;
    *)         die "Unknown engine: $STT_ENGINE" ;;
  esac

  if [[ -z "$transcript" || -z "$(echo "$transcript" | tr -d '[:space:]')" ]]; then
    die "Transcription produced empty output"
  fi

  echo "$transcript" > "$outdir/transcript.txt"
  local word_count
  word_count=$(echo "$transcript" | wc -w | tr -d ' ')
  ok "Transcript: $word_count words"
  step_end=$(date +%s)
  step_elapsed=$((step_end - step_start))
  local time2
  time2=$(format_time "$step_elapsed")
  ok "Completed in ${time2}"

  # ── step 3: summarize ──
  step "Step 3/4 — Summarizing ($OLLAMA_MODEL)"
  step_start=$(date +%s)
  local summarize_prompt
  summarize_prompt="Please provide a clear, concise summary of the following meeting transcript.
Structure the summary with:
- **Topic**: What the meeting was about
- **Key Points**: Main topics discussed (3-5 bullet points)
- **Decisions**: Any decisions that were made
- **Open Questions**: Things left unresolved

TRANSCRIPT:
$transcript"

  local summary
  summary="$(ollama_generate "$summarize_prompt" "You are an expert meeting summarizer. Be concise but thorough. Always use markdown formatting.")"
  echo "$summary" > "$outdir/summary.md"
  ok "Summary saved"
  step_end=$(date +%s)
  step_elapsed=$((step_end - step_start))
  local time3
  time3=$(format_time "$step_elapsed")
  ok "Completed in ${time3}"

  # ── step 4: action items ──
  step "Step 4/4 — Extracting action items ($OLLAMA_MODEL)"
  step_start=$(date +%s)
  local actions_prompt
  actions_prompt="Extract all action items, tasks, and commitments from the following meeting transcript.
For each action item, provide:
- **Who**: The person responsible (or 'Unassigned' if unclear)
- **What**: The specific task or action
- **When**: Any deadline mentioned (or 'No deadline specified')
- **Priority**: High / Medium / Low (based on urgency and context)

Be thorough — capture every commitment, follow-up, and task mentioned or implied.
Format as a numbered list.

TRANSCRIPT:
$transcript"

  local actions
  actions="$(ollama_generate "$actions_prompt" "You are an expert at extracting action items from meeting notes. Be thorough and specific. Always use markdown formatting.")"
  echo "$actions" > "$outdir/action_items.md"
  ok "Action items saved"
  step_end=$(date +%s)
  step_elapsed=$((step_end - step_start))
  local time4
  time4=$(format_time "$step_elapsed")
  ok "Completed in ${time4}"

  # ── combine report ──
  cat > "$outdir/report.md" <<REPORT
# Meeting Report

**Date:** $(date '+%B %d, %Y at %H:%M')
**Source:** $(basename "$input_file")
**STT:** $STT_ENGINE | **LLM:** $OLLAMA_MODEL

---

## Summary

$summary

---

## Action Items

$actions

---

## Full Transcript

$transcript
REPORT

  echo "" >&2
  ok "All done! Files saved to: $outdir/" >&2
  echo "" >&2
  echo "  📄 Report:        $outdir/report.md" >&2
  echo "  📝 Transcript:    $outdir/transcript.txt" >&2
  echo "  📋 Summary:       $outdir/summary.md" >&2
  echo "  ✅ Action Items:  $outdir/action_items.md" >&2
  echo "" >&2

  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
  echo "" >&2
  head -50 "$outdir/report.md" | tail -35 >&2
  echo "" >&2
  echo "(showing preview — full report at $outdir/report.md)" >&2
  
  # Display elapsed time breakdown
  local end_time elapsed minutes seconds
  end_time=$(date +%s)
  elapsed=$((end_time - start_time))
  minutes=$((elapsed / 60))
  seconds=$((elapsed % 60))
  echo "" >&2
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
  echo "⏱  Processing Time Breakdown:" >&2
  echo "   Step 1 (Convert audio):     ${time1:-N/A}" >&2
  echo "   Step 2 (Transcribe):        ${time2:-N/A}" >&2
  echo "   Step 3 (Summarize):         ${time3:-N/A}" >&2
  echo "   Step 4 (Action items):      ${time4:-N/A}" >&2
  echo "   ─────────────────────────────────────" >&2
  printf "   TOTAL:                    %dm %ds\n" "$minutes" "$seconds" >&2
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
}

# ─── entry point ────────────────────────────────────────────────────
case "${1:-help}" in
  setup)   shift; cmd_setup ;;
  doctor)  load_config; cmd_doctor ;;
  config)  cmd_config ;;
  help|--help|-h)
    banner >&2
    cat <<'HELP'
Usage:
  ./meetings <audio_file>              Run the full pipeline
  ./meetings setup                     Install deps + download model
  ./meetings doctor                    Check all dependencies
  ./meetings config                    Show current configuration

Options (override config):
  --stt <whisper|parakeet>             STT engine to use
  --model <path>                        Path to GGUF/GGML model file
  --llm <ollama_model>                 Ollama model for summarization
  --lang <code>                        Language code (default: en, auto for whisper)
  --threads <N>                        Number of threads for STT
  --output <dir>                       Output directory (default: .)

Environment variables:
  MEETINGS_DIR          Config & models directory (default: ~/.meetings)
  MEETINGS_STT          STT engine (whisper/parakeet)
  MEETINGS_STT_MODEL    Path to model file
  MEETINGS_LLM          Ollama model name (default: llama3.1:8b)
  MEETINGS_THREADS      Thread count (default: 4)
  MEETINGS_LANG         Language code (default: en)
  MEETINGS_OUTPUT       Output directory (default: .)

Examples:
  # First time — install everything
  ./meetings setup

  # Process a meeting recording
  ./meetings recording.mp3

  # Use a different LLM model
  ./meetings meeting.wav --llm llama3.1:8b

  # Use parakeet with multilingual model
  ./meetings call.wav --stt parakeet --lang auto

  # Specify output directory
  ./meetings interview.m4a --output ./reports
HELP
    ;;
  *)
    cmd_process "$@"
    ;;
esac