#!/usr/bin/env bash
# do.sh — terminal productivity hub
# works on macOS and Debian/Ubuntu Linux
# zero dependencies: bash + standard unix tools only

set -euo pipefail

# ── error handling trap ──────────────────────────────────────────────────────
failure_handler() {
  local exit_code=$?
  local line_number=$1
  tput cnorm 2>/dev/null || true
  tput rmcup 2>/dev/null || true
  printf "\n\033[31m[!]\033[0m Script exited unexpectedly with status %s on line %s\n" "$exit_code" "$line_number"
}
trap 'failure_handler $LINENO' ERR

# ── data directory & configuration ───────────────────────────────────────────
DATA_DIR="${HOME}/.do"
mkdir -p "$DATA_DIR"

NOTES_FILE="$DATA_DIR/notes.md"
TODOS_FILE="$DATA_DIR/todos.txt"
EVENTS_FILE="$DATA_DIR/events.txt"
REMINDERS_FILE="$DATA_DIR/reminders.txt"
LOG_FILE="$DATA_DIR/log.md"
SCRATCH_FILE="$DATA_DIR/scratch.md"
CONFIG_FILE="$DATA_DIR/config"

touch "$NOTES_FILE" "$TODOS_FILE" "$EVENTS_FILE" "$REMINDERS_FILE" "$LOG_FILE" "$SCRATCH_FILE" "$CONFIG_FILE"

THEME=$(grep '^THEME=' "$CONFIG_FILE" 2>/dev/null | tail -n 1 | cut -d'=' -f2 || echo "dark")
THEME="${THEME:-dark}"

# ── terminal colours function ────────────────────────────────────────────────
init_colors() {
  if tput colors &>/dev/null && [ "$(tput colors)" -ge 8 ]; then
    C_RESET=$(tput sgr0)
    C_BOLD=$(tput bold)
    C_DIM=$(tput dim 2>/dev/null || printf '')
    C_REV=$(tput rev)

    if [[ "$THEME" == "light" ]]; then
      C_BLACK=$(tput setaf 0)
      C_RED=$(tput setaf 1)
      C_GREEN=$(tput setaf 2)
      C_YELLOW=$(tput setaf 3)
      C_BLUE=$(tput setaf 4)
      C_MAGENTA=$(tput setaf 5)
      C_CYAN=$(tput setaf 6)
      C_WHITE=$(tput setaf 0)
      C_BG_BLACK=$(tput setab 7)
      C_BG_BLUE=$(tput setab 4)
      C_BG_CYAN=$(tput setab 6)
    else
      C_BLACK=$(tput setaf 0)
      C_RED=$(tput setaf 1)
      C_GREEN=$(tput setaf 2)
      C_YELLOW=$(tput setaf 3)
      C_BLUE=$(tput setaf 4)
      C_MAGENTA=$(tput setaf 5)
      C_CYAN=$(tput setaf 6)
      C_WHITE=$(tput setaf 7)
      C_BG_BLACK=$(tput setab 0)
      C_BG_BLUE=$(tput setab 4)
      C_BG_CYAN=$(tput setab 6)
    fi
  else
    C_RESET='' C_BOLD='' C_DIM='' C_REV=''
    C_BLACK='' C_RED='' C_GREEN='' C_YELLOW=''
    C_BLUE='' C_MAGENTA='' C_CYAN='' C_WHITE=''
    C_BG_BLACK='' C_BG_BLUE='' C_BG_CYAN=''
  fi
}

init_colors

# ── helpers ──────────────────────────────────────────────────────────────────
clear_screen() { tput clear 2>/dev/null || clear; }
get_width()  { tput cols  2>/dev/null || echo 80; }
get_height() { tput lines 2>/dev/null || echo 24; }

hr() {
  local w; w=$(get_width)
  printf '%s' "${C_DIM}"
  printf '─%.0s' $(seq 1 "$w")
  printf '%s\n' "${C_RESET}"
}

header() {
  local title="$1"
  local w; w=$(get_width)
  clear_screen
  printf '%s%s' "${C_BOLD}${C_CYAN}" "┌"
  printf '─%.0s' $(seq 1 "$((w - 2))")
  printf '┐%s\n' "${C_RESET}"
  local pad=$(( (w - ${#title} - 2) / 2 ))
  printf '%s│%s' "${C_CYAN}" "${C_RESET}"
  printf '%*s' "$pad" ''
  printf '%s%s%s' "${C_BOLD}${C_WHITE}" "$title" "${C_RESET}"
  printf '%*s' "$(( w - pad - ${#title} - 2 ))" ''
  printf '%s│%s\n' "${C_CYAN}" "${C_RESET}"
  printf '%s' "${C_BOLD}${C_CYAN}" "└"
  printf '─%.0s' $(seq 1 "$((w - 2))")
  printf '┘%s\n' "${C_RESET}"
}

timestamp() { date '+%Y-%m-%d %H:%M'; }
today()     { date '+%Y-%m-%d'; }

pause() {
  printf '\n%s  press any key to continue…%s' "${C_DIM}" "${C_RESET}"
  read -r -n1 -s
}

confirm() {
  local msg="${1:-are you sure?}"
  printf '%s %s [y/N] %s' "${C_YELLOW}" "$msg" "${C_RESET}"
  read -r ans
  [[ "$ans" =~ ^[Yy]$ ]]
}

prompt() {
  local label="$1" varname="$2" default="${3:-}"
  if [[ -n "$default" ]]; then
    printf '%s%s%s [%s]: ' "${C_CYAN}" "$label" "${C_RESET}" "$default"
  else
    printf '%s%s%s: ' "${C_CYAN}" "$label" "${C_RESET}"
  fi
  read -r "$varname"
  if [[ -z "${!varname}" && -n "$default" ]]; then
    printf -v "$varname" '%s' "$default"
  fi
}

open_editor() {
  local file="$1"
  local ed="${VISUAL:-${EDITOR:-}}"
  if [[ -z "$ed" ]]; then
    for candidate in nano vim vi; do
      if command -v "$candidate" &>/dev/null; then
        ed="$candidate"; break
      fi
    done
  fi
  "${ed:-nano}" "$file"
}

# ── native markdown pager view ────────────────────────────────────────────────
view_markdown_file() {
  local file="$1"
  if [[ ! -s "$file" ]]; then
    printf '  File is currently empty.\n'
    pause
    return
  fi

  local pager="${PAGER:-less}"
  if ! command -v "$pager" &>/dev/null; then pager="more"; fi

  tput rmcup 2>/dev/null || true

  sed -e "s/^# \(.*\)/${C_BOLD}${C_MAGENTA}══ \1 ══${C_RESET}/g" \
      -e "s/^## \(.*\)/${C_BOLD}${C_CYAN}■ \1${C_RESET}/g" \
      -e "s/^### \(.*\)/${C_DIM}${C_YELLOW}↳ \1${C_RESET}/g" \
      -e "s/\*\*\([^*]*\)\*\*/${C_BOLD}${C_WHITE}\1${C_RESET}/g" \
      -e "s/_\([^_]*\)_/${C_DIM}\1${C_RESET}/g" \
      -e "s/^-\([^-].*\)/  ${C_CYAN}•${C_RESET}\1/g" \
      -e "s/^•\(.*\)/  ${C_CYAN}•${C_RESET}\1/g" \
      "$file" | $pager -R -E -X

  tput smcup 2>/dev/null || true
}

# ── arrow-key menu ───────────────────────────────────────────────────────────
arrow_menu() {
  local title="$1"; shift
  local items=("$@")
  local n=${#items[@]}
  local sel=0
  local key ESC

  ESC=$(printf '\033')
  tput civis 2>/dev/null || true

  while true; do
    header "$title"
    printf '\n'
    for i in "${!items[@]}"; do
      local display_num=$((i + 1))
      if [[ $display_num -eq 10 ]]; then
        display_num=0
      fi

      if [[ $i -eq $sel ]]; then
        printf '  %s%s▶  %s%s\n' "${C_BG_BLUE}${C_WHITE}${C_BOLD}" "${display_num}." "${items[$i]}" "${C_RESET}"
      else
        printf '     %s%s.  %s%s\n' "${C_DIM}" "${display_num}" "${items[$i]}" "${C_RESET}"
      fi
    done
    printf '\n%s  ↑↓ navigate   1-9/0 hotkeys   enter select   q quit%s\n' "${C_DIM}" "${C_RESET}"

    IFS= read -r -s -n1 key
    if [[ "$key" == "$ESC" ]]; then
      IFS= read -r -s -n1 -t 0.1 key2 || key2=''
      if [[ "$key2" == '[' ]]; then
        IFS= read -r -s -n1 -t 0.1 key3 || key3=''
        case "$key3" in
          A) (( sel > 0 )) && (( sel-- )) ;;
          B) (( sel < n-1 )) && (( sel++ )) ;;
          *) ;;
        esac
      else
        tput cnorm 2>/dev/null || true
        MENU_CHOICE=-1; return 1
      fi
    elif [[ "$key" == '' ]]; then
      tput cnorm 2>/dev/null || true
      MENU_CHOICE=$sel; return 0
    elif [[ "$key" =~ ^[1-9]$ ]]; then
      local choice=$(( key - 1 ))
      if (( choice < n )); then
        tput cnorm 2>/dev/null || true
        MENU_CHOICE=$choice; return 0
      fi
    elif [[ "$key" == '0' ]]; then
      if (( n == 10 )); then
        tput cnorm 2>/dev/null || true
        MENU_CHOICE=9; return 0
      else
        tput cnorm 2>/dev/null || true
        MENU_CHOICE=-1; return 1
      fi
    elif [[ "$key" == 'q' || "$key" == 'Q' ]]; then
      tput cnorm 2>/dev/null || true
      MENU_CHOICE=-1; return 1
    fi
  done
}

# ── due reminders check ───────────────────────────────────────────────────────
check_reminders() {
  [[ ! -s "$REMINDERS_FILE" ]] && return
  local now; now=$(date '+%Y%m%d%H%M')
  local due=()
  local keep=()

  while IFS='|' read -r when text || [[ -n "$when" ]]; do
    [[ -z "$when" ]] && continue
    local when_fmt; when_fmt=$(echo "$when" | tr -d ' :-')
    if (( when_fmt <= now )); then
      due+=("$text  (was: $when)")
    else
      keep+=("$when|$text")
    fi
  done < "$REMINDERS_FILE"

  if [[ ${#due[@]} -gt 0 ]]; then
    clear_screen
    printf '%s%s@  REMINDERS DUE%s\n\n' "${C_BOLD}" "${C_YELLOW}" "${C_RESET}"
    for d in "${due[@]}"; do
      printf '  %s•%s %s\n' "${C_RED}" "${C_RESET}" "$d"
    done
    printf '' > "$REMINDERS_FILE"
    for r in "${keep[@]}"; do
      printf '%s\n' "$r" >> "$REMINDERS_FILE"
    done
    pause
  fi
}

# ══════════════════════════════════════════════════════════════════════════════
# MODULES
# ══════════════════════════════════════════════════════════════════════════════

# ── notes ─────────────────────────────────────────────────────────────────────
module_notes() {
  while true; do
    local count; count=$(grep -c '^## ' "$NOTES_FILE" 2>/dev/null || echo 0)
    local items=(
      "New note"
      "List & View Notes  ($count)"
      "Search notes"
      "Edit notes file"
      "← Back"
    )
    arrow_menu "!!  Notes" "${items[@]}" || return

    case $MENU_CHOICE in
      0)  header "!!  New Note"
          prompt "Title" ntitle
          [[ -z "$ntitle" ]] && continue
          printf '\n## %s\n_%s_\n\n' "$ntitle" "$(timestamp)" >> "$NOTES_FILE"
          printf '%sEnter note%s (blank line twice to finish):\n\n' "${C_DIM}" "${C_RESET}"
          local line prev=''
          while IFS= read -r line; do
            [[ -z "$line" && -z "$prev" ]] && break
            printf '%s\n' "$line" >> "$NOTES_FILE"
            prev="$line"
          done
          printf '\n---\n\n' >> "$NOTES_FILE"
          printf '%s✓ note saved%s\n' "${C_GREEN}" "${C_RESET}"
          pause
          ;;
      1)  view_markdown_file "$NOTES_FILE" ;;
      2)  header "!!  Search Notes"
          prompt "Search term" sterm
          [[ -z "$sterm" ]] && continue
          printf '\n'
          grep -i -n "$sterm" "$NOTES_FILE" | head -40 | while IFS=: read -r lnum rest; do
            printf '  %s%s%s  %s\n' "${C_YELLOW}" "$lnum" "${C_RESET}" "$rest"
          done || printf '  no matches\n'
          pause
          ;;
      3)  open_editor "$NOTES_FILE" ;;
      4)  return ;;
    esac
  done
}

# ── todos ─────────────────────────────────────────────────────────────────────
module_todos() {
  while true; do
    local open; open=$(grep -c '^\[ \]' "$TODOS_FILE" 2>/dev/null || echo 0)
    local done_c; done_c=$(grep -c '^\[x\]' "$TODOS_FILE" 2>/dev/null || echo 0)
    local items=(
      "Add todo"
      "List todos  (${open} open, ${done_c} done)"
      "Mark done"
      "Delete done"
      "Clear all"
      "← Back"
    )
    arrow_menu "+  Todos" "${items[@]}" || return

    case $MENU_CHOICE in
      0)  header "+  Add Todo"
          prompt "Task" task
          [[ -z "$task" ]] && continue
          prompt "Priority (h/m/l)" pri "m"
          local ptag
          case "$pri" in h) ptag='[!]' ;; l) ptag='[-]' ;; *) ptag='[~]' ;; esac
          printf '[ ] %s %s — %s\n' "$ptag" "$task" "$(today)" >> "$TODOS_FILE"
          printf '%s✓ added%s\n' "${C_GREEN}" "${C_RESET}"
          pause
          ;;
      1)  header "+  Todos"
          if [[ ! -s "$TODOS_FILE" ]]; then
            printf '  no todos yet.\n'
          else
            local i=1
            while IFS= read -r line || [[ -n "$line" ]]; do
              [[ -z "$line" ]] && continue
              if [[ "$line" == '[ ] '* ]]; then
                printf '  %s%2d.%s %s%s%s\n' "${C_CYAN}" "$i" "${C_RESET}" "${C_WHITE}" "$line" "${C_RESET}"
              else
                printf '  %s%2d.%s %s%s%s\n' "${C_DIM}" "$i" "${C_RESET}" "${C_DIM}" "$line" "${C_RESET}"
              fi
              (( i++ ))
            done < "$TODOS_FILE"
          fi
          pause
          ;;
      2)  header "+  Mark Done"
          local i=1
          while IFS= read -r line || [[ -n "$line" ]]; do
            [[ -z "$line" ]] && continue
            if [[ "$line" == '[ ] '* ]]; then
              printf '  %s%2d.%s %s\n' "${C_CYAN}" "$i" "${C_RESET}" "$line"
            fi
            (( i++ ))
          done < "$TODOS_FILE"
          printf '\n'
          prompt "Line number" dnum
          [[ -z "$dnum" ]] && continue
          if [[ "$OSTYPE" == darwin* ]]; then
            sed -i '' "${dnum}s/^\[ \]/[x]/" "$TODOS_FILE"
          else
            sed -i "${dnum}s/^\[ \]/[x]/" "$TODOS_FILE"
          fi
          printf '%s✓ marked done%s\n' "${C_GREEN}" "${C_RESET}"
          pause
          ;;
      3)  grep -v '^\[x\]' "$TODOS_FILE" > "$DATA_DIR/.todos_tmp" || true
          if [ -s "$DATA_DIR/.todos_tmp" ]; then
            mv "$DATA_DIR/.todos_tmp" "$TODOS_FILE"
          else
            printf '' > "$TODOS_FILE"
          fi
          printf '%s✓ done items cleared%s\n' "${C_GREEN}" "${C_RESET}"
          pause
          ;;
      4)  confirm "Clear ALL todos?" && printf '' > "$TODOS_FILE" && printf '%s✓ cleared%s\n' "${C_GREEN}" "${C_RESET}"
          pause
          ;;
      5)  return ;;
    esac
  done
}

# ── calendar ──────────────────────────────────────────────────────────────────
module_calendar() {
  while true; do
    local items=(
      "View this month"
      "View specific month"
      "Add event"
      "List events"
      "Delete event"
      "← Back"
    )
    arrow_menu "#  Calendar" "${items[@]}" || return

    case $MENU_CHOICE in
      0)  header "#  $(date '+%B %Y')"
          printf '\n'
          cal || cal -h || true
          printf '\n'
          local ym; ym=$(date '+%Y-%m')
          if grep -q "^$ym" "$EVENTS_FILE" 2>/dev/null; then
            printf '%sEvents this month:%s\n' "${C_BOLD}" "${C_RESET}"
            grep "^$ym" "$EVENTS_FILE" | while IFS='|' read -r date text; do
              printf '  %s%s%s  %s\n' "${C_CYAN}" "$date" "${C_RESET}" "$text"
            done
          fi
          pause
          ;;
      1)  header "#  View Month"
          prompt "Year (YYYY)" yr "$(date '+%Y')"
          prompt "Month (1-12)" mo "$(date '+%-m')"
          printf '\n'
          cal "$mo" "$yr" || cal -m "$mo" "$yr" || true
          printf '\n'
          local ym2; ym2=$(printf '%04d-%02d' "$yr" "$mo")
          if grep -q "^$ym2" "$EVENTS_FILE" 2>/dev/null; then
            printf '%sEvents:%s\n' "${C_BOLD}" "${C_RESET}"
            grep "^$ym2" "$EVENTS_FILE" | while IFS='|' read -r date text; do
              printf '  %s%s%s  %s\n' "${C_CYAN}" "$date" "${C_RESET}" "$text"
            done
          fi
          pause
          ;;
      2)  header "#  Add Event"
          prompt "Date (YYYY-MM-DD)" edate "$(today)"
          prompt "Event" etext
          [[ -z "$etext" ]] && continue
          printf '%s|%s\n' "$edate" "$etext" >> "$EVENTS_FILE"
          sort -o "$EVENTS_FILE" "$EVENTS_FILE"
          printf '%s✓ event saved%s\n' "${C_GREEN}" "${C_RESET}"
          pause
          ;;
      3)  header "#  All Events"
          if [[ ! -s "$EVENTS_FILE" ]]; then
            printf '  no events yet.\n'
          else
            local i=1
            while IFS='|' read -r date text || [[ -n "$date" ]]; do
              [[ -z "$date" ]] && continue
              printf '  %s%2d.%s %s%s%s  %s\n' "${C_CYAN}" "$i" "${C_RESET}" "${C_YELLOW}" "$date" "${C_RESET}" "$text"
              (( i++ ))
            done < "$EVENTS_FILE"
          fi
          pause
          ;;
      4)  header "#  Delete Event"
          if [[ ! -s "$EVENTS_FILE" ]]; then
            printf '  no events.\n'; pause; continue
          fi
          local i=1
          while IFS='|' read -r date text || [[ -n "$date" ]]; do
            [[ -z "$date" ]] && continue
            printf '  %s%2d.%s %s%s%s  %s\n' "${C_CYAN}" "$i" "${C_RESET}" "${C_YELLOW}" "$date" "${C_RESET}" "$text"
            (( i++ ))
          done < "$EVENTS_FILE"
          printf '\n'
          prompt "Delete line number" dline
          [[ -z "$dline" ]] && continue
          if [[ "$OSTYPE" == darwin* ]]; then
            sed -i '' "${dline}d" "$EVENTS_FILE"
          else
            sed -i "${dline}d" "$EVENTS_FILE"
          fi
          printf '%s✓ deleted%s\n' "${C_GREEN}" "${C_RESET}"
          pause
          ;;
      5)  return ;;
    esac
  done
}

# ── calculator ────────────────────────────────────────────────────────────────
module_calculator() {
  header "=  Calculator"
  if ! command -v bc &>/dev/null; then
    printf '%sbc not found — please install it%s\n' "${C_RED}" "${C_RESET}"
    pause; return
  fi
  printf '%s  supports: + - * / ^ %% sqrt() s() c() l() and standard bc math%s\n' "${C_DIM}" "${C_RESET}"
  printf '%s  type "q" or press 0/Enter to exit%s\n\n' "${C_DIM}" "${C_RESET}"
  local expr result
  while true; do
    printf '%s›%s ' "${C_CYAN}" "${C_RESET}"
    IFS= read -r expr
    [[ "$expr" =~ ^[qQ]$ || -z "$expr" || "$expr" == "0" ]] && break
    result=$(printf 'scale=10; %s\n' "$expr" | bc -l 2>&1 || echo "Error")
    if [[ -n "$result" && "$result" != "Error" ]]; then
      result=$(printf '%s' "$result" | sed 's/\.?0*$//')
      printf '  %s=%s %s%s%s\n' "${C_DIM}" "${C_RESET}" "${C_GREEN}" "$result" "${C_RESET}"
    fi
  done
}

# ── reminders ────────────────────────────────────────────────────────────────
module_reminders() {
  while true; do
    local count; count=$(wc -l < "$REMINDERS_FILE" | tr -d ' ')
    local items=(
      "Add reminder  ($count pending)"
      "List reminders"
      "Delete reminder"
      "← Back"
    )
    arrow_menu "@  Reminders" "${items[@]}" || return

    case $MENU_CHOICE in
      0)  header "@  Add Reminder"
          prompt "Date (YYYY-MM-DD)" rdate "$(today)"
          prompt "Time (HH:MM, 24h)" rtime "09:00"
          prompt "Reminder text" rtext
          [[ -z "$rtext" ]] && continue
          printf '%s %s|%s\n' "$rdate" "$rtime" "$rtext" >> "$REMINDERS_FILE"
          printf '%s✓ reminder set for %s %s%s\n' "${C_GREEN}" "$rdate" "$rtime" "${C_RESET}"
          pause
          ;;
      1)  header "@  Pending Reminders"
          if [[ ! -s "$REMINDERS_FILE" ]]; then
            printf '  no reminders set.\n'
          else
            local i=1
            while IFS='|' read -r when text || [[ -n "$when" ]]; do
              [[ -z "$when" ]] && continue
              printf '  %s%2d.%s %s%s%s  %s\n' "${C_CYAN}" "$i" "${C_RESET}" "${C_YELLOW}" "$when" "${C_RESET}" "$text"
              (( i++ ))
            done < "$REMINDERS_FILE"
          fi
          pause
          ;;
      2)  header "@  Delete Reminder"
          if [[ ! -s "$REMINDERS_FILE" ]]; then
            printf '  no reminders.\n'; pause; continue
          fi
          local i=1
          while IFS='|' read -r when text || [[ -n "$when" ]]; do
            [[ -z "$when" ]] && continue
            printf '  %s%2d.%s %s%s%s  %s\n' "${C_CYAN}" "$i" "${C_RESET}" "${C_YELLOW}" "$when" "${C_RESET}" "$text"
            (( i++ ))
          done < "$REMINDERS_FILE"
          printf '\n'
          prompt "Delete line number" dline
          [[ -z "$dline" ]] && continue
          if [[ "$OSTYPE" == darwin* ]]; then
            sed -i '' "${dline}d" "$REMINDERS_FILE"
          else
            sed -i "${dline}d" "$REMINDERS_FILE"
          fi
          printf '%s✓ deleted%s\n' "${C_GREEN}" "${C_RESET}"
          pause
          ;;
      3)  return ;;
    esac
  done
}

# ── pomodoro ──────────────────────────────────────────────────────────────────
module_pomodoro() {
  while true; do
    local items=(
      "Start 25min focus"
      "Start 5min break"
      "Start 15min long break"
      "Custom timer"
      "← Back"
    )
    arrow_menu "()  Pomodoro" "${items[@]}" || return

    local mins=0
    local label=''
    case $MENU_CHOICE in
      0) mins=25; label="Focus" ;;
      1) mins=5;  label="Short Break" ;;
      2) mins=15; label="Long Break" ;;
      3)
        header "()  Custom Timer"
        prompt "Minutes" mins
        prompt "Label" label "Timer"
        [[ -z "$mins" ]] && continue
        ;;
      4) return ;;
    esac

    local total=$(( mins * 60 ))
    local start; start=$(date +%s)
    local end=$(( start + total ))

    tput civis 2>/dev/null || true
    while true; do
      local now; now=$(date +%s)
      local remaining=$(( end - now ))
      [[ $remaining -le 0 ]] && break
      local mm=$(( remaining / 60 ))
      local ss=$(( remaining % 60 ))
      local pct=$(( (total - remaining) * 100 / total ))
      local bar_w=40
      local filled=$(( pct * bar_w / 100 ))
      local bar
      bar=$(printf '%*s' "$filled" '' | tr ' ' '█')
      bar+=$(printf '%*s' $(( bar_w - filled )) '' | tr ' ' '░')

      clear_screen
      header "()  $label"
      printf '\n\n'
      printf '      %s%02d:%02d%s\n\n' "${C_BOLD}${C_WHITE}" "$mm" "$ss" "${C_RESET}"
      printf '      %s[%s]%s  %d%%\n\n' "${C_CYAN}" "$bar" "${C_RESET}" "$pct"
      printf '  %spress q to stop%s\n' "${C_DIM}" "${C_RESET}"

      if IFS= read -r -s -n1 -t 1 key 2>/dev/null; then
        [[ "$key" == 'q' || "$key" == 'Q' ]] && break
      fi
    done
    tput cnorm 2>/dev/null || true

    printf '\a'
    clear_screen
    header "()  $label Complete"
    printf '\n  %s%s done!%s  Time: %dm\n' "${C_GREEN}${C_BOLD}" "$label" "${C_RESET}" "$mins"
    if command -v osascript &>/dev/null; then
      osascript -e "display notification \"${label} complete\" with title \"do.sh\"" 2>/dev/null || true
    fi
    pause
  done
}

# ── log / journal ─────────────────────────────────────────────────────────────
module_log() {
  while true; do
    local count; count=$(grep -c '^### ' "$LOG_FILE" 2>/dev/null || echo 0)
    local items=(
      "Quick log entry"
      "View Today's Log"
      "View Full Highlighted Log"
      "Edit log file"
      "← Back"
    )
    arrow_menu "~  Log / Journal  ($count entries)" "${items[@]}" || return

    case $MENU_CHOICE in
      0)  header "~  Log Entry"
          printf '%s  %s%s\n\n' "${C_DIM}" "$(timestamp)" "${C_RESET}"
          printf '%sEntry%s (blank line twice to finish):\n\n' "${C_CYAN}" "${C_RESET}"
          {
            printf '### %s\n\n' "$(timestamp)"
            local line prev=''
            while IFS= read -r line; do
              [[ -z "$line" && -z "$prev" ]] && break
              printf '%s\n' "$line"
              prev="$line"
            done
            printf '\n---\n\n'
          } >> "$LOG_FILE"
          printf '%s✓ logged%s\n' "${C_GREEN}" "${C_RESET}"
          pause
          ;;
      1)  header "~  Today's Log"
          local td; td=$(today)
          if grep -q "^### $td" "$LOG_FILE" 2>/dev/null; then
            sed -n "/^### $td/,/^---/p" "$LOG_FILE"
          else
            printf '  no entries today.\n'
          fi
          pause
          ;;
      2)  view_markdown_file "$LOG_FILE" ;;
      3)  open_editor "$LOG_FILE" ;;
      4)  return ;;
    esac
  done
}

# ── scratch pad ───────────────────────────────────────────────────────────────
module_scratch() {
  while true; do
    local items=(
      "View formatted scratch pad"
      "Append to scratch pad"
      "Edit scratch pad"
      "Clear scratch pad"
      "← Back"
    )
    arrow_menu "* Scratch Pad" "${items[@]}" || return

    case $MENU_CHOICE in
      0)  view_markdown_file "$SCRATCH_FILE" ;;
      1)  header "* Append to Scratch"
          printf '%sText%s (blank line twice to finish):\n\n' "${C_CYAN}" "${C_RESET}"
          local line prev=''
          while IFS= read -r line; do
            [[ -z "$line" && -z "$prev" ]] && break
            printf '%s\n' "$line" >> "$SCRATCH_FILE"
            prev="$line"
          done
          printf '\n' >> "$SCRATCH_FILE"
          printf '%s✓ appended%s\n' "${C_GREEN}" "${C_RESET}"
          pause
          ;;
      2)  open_editor "$SCRATCH_FILE" ;;
      3)  confirm "Clear scratch pad?" && printf '' > "$SCRATCH_FILE" && printf '%s✓ cleared%s\n' "${C_GREEN}" "${C_RESET}"
          pause
          ;;
      4)  return ;;
    esac
  done
}

# ── system dashboard ──────────────────────────────────────────────────────────
module_dashboard() {
  header "::  Dashboard"
  printf '\n'
  local w; w=$(get_width)

  # 1. TIME & DATE BANNER
  printf '  %s%s%s  %s\n\n' "${C_BOLD}${C_CYAN}" "$(date '+%A, %d %B %Y')" "${C_RESET}" "$(date '+%H:%M:%S')"

  # 2. Daily Zen Quote
  if command -v curl &>/dev/null; then
    local live_quote
    live_quote=$(curl -sL --max-time 3 "https://zenquotes.io/api/random" | grep -o '"q":"[^"]*"' | sed 's/"q":"//; s/"//' || echo "")
    if [[ -n "$live_quote" ]]; then
      printf '  %s"%s"%s\n\n' "${C_DIM}${C_YELLOW}" "$live_quote" "${C_RESET}"
    fi
  fi

  # 3. Weather
  if command -v curl &>/dev/null; then
    printf '  %sWeather:%s ' "${C_DIM}" "${C_RESET}"
    curl -sS --max-time 3 "wttr.in/?format=3" 2>/dev/null || printf '%s[Weather offline]%s\n' "${C_RED}" "${C_RESET}"
  fi

  # 4. Hostname & Network Info
  local hostname; hostname=$(hostname -s 2>/dev/null || hostname)
  printf '  %sHost:%s    %s\n' "${C_DIM}" "${C_RESET}" "$hostname"

  if command -v curl &>/dev/null; then
    local public_ip geo_city geo_region network_info
    public_ip=$(curl -sL --max-time 2 "https://ifconfig.me" || echo "")

    if [[ -n "$public_ip" ]]; then
      local geo_data
      geo_data=$(curl -sL --max-time 2 "https://ipinfo.io/json" 2>/dev/null || echo "")
      geo_city=$(printf '%s' "$geo_data" | grep '"city"' | sed -e 's/[",]//g' -e 's/.*city://' -e 's/^[[:space:]]*//')
      geo_region=$(printf '%s' "$geo_data" | grep '"region"' | sed -e 's/[",]//g' -e 's/.*region://' -e 's/^[[:space:]]*//')

      if [[ -n "$geo_city" && -n "$geo_region" ]]; then
        network_info="${public_ip} (${geo_city}, ${geo_region})"
      else
        network_info="${public_ip}"
      fi
      printf '  %sNetwork:%s %s\n' "${C_DIM}" "${C_RESET}" "$network_info"
    fi
  fi

  # 5. Diagnostics
  local uptime_str
  uptime_str=$(uptime 2>/dev/null | sed 's/.*up /up /' | sed 's/,.*load.*//' | xargs || echo "unknown")
  printf '  %sUptime:%s  %s\n' "${C_DIM}" "${C_RESET}" "$uptime_str"

  local disk
  disk=$(df -h / 2>/dev/null | awk 'NR==2 {print $3 " used / " $2 " total (" $5 ")"}')
  printf '  %sDisk /:%s  %s\n' "${C_DIM}" "${C_RESET}" "$disk"

  if [[ "$OSTYPE" == darwin* ]]; then
    local pages_free pages_active pages_inactive pages_wired page_size
    page_size=$(sysctl -n hw.pagesize 2>/dev/null || echo 4096)
    pages_wired=$(vm_stat 2>/dev/null | awk '/Pages wired/ {gsub(/\./,"",$NF); print $NF}' || echo 0)
    pages_active=$(vm_stat 2>/dev/null | awk '/Pages active/ {gsub(/\./,"",$NF); print $NF}' || echo 0)
    local used_mb=$(( (pages_wired + pages_active) * page_size / 1048576 ))
    local total_mb=$(( $(sysctl -n hw.memsize 2>/dev/null || echo 0) / 1048576 ))
    printf '  %sMemory:%s  %dMB used / %dMB total\n' "${C_DIM}" "${C_RESET}" "$used_mb" "$total_mb"
  else
    local mem
    mem=$(free -h 2>/dev/null | awk '/^Mem:/ {print $3 " used / " $2 " total"}' || echo "unknown")
    [[ -n "$mem" ]] && printf '  %sMemory:%s  %s\n' "${C_DIM}" "${C_RESET}" "$mem"
  fi

  printf '\n'
  hr

  # 6. DYNAMIC RSS WIRE
  if command -v curl &>/dev/null; then
    local feed_url=$(grep '^FEED_URL=' "$CONFIG_FILE" | tail -n 1 | cut -d'=' -f2 || echo "")
    local feed_title=$(grep '^FEED_TITLE=' "$CONFIG_FILE" | tail -n 1 | cut -d'=' -f2 || echo "")
    feed_url="${feed_url:-https://feeds.npr.org/1001/rss.xml}"
    feed_title="${feed_title:-NPR News}"

    printf '\n  %s%s  %s%s\n' "${C_BOLD}" "${C_YELLOW}" "${feed_title^^} WIRE" "${C_RESET}"
    local news_feed
    news_feed=$(curl -sL --max-time 3 "$feed_url" | \
      grep -o '<title><!\[CDATA\[[^\]]*\]\]><\/title>\|<title>[^<]*<\/title>' | \
      sed -e 's/<title><!\[CDATA\[//g' -e 's/\]\]><\/title>//g' \
          -e 's/<title>//g' -e 's/<\/title>//g' \
          -e 's/^[[:space:]]*//' -e '/^[[:space:]]*$/d' | \
      grep -v -E "NPR Topics|NPR : National Public Radio|Hacker News|Phoronix" | \
      head -6 || echo "")

    if [[ -n "$news_feed" ]]; then
      printf '%s\n' "$news_feed" | while read -r line; do
        printf '    %s•%s %s\n' "${C_CYAN}" "${C_RESET}" "$line"
      done
    else
      printf '    %s[News stream unserviceable or offline]%s\n' "${C_RED}" "${C_RESET}"
    fi
  fi

  printf '\n'
  hr

  # 7. Local Metrics
  local open; open=$(grep -c '^\[ \]' "$TODOS_FILE" 2>/dev/null || echo 0)
  printf '\n  %s+ Todos:%s  %s open\n' "${C_YELLOW}" "${C_RESET}" "$open"

  local rcount; rcount=$(wc -l < "$REMINDERS_FILE" | tr -d ' ' || echo 0)
  printf '  %s@ Reminders:%s %s pending\n' "${C_YELLOW}" "${C_RESET}" "$rcount"

  if [[ -s "$EVENTS_FILE" ]]; then
    local from; from=$(today)
    local to
    if [[ "$OSTYPE" == darwin* ]]; then
      to=$(date -v+7d '+%Y-%m-%d')
    else
      to=$(date -d '+7 days' '+%Y-%m-%d')
    fi
    local upcoming
    upcoming=$(awk -F'|' -v f="$from" -v t="$to" '$1 >= f && $1 <= t {print "  " $1 "  " $2}' "$EVENTS_FILE" 2>/dev/null || echo "")
    if [[ -n "$upcoming" ]]; then
      printf '\n  %s# Upcoming (7 days):%s\n' "${C_YELLOW}" "${C_RESET}"
      printf '%s%s%s\n' "${C_CYAN}" "$upcoming" "${C_RESET}"
    fi
  fi

  printf '\n'
  pause
}

# ── text tools sub-menu ───────────────────────────────────────────────────────
module_text() {
  while true; do
    local items=(
      "Word count a file"
      "Count words in typed text"
      "Generate password"
      "Base64 tools…"
      "URL encode"
      "← Back"
    )
    arrow_menu "%  Text Tools" "${items[@]}" || return

    case $MENU_CHOICE in
      0)  header "%  Word Count"
          prompt "File path" fpath
          [[ -z "$fpath" ]] && continue
          fpath="${fpath/#\~/$HOME}"
          if [[ ! -f "$fpath" ]]; then
            printf '%sfile not found%s\n' "${C_RED}" "${C_RESET}"
          else
            wc -l -w -c "$fpath"
            printf '%s(lines, words, bytes)%s\n' "${C_DIM}" "${C_RESET}"
          fi
          pause
          ;;
      1)  header "%  Word Count Text"
          printf '%sType/paste text, then Ctrl-D:%s\n\n' "${C_DIM}" "${C_RESET}"
          local text; text=$(cat)
          local wc_out; wc_out=$(printf '%s' "$text" | wc -l -w -c)
          printf '\n%s%s%s  (lines, words, bytes)\n' "${C_GREEN}" "$wc_out" "${C_RESET}"
          pause
          ;;
      2)  header "%  Generate Password"
          prompt "Length" plen "20"
          local pw
          pw=$(LC_ALL=C tr -dc 'A-Za-z0-9!@#$%^&*()-_=+[]{}' < /dev/urandom | head -c "$plen" || echo "")
          printf '\n  %s%s%s\n' "${C_GREEN}${C_BOLD}" "$pw" "${C_RESET}"
          pause
          ;;
      3)  module_text_base64 ;;
      4)  header "%  URL Encode"
          prompt "Text" utext
          printf '%s' "$utext" | python3 -c "import sys,urllib.parse; print(urllib.parse.quote(sys.stdin.read().strip()))" 2>/dev/null \
            || printf '%s' "$utext" | sed 's/ /%20/g; s/&/%26/g; s/=/%3D/g; s/?/%3F/g'
          printf '\n'
          pause
          ;;
      5)  return ;;
    esac
  done
}

# ── text tools sub-menu: base64 ───────────────────────────────────────────────
module_text_base64() {
  while true; do
    local items=(
      "Base64 encode string"
      "Base64 decode string"
      "← Back"
    )
    arrow_menu "%  Base64 Tools" "${items[@]}" || return

    case $MENU_CHOICE in
      0)  header "%  Base64 Encode"
          prompt "Text" btext
          printf '%s\n' "$btext" | base64
          pause
          ;;
      1)  header "%  Base64 Decode"
          prompt "Base64 string" bstr
          printf '%s' "$bstr" | base64 -d 2>/dev/null && printf '\n'
          pause
          ;;
      2)  return ;;
    esac
  done
}

# ── settings menu (theme & rss switcher) ──────────────────────────────────────
module_settings() {
  while true; do
    local items=(
      "Set Feed: NPR News"
      "Set Feed: Hacker News"
      "Set Feed: Phoronix (Linux Tech)"
      "Switch to Light Theme"
      "Switch to Dark Theme"
      "← Back"
    )
    arrow_menu "⚙  RSS & Settings (Current Theme: ${THEME^})" "${items[@]}" || return

    case $MENU_CHOICE in
      0) printf 'FEED_URL=https://feeds.npr.org/1001/rss.xml\nFEED_TITLE=NPR News\n' >> "$CONFIG_FILE" ;;
      1) printf 'FEED_URL=https://news.ycombinator.com/rss\nFEED_TITLE=Hacker News\n' >> "$CONFIG_FILE" ;;
      2) printf 'FEED_URL=https://www.phoronix.com/phoronix-rss.php\nFEED_TITLE=Phoronix\n' >> "$CONFIG_FILE" ;;
      3)
        THEME="light"
        printf 'THEME=light\n' >> "$CONFIG_FILE"
        init_colors
        printf '%s✓ Applied Light Theme%s\n' "${C_GREEN}" "${C_RESET}"
        ;;
      4)
        THEME="dark"
        printf 'THEME=dark\n' >> "$CONFIG_FILE"
        init_colors
        printf '%s✓ Applied Dark Theme%s\n' "${C_GREEN}" "${C_RESET}"
        ;;
      5)  return ;;
    esac

    # Keep config pristine: clean structural entries to avoid growth
    local tmp_cfg="$DATA_DIR/.cfg_tmp"
    local current_furl; current_furl=$(grep '^FEED_URL=' "$CONFIG_FILE" | tail -n 1 || echo "")
    local current_fttl; current_fttl=$(grep '^FEED_TITLE=' "$CONFIG_FILE" | tail -n 1 || echo "")
    local current_thm;  current_thm=$(grep '^THEME=' "$CONFIG_FILE" | tail -n 1 || echo "")

    printf '' > "$tmp_cfg"
    [[ -n "$current_furl" ]] && printf '%s\n' "$current_furl" >> "$tmp_cfg"
    [[ -n "$current_fttl" ]] && printf '%s\n' "$current_fttl" >> "$tmp_cfg"
    [[ -n "$current_thm" ]]  && printf '%s\n' "$current_thm" >> "$tmp_cfg"
    mv "$tmp_cfg" "$CONFIG_FILE"

    printf '%s✓ Preference Updated%s\n' "${C_GREEN}" "${C_RESET}"
    pause
  done
}

# ── system administration sub-menu ────────────────────────────────────────────
module_sysadmin() {
  while true; do
    local items=(
      "Text Tools…"
      "⚙   Settings"
      "?   Help Guide"
      "← Back"
    )
    arrow_menu "🛠  System & Configuration" "${items[@]}" || return

    case $MENU_CHOICE in
      0)  module_text ;;
      1)  module_settings ;;
      2)  module_help ;;
      3)  return ;;
    esac
  done
}

# ── documentation system ──────────────────────────────────────────────────────
module_help() {
  local pager="${PAGER:-less}"
  if ! command -v "$pager" &>/dev/null; then
    pager="more"
  fi

  tput rmcup 2>/dev/null || true
  clear_screen

  local tmp_help="$DATA_DIR/.help_tmp"

  cat << 'EOF' > "$tmp_help"
================================================================================
                     DO.SH — TERMINAL PRODUCTIVITY HUB
================================================================================

do.sh is a minimal, keyboard-driven productivity utility built entirely in
pure Bash using standard POSIX command-line tools. It maintains structured
plaintext backend files within your home directory.

--------------------------------------------------------------------------------
1. NAVIGATION AND UTILITY SHORTCUTS
--------------------------------------------------------------------------------
  • Arrow Keys (Up / Down) : Highlight a menu option.
  • Hotkeys (1 - 9)        : Quick-jump directly to an entry index.
  • Zero (0)               : Instantly execute the 10th option (Back/Quit).
  • Enter                  : Confirm highlighted selection.
  • Q / Escape             : Instantly go back a menu tier or quit the app.
  • Blank twice (Return)   : When capturing long-form text (Notes/Logs), press
                             Return on an empty line twice to close entry.

--------------------------------------------------------------------------------
2. COMPONENT OVERVIEW
--------------------------------------------------------------------------------
  • Dashboard   : Consolidates live network metadata, local resource telemetry,
                  asynchronous weather patterns, and a dynamic RSS wire.
  • Notes       : Appends structural Markdown definitions directly to your
                  persistent local database file.
  • Todos       : Tracks outstanding workflows with prioritization metrics.
  • Calendar    : Interfaces with native terminal utilities to map timelines.
  • Calculator  : Passes raw arithmetic expressions straight to the bc backend.
  • Reminders   : Polls epoch timestamps to trigger intrusive task alerts.
  • Pomodoro    : Provisions focused productivity intervals with desktop notifications.
  • Log/Journal : Maintains chronological personal logs indexed by timestamp.
  • Scratch Pad : Provides volatile workspace for intermediate formatting buffers.

================================================================================
Press [q] to exit this documentation browser and return to the application.
================================================================================
EOF

  $pager -R -E -X "$tmp_help"
  rm -f "$tmp_help"

  tput smcup 2>/dev/null || true
}

# ══════════════════════════════════════════════════════════════════════════════
# MAIN MENU
# ══════════════════════════════════════════════════════════════════════════════

main() {
  trap 'tput cnorm 2>/dev/null; tput rmcup 2>/dev/null || true; printf "\n"' EXIT INT TERM

  tput smcup 2>/dev/null || true
  check_reminders

  local main_items=(
    "::     Dashboard"
    "!!     Notes"
    "+      Todos"
    "#      Calendar"
    "=      Calculator"
    "@      Reminders"
    "()     Pomodoro"
    "~      Log / Journal"
    "*      Scratch Pad"
    "X      Utilities"
  )

  while true; do
    arrow_menu "do.sh  —  $(date '+%a %d %b  %H:%M')" "${main_items[@]}" || break

    case $MENU_CHOICE in
      0)  module_dashboard ;;
      1)  module_notes ;;
      2)  module_todos ;;
      3)  module_calendar ;;
      4)  module_calculator ;;
      5)  module_reminders ;;
      6)  module_pomodoro ;;
      7)  module_log ;;
      8)  module_scratch ;;
      9)  module_sysadmin ;;
    esac
  done

  tput rmcup 2>/dev/null || true
  printf '%sbye%s\n' "${C_DIM}" "${C_RESET}"
}

# Clear trap on clean exit so failure_handler doesn't trigger
trap - ERR
main "$@"
