#!/bin/sh
# =============================================================================
# secrets-cli — terminal (ncurses) front-end for `secrets`. Menu-driven access
# to stores, mirrors and templates, so nothing has to be memorised.
#
# Uses `dialog` (or `whiptail`) for the UI — no Python. Every action shells out
# to `secrets` itself, so this front-end reuses exactly the same logic, and the
# PASSPHRASE IS NEVER SEEN BY THIS PROGRAM: for anything that needs it we drop
# out of the menu to the bare terminal and let gpg's own pinentry prompt you.
#
# Usage: secrets-cli          (menu-driven)
#        secrets-cli --help | --version
# =============================================================================
set -eu
VERSION=1.0.0

case "${1:-}" in
  -h|--help) sed -n '3,13p' "$0" | sed 's|^# \{0,1\}||'; exit 0 ;;
  -V|--version) echo "secrets-cli $VERSION"; exit 0 ;;
esac

SECRETS=${SECRETS_BIN:-secrets}
command -v "$SECRETS" >/dev/null 2>&1 || { echo "secrets-cli: '$SECRETS' not found on PATH" >&2; exit 1; }

if   command -v dialog   >/dev/null 2>&1; then DIA=dialog
elif command -v whiptail >/dev/null 2>&1; then DIA=whiptail
else echo "secrets-cli: needs 'dialog' or 'whiptail' (sudo apt install dialog)" >&2; exit 1; fi


# ── Theme (shared with the Tk GUIs via matching palettes) ────────────────────
# dialog(1) reads $DIALOGRC afresh on every invocation, so toggling mid-session
# takes effect on the very next menu -- no restart, no re-exec.
dt_share(){
    for d in "${DARIOTOOLS_SHARE:-}" "$HOME/.local/share/dariotools" \
             /usr/local/share/dariotools /usr/share/dariotools; do
        [ -n "$d" ] && [ -f "$d/dialogrc.dark" ] && { printf '%s' "$d"; return 0; }
    done
    return 1
}
THEMEFILE="${SECRETS_DIR:-$HOME/.secrets}/.tui_theme"
THEME=dark
[ -f "$THEMEFILE" ] && THEME=$(cat "$THEMEFILE" 2>/dev/null)
case "$THEME" in light|dark) ;; *) THEME=dark ;; esac
apply_theme(){
    THEME="$1"
    _sh=$(dt_share) || return 0                 # no themes installed: plain dialog
    DIALOGRC="$_sh/dialogrc.$THEME"; export DIALOGRC
    printf '%s' "$THEME" > "$THEMEFILE" 2>/dev/null || true
    chmod 600 "$THEMEFILE" 2>/dev/null || true
}
toggle_theme(){ [ "$THEME" = dark ] && apply_theme light || apply_theme dark; }
apply_theme "$THEME"

TITLE="secrets $($SECRETS --version 2>/dev/null | awk '{print $2}')"
TMP=$(mktemp); trap 'rm -f "$TMP"' EXIT INT TERM HUP

msg(){ $DIA --title "$TITLE" --msgbox "$1" "${2:-12}" "${3:-72}"; }
ask(){ $DIA --title "$TITLE" --inputbox "$1" 9 66 "${2:-}" 2>"$TMP" && cat "$TMP" || return 1; }
confirm(){ $DIA --title "$TITLE" --yesno "$1" 9 66; }

# Run a command on the REAL terminal: dialog owns the screen, and gpg's pinentry
# plus $EDITOR both need it back. Clear, run, pause, return to the menu.
run_visible(){
    clear
    printf '\033[1;33m>>> %s\033[0m\n\n' "$*"
    set +e; "$@"; rc=$?; set -e
    printf '\n\033[1;33m--- exit %s. Press Enter to return to the menu. ---\033[0m' "$rc"
    read -r _dummy || true
    return 0
}

# Build a dialog menu of stores. Returns the chosen name on stdout.
pick_store(){
    n=0; set --
    for line in $($SECRETS list 2>/dev/null | awk '$2 ~ /^[0-9]+$/ && $3=="bytes" {print $1}'); do
        n=$((n+1)); set -- "$@" "$line" "store"
    done
    [ "$n" -gt 0 ] || { msg "No stores yet.\n\nUse  Stores -> New store  to create one." 9 60; return 1; }
    $DIA --title "$TITLE" --menu "Choose a store:" 16 60 8 "$@" 2>"$TMP" || return 1
    cat "$TMP"
}

pick_template(){
    n=0; set -- "(none - blank sheet)" "start empty"
    # Match on FIELDS, not indentation -- leading-space counts are a detail of
    # the output format and matching them broke silently once already.
    for t in $($SECRETS templates 2>/dev/null | awk '$2 ~ /^[0-9]+$/ && $3=="bytes" {print $1}'); do
        n=$((n+1)); set -- "$@" "$t" "template"
    done
    $DIA --title "$TITLE" --menu "Start from which template?" 16 64 8 "$@" 2>"$TMP" || return 1
    sel=$(cat "$TMP"); [ "$sel" = "(none - blank sheet)" ] && echo "" || echo "$sel"
}

menu_stores(){
    while :; do
        $DIA --title "$TITLE — Stores" --menu "" 17 64 9 \
            view   "View a store (screen only)" \
            edit   "Edit a store (add/change/remove)" \
            new    "New store, from a template" \
            passwd "Change a store's passphrase" \
            remove "Delete a store (mirrors kept)" \
            list   "List stores" \
            back   "Back" 2>"$TMP" || return 0
        case "$(cat "$TMP")" in
            view)   s=$(pick_store) && run_visible "$SECRETS" view "$s" ;;
            edit)   s=$(pick_store) && run_visible "$SECRETS" edit "$s" ;;
            new)    s=$(ask "Name for the new store:") || continue
                    t=$(pick_template) || continue
                    if [ -n "$t" ]; then run_visible "$SECRETS" init "$s" "$t"
                    else run_visible "$SECRETS" init "$s"; fi ;;
            passwd) s=$(pick_store) && run_visible "$SECRETS" passwd "$s" ;;
            remove) s=$(pick_store) || continue
                    confirm "Delete store '$s'?\n\nMirrored copies are NOT removed." \
                      && run_visible "$SECRETS" rm "$s" ;;
            list)   $SECRETS list > "$TMP" 2>&1; $DIA --title "Stores" --textbox "$TMP" 18 74 ;;
            back|*) return 0 ;;
        esac
    done
}

menu_mirrors(){
    while :; do
        $DIA --title "$TITLE — Mirrors" --menu "" 15 70 6 \
            status "Which copies exist, and which passphrase each needs" \
            rekey  "Bring old copies up to the current passphrase" \
            purge  "Shred old copies (only if a passphrase leaked)" \
            back   "Back" 2>"$TMP" || return 0
        case "$(cat "$TMP")" in
            status) s=$(pick_store) || continue
                    $SECRETS mirrors "$s" > "$TMP" 2>&1
                    $DIA --title "Mirrors — $s" --textbox "$TMP" 20 92 ;;
            rekey)  s=$(pick_store) && run_visible "$SECRETS" rekey "$s" ;;
            purge)  s=$(pick_store) || continue
                    confirm "SHRED every mirrored copy of '$s' from older passphrases?\n\nPrefer Rekey unless the old passphrase leaked." \
                      && run_visible "$SECRETS" purge "$s" ;;
            back|*) return 0 ;;
        esac
    done
}

menu_templates(){
    while :; do
        $DIA --title "$TITLE — Templates" --menu "Templates hold no secrets." 16 64 7 \
            list "List templates" \
            new  "New template" \
            edit "Edit a template" \
            copy "Copy a template" \
            rm   "Delete a template" \
            back "Back" 2>"$TMP" || return 0
        case "$(cat "$TMP")" in
            list) $SECRETS templates > "$TMP" 2>&1; $DIA --title "Templates" --textbox "$TMP" 18 78 ;;
            new)  t=$(ask "Name for the new template:") && run_visible "$SECRETS" template new "$t" ;;
            edit) t=$(ask "Template to edit:") && run_visible "$SECRETS" template edit "$t" ;;
            copy) a=$(ask "Copy FROM:") || continue; b=$(ask "Copy TO:") || continue
                  run_visible "$SECRETS" template copy "$a" "$b" ;;
            rm)   t=$(ask "Template to delete:") && run_visible "$SECRETS" template rm "$t" ;;
            back|*) return 0 ;;
        esac
    done
}

menu_tools(){
    while :; do
        $DIA --title "$TITLE — Tools" --menu "" 15 70 6 \
            scan    "Find credential files loose on the disks" \
            archive "Encrypt a file or folder" \
            restore "Unpack an encrypted archive" \
            back    "Back" 2>"$TMP" || return 0
        case "$(cat "$TMP")" in
            scan)    $SECRETS scan > "$TMP" 2>&1; $DIA --title "Loose credential files" --textbox "$TMP" 22 100 ;;
            archive) p=$(ask "Path to encrypt:") && run_visible "$SECRETS" archive "$p" ;;
            restore) f=$(ask "Archive (.tar.gz.gpg):") || continue
                     d=$(ask "Restore into which directory:") || continue
                     run_visible "$SECRETS" restore "$f" "$d" ;;
            back|*)  return 0 ;;
        esac
    done
}

while :; do
    $DIA --title "$TITLE" --menu "Encrypted credential sheets." 17 62 7 \
        stores    "Stores — view, edit, create, passphrase" \
        mirrors   "Mirrors — copies and passphrase generations" \
        templates "Templates — the forms stores start from" \
        tools     "Tools — scan, archive, restore" \
        theme     "Colour theme (now: $THEME)" \
        help      "Manual" \
        quit      "Quit" 2>"$TMP" || { clear; exit 0; }
    case "$(cat "$TMP")" in
        stores)    menu_stores ;;
        mirrors)   menu_mirrors ;;
        templates) menu_templates ;;
        tools)     menu_tools ;;
        theme)     toggle_theme ;;
        help)      run_visible man secrets ;;
        quit|*)    clear; exit 0 ;;
    esac
done
