#!/usr/bin/env bash
# cmd — script launcher for cmd.mle.sh
#
# Usage:
#   curl -fsSL https://cmd.mle.sh | bash                                 interactive menu
#   curl -fsSL https://cmd.mle.sh | bash -s -- <name> [args...]          run by name
#   curl -fsSL https://cmd.mle.sh | bash -s -- --list                    print index, no menu
#   curl -fsSL https://cmd.mle.sh | bash -s -- --get <name>              download to ./<name>.sh
#   curl -fsSL https://cmd.mle.sh | bash -s -- --help
#
# Source: https://git.mle.sh/${CMD_OWNER:-matthias}/cmd

set -euo pipefail

REPO_HOST="${CMD_HOST:-git.mle.sh}"
REPO_OWNER="${CMD_OWNER:-matthias}"
REPO_NAME="${CMD_REPO:-cmd}"
REPO_BRANCH="${CMD_BRANCH:-main}"
PARALLEL="${CMD_PARALLEL:-8}"

API="https://${REPO_HOST}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}"
RAW="https://${REPO_HOST}/${REPO_OWNER}/${REPO_NAME}/raw/branch/${REPO_BRANCH}"

err()  { printf 'cmd: %s\n' "$*" >&2; }
die()  { err "$*"; exit 1; }
need() { command -v "$1" >/dev/null 2>&1 || die "missing dependency: $1"; }

need curl
need jq

# stdin is the launcher source when invoked as `curl … | bash`,
# so any interactive prompts must read from /dev/tty directly.
if [ -e /dev/tty ]; then
    HAS_TTY=1
else
    HAS_TTY=0
fi

read_tty() {
    [ "$HAS_TTY" -eq 1 ] || die "interactive selection requires a TTY"
    IFS= read -r "$1" </dev/tty
}

fetch_tree() {
    local sha
    sha=$(curl -fsSL "${API}/branches/${REPO_BRANCH}" | jq -r '.commit.id') \
        || die "could not reach ${API}/branches/${REPO_BRANCH}"
    [ -n "$sha" ] && [ "$sha" != "null" ] || die "could not resolve branch ${REPO_BRANCH}"
    curl -fsSL "${API}/git/trees/${sha}?recursive=true" \
        | jq -r '.tree[] | select(.type=="blob") | .path' \
        | grep -E '^scripts/.+\.sh$' || true
}

# Fetch a single script's header (first 25 lines) and emit one TSV row.
# Exported for xargs -P parallelism.
_fetch_header() {
    local path="$1"
    local hdr name desc tags interactive args
    hdr=$(curl -fsSL "${RAW}/${path}" 2>/dev/null | head -25) || return 0
    name="${path##*/}"; name="${name%.sh}"
    desc=$(grep -m1        '^#[[:space:]]*cmd:desc'        <<<"$hdr" | sed -E 's/^#[[:space:]]*cmd:desc[[:space:]]+//' || true)
    tags=$(grep -m1        '^#[[:space:]]*cmd:tags'        <<<"$hdr" | sed -E 's/^#[[:space:]]*cmd:tags[[:space:]]+//' || true)
    interactive=$(grep -m1 '^#[[:space:]]*cmd:interactive' <<<"$hdr" | sed -E 's/^#[[:space:]]*cmd:interactive[[:space:]]+//' || true)
    args=$(grep -m1        '^#[[:space:]]*cmd:args'        <<<"$hdr" | sed -E 's/^#[[:space:]]*cmd:args[[:space:]]+//' || true)
    printf '%s\t%s\t%s\t%s\t%s\t%s\n' "$name" "$path" "${desc:-}" "${tags:-}" "${interactive:-}" "${args:-}"
}
export -f _fetch_header

build_index() {
    local paths
    paths=$(fetch_tree)
    [ -n "$paths" ] || die "no scripts found in ${REPO_OWNER}/${REPO_NAME}@${REPO_BRANCH}:scripts/"
    export RAW
    printf '%s\n' "$paths" | xargs -I{} -P"$PARALLEL" bash -c '_fetch_header "$@"' _ {} | sort
}

resolve_path() {
    awk -F'\t' -v n="$1" '$1==n {print $2; exit}' "$INDEX"
}

resolve_meta() {
    awk -F'\t' -v n="$1" '$1==n {print $5"|"$6; exit}' "$INDEX"
}

run_script() {
    local name="$1"; shift
    local path tmp shebang interp meta interactive
    path=$(resolve_path "$name")
    [ -n "$path" ] || die "unknown script: $name"

    meta=$(resolve_meta "$name")
    interactive="${meta%%|*}"

    tmp=$(mktemp -t "cmd.${name}.XXXXXX.sh")
    # shellcheck disable=SC2064
    trap "rm -f '$tmp'" RETURN
    curl -fsSL "${RAW}/${path}" -o "$tmp"
    chmod +x "$tmp"

    shebang=$(head -n1 "$tmp")
    case "$shebang" in
        *bash*) interp=bash ;;
        *sh*)   interp=sh   ;;
        *)      interp=bash ;;
    esac

    # Give the script a real stdin: TTY for interactive scripts so prompts work,
    # /dev/null otherwise so accidental `read`s don't hang on the launcher source.
    if [ "$interactive" = "yes" ] && [ "$HAS_TTY" -eq 1 ]; then
        "$interp" "$tmp" "$@" </dev/tty
    else
        "$interp" "$tmp" "$@" </dev/null
    fi
}

download_script() {
    local name="$1" path out
    path=$(resolve_path "$name")
    [ -n "$path" ] || die "unknown script: $name"
    out="./${name}.sh"
    curl -fsSL "${RAW}/${path}" -o "$out"
    chmod +x "$out"
    echo "saved: $out"
}

print_list() {
    awk -F'\t' '{ printf "%-24s  %s\n", $1, $3 }' "$INDEX"
}

render_menu() {
    local current="" group line_no=0
    : > "$MENU_MAP"
    while IFS=$'\t' read -r name path desc tags interactive args; do
        group="${path#scripts/}"
        if [[ "$group" == */* ]]; then
            group="${group%/*}"
        else
            group="(top)"
        fi
        if [ "$group" != "$current" ]; then
            printf '\n%s\n' "$group" >&2
            current="$group"
        fi
        line_no=$((line_no+1))
        printf '  %2d) %-22s %s\n' "$line_no" "$name" "${desc:-}" >&2
        printf '%d\t%s\n' "$line_no" "$name" >>"$MENU_MAP"
    done < "$INDEX"
}

interactive_menu() {
    while :; do
        render_menu
        printf '\nselect [number | name | /<filter> | d <name> | q]: ' >&2
        local choice
        read_tty choice
        case "$choice" in
            ""|q|quit|exit) return 0 ;;
            d\ *)   download_script "${choice#d }"; return 0 ;;
            /*)
                local needle="${choice#/}"
                grep -i -- "$needle" "$INDEX" > "${INDEX}.filtered" || true
                if [ ! -s "${INDEX}.filtered" ]; then
                    err "no matches for '$needle'"
                    rm -f "${INDEX}.filtered"
                    continue
                fi
                mv "${INDEX}.filtered" "$INDEX"
                continue
                ;;
            [0-9]*)
                local picked
                picked=$(awk -F'\t' -v n="$choice" '$1==n {print $2; exit}' "$MENU_MAP")
                [ -n "$picked" ] || { err "invalid selection: $choice"; continue; }
                run_script "$picked"
                return 0
                ;;
            *)
                run_script "$choice"
                return 0
                ;;
        esac
    done
}

usage() {
    sed -n '2,11p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
}

INDEX=$(mktemp -t cmd.index.XXXXXX)
MENU_MAP=$(mktemp -t cmd.menu.XXXXXX)
trap 'rm -f "$INDEX" "$MENU_MAP"' EXIT

case "${1:-}" in
    -h|--help)
        usage
        ;;
    --list)
        build_index > "$INDEX"
        print_list
        ;;
    --get)
        shift
        [ $# -gt 0 ] || die "usage: --get <name>"
        build_index > "$INDEX"
        download_script "$1"
        ;;
    "")
        printf 'cmd.mle.sh — fetching index from %s/%s@%s ...\n' \
            "$REPO_OWNER" "$REPO_NAME" "$REPO_BRANCH" >&2
        build_index > "$INDEX"
        interactive_menu
        ;;
    -*)
        die "unknown option: $1 (try --help)"
        ;;
    *)
        name="$1"; shift
        build_index > "$INDEX"
        run_script "$name" "$@"
        ;;
esac
