#!/usr/bin/env bash
# optimize-images.sh — Image auditor, metadata stripper, and WebP optimizer for fosscat.com
#
# Usage:
#   ./scripts/optimize-images.sh              # Interactive mode
#   ./scripts/optimize-images.sh --dry-run    # Show what would happen without changing anything
#   ./scripts/optimize-images.sh --yes        # Skip all confirmation prompts
#   ./scripts/optimize-images.sh --audit-only # Only run the audit phase (no changes)

set -euo pipefail

# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------
IMAGES_DIR="static/images"
CONTENT_DIR="content"
CONFIG_FILE="config.toml"
MAX_WIDTH=2000
MAX_HEIGHT=2000
WEBP_QUALITY=82

# ---------------------------------------------------------------------------
# CLI flags
# ---------------------------------------------------------------------------
DRY_RUN=false
AUTO_YES=false
AUDIT_ONLY=false

for arg in "$@"; do
  case "$arg" in
    --dry-run)   DRY_RUN=true ;;
    --yes|-y)    AUTO_YES=true ;;
    --audit-only) AUDIT_ONLY=true ;;
    --help|-h)
      echo "Usage: $0 [--dry-run] [--yes] [--audit-only]"
      echo ""
      echo "  --dry-run     Show what would happen without making changes"
      echo "  --yes, -y     Skip confirmation prompts"
      echo "  --audit-only  Only run the audit (no modifications)"
      echo "  --help, -h    Show this help"
      exit 0
      ;;
    *)
      echo "Unknown option: $arg"
      echo "Run $0 --help for usage"
      exit 1
      ;;
  esac
done

# ---------------------------------------------------------------------------
# Colors and formatting
# ---------------------------------------------------------------------------
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
DIM='\033[2m'
NC='\033[0m' # No Color

info()    { echo -e "${BLUE}[INFO]${NC} $*"; }
success() { echo -e "${GREEN}[OK]${NC} $*"; }
warn()    { echo -e "${YELLOW}[WARN]${NC} $*"; }
error()   { echo -e "${RED}[ERROR]${NC} $*"; }
header()  { echo -e "\n${BOLD}${CYAN}═══ $* ═══${NC}\n"; }

# ---------------------------------------------------------------------------
# Dependency checks
# ---------------------------------------------------------------------------
check_deps() {
  local missing=()
  for cmd in exiftool convert identify cwebp; do
    if ! command -v "$cmd" &>/dev/null; then
      missing+=("$cmd")
    fi
  done

  if [[ ${#missing[@]} -gt 0 ]]; then
    error "Missing required tools: ${missing[*]}"
    echo "  These are provided by the Nix dev shell. Run:"
    echo "    nix develop   # or let direnv load the flake"
    echo ""
    echo "  Required nix packages:"
    echo "    perl538Packages.ImageExifTool  (exiftool)"
    echo "    imagemagick                    (convert, identify)"
    echo "    libwebp                        (cwebp)"
    exit 1
  fi
}

# ---------------------------------------------------------------------------
# Ensure we're in the project root
# ---------------------------------------------------------------------------
if [[ ! -f "$CONFIG_FILE" ]] || [[ ! -d "$IMAGES_DIR" ]]; then
  error "Must be run from the project root (where $CONFIG_FILE and $IMAGES_DIR exist)"
  exit 1
fi

check_deps

# ---------------------------------------------------------------------------
# Utility: human-readable file size
# ---------------------------------------------------------------------------
human_size() {
  local bytes=$1
  if (( bytes >= 1048576 )); then
    local mb_whole=$(( bytes / 1048576 ))
    local mb_frac=$(( (bytes % 1048576) * 10 / 1048576 ))
    echo "${mb_whole}.${mb_frac} MB"
  elif (( bytes >= 1024 )); then
    echo "$(( bytes / 1024 )) KB"
  else
    echo "${bytes} B"
  fi
}

# ---------------------------------------------------------------------------
# Manifest: track which images have been optimized (skip re-processing)
# ---------------------------------------------------------------------------
MANIFEST_FILE=".image-manifest"

manifest_hash() {
  sha256sum "$1" | cut -d' ' -f1
}

# Look up a file's stored hash; prints it or empty string if not found
manifest_lookup() {
  local file="$1"
  if [[ -f "$MANIFEST_FILE" ]]; then
    grep -F "  $file" "$MANIFEST_FILE" 2>/dev/null | head -1 | awk '{print $1}' || true
  fi
}

# Update or add a file's hash in the manifest
manifest_update() {
  local file="$1"
  local hash
  hash=$(manifest_hash "$file")

  if [[ ! -f "$MANIFEST_FILE" ]]; then
    echo "# fosscat.com image optimization manifest — do not edit manually" > "$MANIFEST_FILE"
  fi

  # Remove existing entry for this file (if any)
  if grep -qF "  $file" "$MANIFEST_FILE" 2>/dev/null; then
    grep -vF "  $file" "$MANIFEST_FILE" > "${MANIFEST_FILE}.tmp"
    mv "${MANIFEST_FILE}.tmp" "$MANIFEST_FILE"
  fi

  # Append new entry
  echo "$hash  $file" >> "$MANIFEST_FILE"
}

# Remove a file's entry from the manifest
manifest_remove() {
  local file="$1"
  if [[ -f "$MANIFEST_FILE" ]] && grep -qF "  $file" "$MANIFEST_FILE" 2>/dev/null; then
    grep -vF "  $file" "$MANIFEST_FILE" > "${MANIFEST_FILE}.tmp"
    mv "${MANIFEST_FILE}.tmp" "$MANIFEST_FILE"
  fi
}

# ---------------------------------------------------------------------------
# Utility: confirm prompt (respects --yes and --dry-run)
# ---------------------------------------------------------------------------
confirm() {
  local prompt="$1"
  if $AUTO_YES; then
    return 0
  fi
  if $DRY_RUN; then
    echo -e "  ${DIM}(dry-run: would ask) $prompt${NC}"
    return 0
  fi
  echo -en "  $prompt ${BOLD}[y/N]${NC} "
  read -r answer
  [[ "$answer" =~ ^[Yy]$ ]]
}

# ---------------------------------------------------------------------------
# PHASE 1: AUDIT
# ---------------------------------------------------------------------------
phase_audit() {
  header "PHASE 1: IMAGE AUDIT"

  # Collect all image files
  local -a image_files=()
  while IFS= read -r -d '' f; do
    image_files+=("$f")
  done < <(find "$IMAGES_DIR" -maxdepth 1 -type f \( -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' -o -iname '*.webp' -o -iname '*.gif' \) -print0 | sort -z)

  if [[ ${#image_files[@]} -eq 0 ]]; then
    warn "No images found in $IMAGES_DIR"
    return
  fi

  # --- Image inventory table ---
  echo -e "${BOLD}Image Inventory${NC}"
  printf "  %-40s  %-6s  %-12s  %s\n" "FILENAME" "FORMAT" "DIMENSIONS" "SIZE"
  printf "  %-40s  %-6s  %-12s  %s\n" "--------" "------" "----------" "----"

  local total_size=0
  for img in "${image_files[@]}"; do
    local fname
    fname=$(basename "$img")
    local ext="${fname##*.}"
    local fsize
    fsize=$(stat -c%s "$img" 2>/dev/null || stat -f%z "$img" 2>/dev/null)
    total_size=$((total_size + fsize))
    local dims
    dims=$(identify -format "%wx%h" "$img" 2>/dev/null || echo "unknown")
    printf "  %-40s  %-6s  %-12s  %s\n" "$fname" "$ext" "$dims" "$(human_size "$fsize")"
  done

  echo ""
  info "Total: ${#image_files[@]} images, $(human_size $total_size)"

  # --- EXIF / Metadata scan ---
  echo ""
  echo -e "${BOLD}Metadata / Privacy Scan${NC}"

  local privacy_issues=0
  # Sensitive tag names to check (extracted in a single exiftool call per image)
  local sensitive_tag_args=(
    -GPSLatitude -GPSLongitude -GPSPosition
    -SerialNumber -CameraSerialNumber -BodySerialNumber -LensSerialNumber
    -OwnerName -Artist -Copyright -Creator -Rights
    -By-line -Contact
    -Make -Model -LensModel -Software
    -DateTime -DateTimeOriginal -CreateDate
    -CreatorTool -ImageDescription -UserComment
  )

  for img in "${image_files[@]}"; do
    local fname
    fname=$(basename "$img")
    local has_metadata=false
    local metadata_lines=()

    # Single exiftool call to extract all sensitive tags at once
    local exif_output
    exif_output=$(exiftool -s -f "${sensitive_tag_args[@]}" "$img" 2>/dev/null || true)

    while IFS= read -r line; do
      [[ -z "$line" ]] && continue
      # exiftool -s output format: "TagName                         : value"
      local tagname value
      tagname=$(echo "$line" | sed 's/\s*:.*//' | xargs)
      value=$(echo "$line" | sed 's/^[^:]*:\s*//')

      # Skip tags with no value (exiftool -f shows "-" for missing tags)
      [[ "$value" == "-" ]] && continue
      [[ -z "$value" ]] && continue

      has_metadata=true
      # Highlight GPS data in red
      if [[ "$tagname" == *GPS* ]] || [[ "$tagname" == *Latitude* ]] || [[ "$tagname" == *Longitude* ]]; then
        metadata_lines+=("${RED}!!${NC} $tagname: $value")
      elif [[ "$tagname" == *Serial* ]] || [[ "$tagname" == *Owner* ]] || [[ "$tagname" == *Artist* ]] || [[ "$tagname" == *Creator* ]]; then
        metadata_lines+=("${YELLOW}!${NC}  $tagname: $value")
      else
        metadata_lines+=("${DIM}-${NC}  $tagname: $value")
      fi
    done <<< "$exif_output"

    if $has_metadata; then
      privacy_issues=$((privacy_issues + 1))
      echo -e "  ${YELLOW}$fname${NC} — metadata found:"
      for line in "${metadata_lines[@]}"; do
        echo -e "      $line"
      done
    else
      echo -e "  ${GREEN}$fname${NC} — clean"
    fi
  done

  echo ""
  if [[ $privacy_issues -gt 0 ]]; then
    warn "$privacy_issues image(s) contain metadata that should be stripped"
  else
    success "All images are clean of sensitive metadata"
  fi

  # --- Cross-reference with content ---
  echo ""
  echo -e "${BOLD}Content Reference Check${NC}"

  # Collect all image references from content files
  local -a referenced_images=()
  local -a broken_refs=()
  local -a inconsistent_paths=()

  while IFS= read -r -d '' mdfile; do
    # Front matter image field (handles both `image: "..."` and `  image: "..."` under cover:)
    while IFS= read -r fm_image; do
      [[ -z "$fm_image" ]] && continue
      # Clean up: remove surrounding quotes and whitespace
      fm_image=$(echo "$fm_image" | sed 's/^[[:space:]]*image:[[:space:]]*//' | sed 's/^["'\'']//' | sed 's/["'\'']\s*$//')

      if [[ -n "$fm_image" ]] && [[ "$fm_image" != '""' ]] && [[ "$fm_image" != http* ]]; then
        # Normalize: Hugo serves /images/... from static/images/...
        local fs_path="static/${fm_image#/}"

        # Check if it's a broken reference
        if [[ ! -f "$fs_path" ]]; then
          broken_refs+=("$mdfile|$fm_image")
        else
          referenced_images+=("$fs_path")
        fi

        # Check for inconsistent path (missing leading /)
        if [[ "$fm_image" != /* ]]; then
          inconsistent_paths+=("$mdfile|$fm_image")
        fi
      fi
    done < <(grep -E '^\s*image:\s' "$mdfile" 2>/dev/null || true)

    # Inline markdown images: ![alt](/images/foo.jpg#center)
    while IFS= read -r inline_ref; do
      [[ -z "$inline_ref" ]] && continue
      # Strip #fragment
      local clean_ref="${inline_ref%%#*}"
      local fs_ref="static/${clean_ref#/}"

      if [[ ! -f "$fs_ref" ]] && [[ "$clean_ref" != http* ]]; then
        broken_refs+=("$mdfile|$inline_ref")
      else
        referenced_images+=("$fs_ref")
      fi
    done < <(grep -oP '!\[[^\]]*\]\(\K[^)]+' "$mdfile" 2>/dev/null || true)

  done < <(find "$CONTENT_DIR" -name '*.md' -print0)

  # Also check config.toml for avatarUrl
  local avatar_path
  avatar_path=$(grep 'avatarUrl' "$CONFIG_FILE" | sed 's/.*=\s*["'\'']\(.*\)["'\'']/\1/' || true)
  if [[ -n "$avatar_path" ]]; then
    referenced_images+=("static/${avatar_path#/}")
  fi

  # Find unreferenced images (compare using static/images/... paths)
  local -a unreferenced=()
  for img in "${image_files[@]}"; do
    local found=false
    for ref in "${referenced_images[@]}"; do
      if [[ "$ref" == "$img" ]]; then
        found=true
        break
      fi
    done
    if ! $found; then
      unreferenced+=("$img")
    fi
  done

  # Report broken references
  if [[ ${#broken_refs[@]} -gt 0 ]]; then
    warn "${#broken_refs[@]} broken image reference(s):"
    for entry in "${broken_refs[@]}"; do
      local file="${entry%%|*}"
      local ref="${entry##*|}"
      echo -e "    ${RED}$ref${NC}  in  ${DIM}$file${NC}"
    done
  else
    success "No broken image references"
  fi

  # Report unreferenced images
  echo ""
  if [[ ${#unreferenced[@]} -gt 0 ]]; then
    warn "${#unreferenced[@]} unreferenced image(s) (not used in any content):"
    for img in "${unreferenced[@]}"; do
      local fsize
      fsize=$(stat -c%s "$img" 2>/dev/null || stat -f%z "$img" 2>/dev/null)
      echo -e "    ${YELLOW}$(basename "$img")${NC}  ($(human_size "$fsize"))"
    done
  else
    success "All images are referenced in content"
  fi

  # Report inconsistent paths
  if [[ ${#inconsistent_paths[@]} -gt 0 ]]; then
    echo ""
    warn "${#inconsistent_paths[@]} image path(s) missing leading '/':"
    for entry in "${inconsistent_paths[@]}"; do
      local file="${entry%%|*}"
      local ref="${entry##*|}"
      echo -e "    ${YELLOW}$ref${NC}  in  ${DIM}$file${NC}"
    done
  fi

  # Export arrays for later phases (bash 4+ trick: print to temp files)
  printf '%s\n' "${image_files[@]}" > /tmp/optimg_files.txt
  printf '%s\n' "${unreferenced[@]+"${unreferenced[@]}"}" > /tmp/optimg_unreferenced.txt
  printf '%s\n' "${broken_refs[@]+"${broken_refs[@]}"}" > /tmp/optimg_broken.txt
  echo "$total_size" > /tmp/optimg_total_size.txt
}

# ---------------------------------------------------------------------------
# PHASE 2: METADATA STRIPPING
# ---------------------------------------------------------------------------
phase_strip_metadata() {
  header "PHASE 2: METADATA STRIPPING"

  if $DRY_RUN; then
    info "(dry-run) Would strip all EXIF/IPTC/XMP metadata from images"
    echo ""
    return
  fi

  local -a image_files=()
  mapfile -t image_files < /tmp/optimg_files.txt

  local stripped=0
  for img in "${image_files[@]}"; do
    [[ -z "$img" ]] && continue
    local fname
    fname=$(basename "$img")

    # Check if image has strippable EXIF/XMP/IPTC metadata (not just file properties)
    # Use -EXIF:All -XMP:All -IPTC:All to only check real metadata groups
    local meta_check
    meta_check=$(exiftool -s -s -s -EXIF:All -XMP:All -IPTC:All "$img" 2>/dev/null || true)

    if [[ -z "$meta_check" ]]; then
      echo -e "  ${DIM}$fname — already clean, skipping${NC}"
      continue
    fi

    # Auto-orient JPEG/PNG before stripping (applies EXIF rotation to pixels)
    local ext="${fname##*.}"
    ext=$(echo "$ext" | tr '[:upper:]' '[:lower:]')
    if [[ "$ext" == "jpg" ]] || [[ "$ext" == "jpeg" ]] || [[ "$ext" == "png" ]]; then
      magick "$img" -auto-orient "$img" 2>/dev/null || true
    fi

    # Strip all metadata
    exiftool -all= -overwrite_original "$img" 2>/dev/null
    stripped=$((stripped + 1))
    echo -e "  ${GREEN}$fname${NC} — metadata stripped"

    # Update manifest since file content changed
    manifest_update "$img"
  done

  echo ""
  success "Stripped metadata from $stripped image(s)"
}

# ---------------------------------------------------------------------------
# PHASE 3: CONVERT & COMPRESS
# ---------------------------------------------------------------------------
phase_convert() {
  header "PHASE 3: CONVERT TO WEBP & COMPRESS"

  local -a image_files=()
  mapfile -t image_files < /tmp/optimg_files.txt

  # Delete unreferenced images first
  local -a unreferenced=()
  mapfile -t unreferenced < /tmp/optimg_unreferenced.txt

  if [[ ${#unreferenced[@]} -gt 0 ]] && [[ -n "${unreferenced[0]}" ]]; then
    echo -e "${BOLD}Removing unreferenced images${NC}"
    for img in "${unreferenced[@]}"; do
      [[ -z "$img" ]] && continue
      local fsize
      fsize=$(stat -c%s "$img" 2>/dev/null || stat -f%z "$img" 2>/dev/null)
      if $DRY_RUN; then
        echo -e "  ${DIM}(dry-run) Would delete: $(basename "$img") ($(human_size "$fsize"))${NC}"
      else
        rm -f "$img"
        manifest_remove "$img"
        echo -e "  ${RED}Deleted:${NC} $(basename "$img") ($(human_size "$fsize"))"
      fi
    done
    echo ""
  fi

  echo -e "${BOLD}Converting images to WebP (quality $WEBP_QUALITY, max ${MAX_WIDTH}x${MAX_HEIGHT})${NC}"
  printf "  %-40s  %-12s  %-12s  %s\n" "FILENAME" "BEFORE" "AFTER" "STATUS"
  printf "  %-40s  %-12s  %-12s  %s\n" "--------" "------" "-----" "------"

  local total_before=0
  local total_after=0
  local converted=0
  local skipped=0
  local resized_only=0

  for img in "${image_files[@]}"; do
    [[ -z "$img" ]] && continue
    # Skip if this was an unreferenced file we just deleted
    [[ ! -f "$img" ]] && continue

    local fname
    fname=$(basename "$img")
    local ext="${fname##*.}"
    local base="${fname%.*}"
    local ext_lower
    ext_lower=$(echo "$ext" | tr '[:upper:]' '[:lower:]')
    local webp_path="$IMAGES_DIR/${base}.webp"

    local before_size
    before_size=$(stat -c%s "$img" 2>/dev/null || stat -f%z "$img" 2>/dev/null)
    total_before=$((total_before + before_size))

    # --- Manifest-based skip logic ---
    local current_hash stored_hash
    current_hash=$(manifest_hash "$img")
    stored_hash=$(manifest_lookup "$img")

    if [[ "$ext_lower" == "webp" ]] && [[ "$current_hash" == "$stored_hash" ]] && [[ -n "$stored_hash" ]]; then
      # Already optimized, hash unchanged — skip entirely
      total_after=$((total_after + before_size))
      skipped=$((skipped + 1))
      printf "  %-40s  %-12s  %-12s  ${DIM}%s${NC}\n" \
        "$fname" "$(human_size "$before_size")" "—" "already optimized"
      continue
    fi

    if $DRY_RUN; then
      if [[ "$ext_lower" == "webp" ]] && [[ -n "$stored_hash" ]] && [[ "$current_hash" != "$stored_hash" ]]; then
        echo -e "  ${DIM}(dry-run) $fname — edited, would check dimensions only${NC}"
      elif [[ "$ext_lower" == "webp" ]] && [[ -z "$stored_hash" ]]; then
        echo -e "  ${DIM}(dry-run) $fname — new webp, would record in manifest${NC}"
      else
        echo -e "  ${DIM}(dry-run) Would convert: $fname -> ${base}.webp${NC}"
      fi
      local est_after=$before_size
      case "$ext_lower" in
        jpg|jpeg) est_after=$((before_size / 5)) ;;
        png)      est_after=$((before_size / 3)) ;;
        webp)     est_after=$before_size ;;
      esac
      total_after=$((total_after + est_after))
      converted=$((converted + 1))
      continue
    fi

    # Get current dimensions
    local cur_width cur_height
    read -r cur_width cur_height < <(identify -format "%w %h\n" "$img" 2>/dev/null || echo "0 0")

    local needs_resize=false
    if (( cur_width > MAX_WIDTH )) || (( cur_height > MAX_HEIGHT )); then
      needs_resize=true
    fi

    # --- Already WebP (edited or new): skip lossy re-compression ---
    if [[ "$ext_lower" == "webp" ]]; then
      if $needs_resize; then
        # Resize oversized WebP via ImageMagick (lossless resize, no re-encoding)
        local tmp_resized
        tmp_resized=$(mktemp /tmp/optimg_XXXXXX.webp)
        magick "$img" -resize "${MAX_WIDTH}x${MAX_HEIGHT}>" "$tmp_resized"
        local new_dims
        new_dims=$(magick identify -format '%wx%h' "$tmp_resized")
        mv "$tmp_resized" "$img"
        info "  Resized $fname: ${cur_width}x${cur_height} -> $new_dims"
        resized_only=$((resized_only + 1))
      fi

      # Record in manifest (whether resized or just newly tracked)
      manifest_update "$img"

      local after_size
      after_size=$(stat -c%s "$img" 2>/dev/null || stat -f%z "$img" 2>/dev/null)
      total_after=$((total_after + after_size))

      if $needs_resize; then
        local savings=0
        if (( before_size > 0 )); then
          savings=$(( (before_size - after_size) * 100 / before_size ))
        fi
        printf "  %-40s  %-12s  %-12s  ${CYAN}%s${NC}\n" \
          "$fname" "$(human_size "$before_size")" "$(human_size "$after_size")" "resized (${savings}%)"
      elif [[ -z "$stored_hash" ]]; then
        printf "  %-40s  %-12s  %-12s  ${GREEN}%s${NC}\n" \
          "$fname" "$(human_size "$before_size")" "—" "recorded"
      else
        printf "  %-40s  %-12s  %-12s  ${CYAN}%s${NC}\n" \
          "$fname" "$(human_size "$before_size")" "—" "updated (edited)"
      fi

      converted=$((converted + 1))
      continue
    fi

    # --- Non-WebP image: full convert + compress pipeline ---
    local cwebp_input="$img"
    local tmp_resized=""

    if $needs_resize; then
      # Resize via ImageMagick, output to temp PNG for cwebp
      tmp_resized=$(mktemp /tmp/optimg_XXXXXX.png)
      magick "$img" -resize "${MAX_WIDTH}x${MAX_HEIGHT}>" -quality 100 "$tmp_resized"
      info "  Resized $fname: ${cur_width}x${cur_height} -> $(magick identify -format '%wx%h' "$tmp_resized")"
      cwebp_input="$tmp_resized"
    fi

    # Convert to WebP via cwebp
    cwebp -q "$WEBP_QUALITY" "$cwebp_input" -o "$webp_path" 2>/dev/null

    # Cleanup temp file if we resized
    [[ -n "$tmp_resized" ]] && rm -f "$tmp_resized"

    # Delete original non-WebP source
    rm -f "$img"

    # Record the new WebP in the manifest
    manifest_update "$webp_path"

    local after_size
    after_size=$(stat -c%s "$webp_path" 2>/dev/null || stat -f%z "$webp_path" 2>/dev/null)
    total_after=$((total_after + after_size))

    local savings=0
    if (( before_size > 0 )); then
      savings=$(( (before_size - after_size) * 100 / before_size ))
    fi

    local savings_color="$GREEN"
    if (( savings < 10 )); then
      savings_color="$YELLOW"
    fi

    printf "  %-40s  %-12s  %-12s  ${savings_color}%s%%${NC}\n" \
      "${base}.webp" "$(human_size "$before_size")" "$(human_size "$after_size")" "$savings"

    converted=$((converted + 1))
  done

  echo ""
  local total_savings=0
  if (( total_before > 0 )) && (( total_after > 0 )); then
    total_savings=$(( (total_before - total_after) * 100 / total_before ))
  fi
  if (( skipped > 0 )); then
    info "Skipped $skipped already-optimized image(s)"
  fi
  if (( resized_only > 0 )); then
    info "Resized $resized_only edited image(s) (no re-compression)"
  fi
  info "Processed $converted image(s)"
  if (( total_before > total_after )); then
    info "Total: $(human_size $total_before) -> $(human_size $total_after) (${total_savings}% reduction)"
  else
    info "Total size: $(human_size $total_after)"
  fi

  # Save totals for summary
  echo "$total_before" > /tmp/optimg_total_before.txt
  echo "$total_after" > /tmp/optimg_total_after.txt
  echo "$converted" > /tmp/optimg_converted.txt
  echo "$skipped" > /tmp/optimg_skipped.txt
}

# ---------------------------------------------------------------------------
# PHASE 4: UPDATE CONTENT REFERENCES
# ---------------------------------------------------------------------------
phase_update_refs() {
  header "PHASE 4: UPDATE CONTENT REFERENCES"

  local updated_files=0

  # --- Step 1: Update image extensions in content files ---
  # This must happen BEFORE broken ref clearing, since .jpg/.png files are now .webp
  echo -e "${BOLD}Updating image references (.jpg/.jpeg/.png -> .webp)${NC}"

  while IFS= read -r -d '' mdfile; do
    local changed=false

    # Normalize front matter paths first: change image: "images/... to image: "/images/...
    if grep -qE '^\s*image:\s*"images/' "$mdfile" 2>/dev/null; then
      if ! $DRY_RUN; then
        sed -i -E 's@^(\s*image:\s*)"images/@\1"/images/@' "$mdfile"
      fi
      changed=true
    fi

    # Update front matter image field (only local paths, not http URLs)
    # Handles both `image: "/images/..."` and `  image: "/images/..."` (indented under cover:)
    if grep -qE '^\s*image:\s*"/images/.*\.(jpg|jpeg|JPG|JPEG|png|PNG)"' "$mdfile" 2>/dev/null; then
      if ! $DRY_RUN; then
        sed -i -E 's@^(\s*image:\s*"/images/[^"]*)\.(jpg|jpeg|JPG|JPEG|png|PNG)"@\1.webp"@' "$mdfile"
      fi
      changed=true
    fi

    # Update inline markdown images: ![alt](/images/foo.jpg#center)
    # Only match local /images/ paths, not external URLs
    if grep -qP '!\[[^\]]*\]\(/images/[^)]*\.(jpg|jpeg|JPG|JPEG|png|PNG)(#[^)]*)?\)' "$mdfile" 2>/dev/null; then
      if ! $DRY_RUN; then
        sed -i -E 's@(!\[[^]]*\]\(/images/[^.)]*)\.(jpg|jpeg|JPG|JPEG|png|PNG)([#][^)]*)?(\))@\1.webp\3\4@g' "$mdfile"
      fi
      changed=true
    fi

    if $changed; then
      local relpath="${mdfile}"
      if $DRY_RUN; then
        echo -e "  ${DIM}(dry-run) Would update refs in: $relpath${NC}"
      else
        echo -e "  ${GREEN}Updated${NC} $relpath"
      fi
      updated_files=$((updated_files + 1))
    fi
  done < <(find "$CONTENT_DIR" -name '*.md' -print0)

  # --- Step 2: Update config.toml avatar ---
  if grep -q 'avatarUrl.*\.png' "$CONFIG_FILE" 2>/dev/null; then
    if $DRY_RUN; then
      echo -e "  ${DIM}(dry-run) Would update avatarUrl in $CONFIG_FILE${NC}"
    else
      sed -i 's@avatarUrl = "/images/fosscat_icon\.png"@avatarUrl = "/images/fosscat_icon.webp"@' "$CONFIG_FILE"
      echo -e "  ${GREEN}Updated${NC} avatarUrl in $CONFIG_FILE"
    fi
    updated_files=$((updated_files + 1))
  fi

  # --- Step 3: Clear genuinely broken image references ---
  # Only clear refs that still don't resolve after extension updates
  # (e.g., placeholder /images/img.jpg that was never a real image)
  echo ""
  echo -e "${BOLD}Checking for remaining broken image references${NC}"

  local cleared=0
  while IFS= read -r -d '' mdfile; do
    # Check front matter image fields
    while IFS= read -r fm_line; do
      [[ -z "$fm_line" ]] && continue
      local fm_image
      fm_image=$(echo "$fm_line" | sed 's/^[[:space:]]*image:[[:space:]]*//' | sed 's/^["'\'']//' | sed 's/["'\'']\s*$//')

      [[ -z "$fm_image" ]] && continue
      [[ "$fm_image" == '""' ]] && continue
      [[ "$fm_image" == http* ]] && continue

      local fs_path="static/${fm_image#/}"
      if [[ ! -f "$fs_path" ]]; then
        if $DRY_RUN; then
          echo -e "  ${DIM}(dry-run) Would clear broken ref in: $mdfile (was: $fm_image)${NC}"
        else
          local escaped_image
          escaped_image=$(echo "$fm_image" | sed 's/[.[\/*^$]/\\&/g')
          sed -i -E "s@^(\s*image:\s*).*${escaped_image}.*@\1\"\"@" "$mdfile"
          echo -e "  ${GREEN}Cleared${NC} broken ref ${DIM}$fm_image${NC} in ${DIM}$mdfile${NC}"
          cleared=$((cleared + 1))
        fi
      fi
    done < <(grep -E '^\s*image:\s' "$mdfile" 2>/dev/null || true)
  done < <(find "$CONTENT_DIR" -name '*.md' -print0)

  if [[ $cleared -eq 0 ]] && ! $DRY_RUN; then
    success "No broken image references remaining"
  fi

  echo ""
  info "Updated $updated_files file(s)"
}

# ---------------------------------------------------------------------------
# PHASE 5: SUMMARY
# ---------------------------------------------------------------------------
phase_summary() {
  header "PHASE 5: SUMMARY"

  if $DRY_RUN; then
    echo -e "${BOLD}${YELLOW}DRY RUN — no changes were made${NC}"
    echo ""
  fi

  local total_before total_after converted skipped
  total_before=$(cat /tmp/optimg_total_before.txt 2>/dev/null || cat /tmp/optimg_total_size.txt 2>/dev/null || echo 0)
  total_after=$(cat /tmp/optimg_total_after.txt 2>/dev/null || echo 0)
  converted=$(cat /tmp/optimg_converted.txt 2>/dev/null || echo 0)
  skipped=$(cat /tmp/optimg_skipped.txt 2>/dev/null || echo 0)

  local savings=0
  if (( total_before > 0 )) && (( total_after > 0 )); then
    savings=$(( (total_before - total_after) * 100 / total_before ))
  fi

  echo -e "  Images processed:  ${BOLD}$converted${NC}"
  if (( skipped > 0 )); then
    echo -e "  Images skipped:    ${BOLD}$skipped${NC} (already optimized)"
  fi
  if (( total_after > 0 )) && (( total_before > total_after )); then
    echo -e "  Size before:       ${BOLD}$(human_size "$total_before")${NC}"
    echo -e "  Size after:        ${BOLD}$(human_size "$total_after")${NC}"
    echo -e "  Total reduction:   ${BOLD}${GREEN}${savings}%${NC}"
  elif (( total_after > 0 )); then
    echo -e "  Total size:        ${BOLD}$(human_size "$total_after")${NC}"
  fi

  # Clean up stale manifest entries (files that no longer exist)
  if [[ -f "$MANIFEST_FILE" ]] && ! $DRY_RUN; then
    local stale=0
    local manifest_tmp
    manifest_tmp=$(mktemp /tmp/optimg_manifest_XXXXXX.txt)
    while IFS= read -r line; do
      # Keep comment lines
      if [[ "$line" == \#* ]]; then
        echo "$line" >> "$manifest_tmp"
        continue
      fi
      # Extract filename (second field, after hash + two spaces)
      local entry_file
      entry_file=$(echo "$line" | awk '{print $2}')
      if [[ -z "$entry_file" ]] || [[ -f "$entry_file" ]]; then
        echo "$line" >> "$manifest_tmp"
      else
        stale=$((stale + 1))
      fi
    done < "$MANIFEST_FILE"
    mv "$manifest_tmp" "$MANIFEST_FILE"
    if (( stale > 0 )); then
      info "Removed $stale stale manifest entry/entries"
    fi
  fi

  echo ""
  echo -e "  ${BOLD}Next steps:${NC}"
  echo -e "    1. Run ${CYAN}hugo server${NC} and verify images look correct"
  echo -e "    2. Check the browser dev tools Network tab for proper WebP delivery"
  echo -e "    3. Commit when satisfied: ${CYAN}git add -A && git commit -m \"optimize: convert images to webp, strip metadata\"${NC}"

  # Cleanup temp files
  rm -f /tmp/optimg_*.txt
}

# ---------------------------------------------------------------------------
# MAIN
# ---------------------------------------------------------------------------
main() {
  echo -e "${BOLD}${CYAN}"
  echo "  ┌─────────────────────────────────────────┐"
  echo "  │   fosscat.com Image Optimizer            │"
  echo "  │   Strip metadata · Convert to WebP       │"
  echo "  │   Resize · Audit references              │"
  echo "  └─────────────────────────────────────────┘"
  echo -e "${NC}"

  if $DRY_RUN; then
    echo -e "  ${YELLOW}Running in DRY RUN mode — no files will be modified${NC}"
    echo ""
  fi

  # Phase 1: Audit (always runs)
  phase_audit

  if $AUDIT_ONLY; then
    echo ""
    info "Audit complete. Run without --audit-only to process images."
    rm -f /tmp/optimg_*.txt
    return
  fi

  # Confirm before proceeding
  echo ""
  if ! $AUTO_YES && ! $DRY_RUN; then
    echo -en "  ${BOLD}Proceed with optimization? [y/N]${NC} "
    read -r answer
    if [[ ! "$answer" =~ ^[Yy]$ ]]; then
      info "Aborted."
      rm -f /tmp/optimg_*.txt
      exit 0
    fi
  fi

  # Phase 2: Strip metadata
  phase_strip_metadata

  # Phase 3: Convert & compress
  phase_convert

  # Phase 4: Update references
  phase_update_refs

  # Phase 5: Summary
  phase_summary
}

main
