#!/bin/sh
# nanook installer — curl-pipeable.
#
#   curl -fsSL https://nanook.cstef.dev/install.sh | sh
#
# Env (all optional):
#   NANOOK_VERSION       release tag (default: latest)
#   NANOOK_INSTALL_DIR   target dir (default: walks $PATH like `nanook install`)
#   NANOOK_OS, NANOOK_ARCH    override platform detection
#   NANOOK_SKIP_VERIFY=1     skip sha256 verification
#   NANOOK_NO_TRUST=1        skip release-key trust bootstrap
#   NO_COLOR=1              disable color
#
# Flags: --version <tag> | --dir <path> | --skip-verify | --no-trust | --help

set -eu

REPO="cstef/nanook"
BASE="https://codeberg.org/$REPO"
BIN=nanook

# ── style ────────────────────────────────────────────────────────────────
if [ -t 1 ] && [ "${NO_COLOR:-}" = "" ] && [ "${TERM:-}" != "dumb" ]; then
    R=$(printf '\033[31m'); G=$(printf '\033[32m'); Y=$(printf '\033[33m')
    B=$(printf '\033[34m'); C=$(printf '\033[36m'); D=$(printf '\033[2m')
    BOLD=$(printf '\033[1m'); X=$(printf '\033[0m')
else
    R= G= Y= B= C= D= BOLD= X=
fi

step() { printf '%s::%s %s\n' "$C" "$X" "$1" >&2; }
ok()   { printf '%s ok%s   %s\n' "$G" "$X" "$1" >&2; }
warn() { printf '%s warn%s %s\n' "$Y" "$X" "$1" >&2; }
die()  { printf '%s err%s  %s\n' "$R" "$X" "$1" >&2; exit 1; }
have() { command -v "$1" >/dev/null 2>&1; }

usage() {
    cat <<EOF
${BOLD}nanook installer${X}

  ${D}\$${X} ${C}curl -fsSL${X} https://nanook.cstef.dev/install.sh ${C}| sh${X}

${BOLD}Options${X}
  --version ${D}<tag>${X}   pin a release (default: latest)
  --dir ${D}<path>${X}      install directory
  --skip-verify      skip sha256 check
  --no-trust         skip release-key trust bootstrap
  --help, -h         show this

${BOLD}Env${X}
  NANOOK_VERSION, NANOOK_INSTALL_DIR, NANOOK_OS, NANOOK_ARCH,
  NANOOK_SKIP_VERIFY, NANOOK_NO_TRUST, NO_COLOR
EOF
}

# ── args ─────────────────────────────────────────────────────────────────
VERSION="${NANOOK_VERSION:-}"
DIR="${NANOOK_INSTALL_DIR:-}"
SKIP="${NANOOK_SKIP_VERIFY:-}"
NO_TRUST="${NANOOK_NO_TRUST:-}"

while [ $# -gt 0 ]; do
    case "$1" in
        --version) VERSION="$2"; shift 2 ;;
        --dir)     DIR="$2"; shift 2 ;;
        --skip-verify) SKIP=1; shift ;;
        --no-trust)    NO_TRUST=1; shift ;;
        --help|-h) usage; exit 0 ;;
        *) die "unknown arg: $1" ;;
    esac
done

# ── platform ─────────────────────────────────────────────────────────────
OS="${NANOOK_OS:-$(uname -s)}"
ARCH="${NANOOK_ARCH:-$(uname -m)}"
case "$OS" in   Linux|linux) OS=linux ;; *) die "unsupported OS: $OS" ;; esac
case "$ARCH" in x86_64|amd64) ARCH=x86_64 ;;
                aarch64|arm64) ARCH=aarch64 ;;
                *) die "unsupported arch: $ARCH" ;; esac

# ── tools ────────────────────────────────────────────────────────────────
if have curl;   then DL='curl -fsSL -o'
elif have wget; then DL='wget -qO'
else die "need curl or wget"
fi
have tar || die "need tar"

# ── install dir picker (mirrors `nanook install`) ─────────────────────────
writable() {
    p="$1/.nanook-probe-$$"
    if (set -C; : > "$p") 2>/dev/null; then rm -f "$p"; return 0; fi
    return 1
}
pick_dir() {
    user= sys= has_sys=0
    IFS=:
    for d in $PATH; do
        [ -d "$d" ] || continue
        case "$d" in
            "$HOME"|"$HOME"/*)
                [ -z "$user" ] && writable "$d" && user="$d" ;;
            *)
                has_sys=1
                [ -z "$sys" ] && writable "$d" && sys="$d" ;;
        esac
    done
    unset IFS
    if   [ -n "$user" ];                       then printf '%s' "$user"
    elif [ -n "$sys" ];                        then printf '%s' "$sys"
    elif [ "$has_sys" = 1 ] && have sudo;      then printf '%s' /usr/local/bin
    else                                            printf '%s' "$HOME/.local/bin"
    fi
}
[ -z "$DIR" ] && DIR=$(pick_dir)

# ── url ──────────────────────────────────────────────────────────────────
asset="$BIN-$OS-$ARCH.tar.gz"
if [ -z "$VERSION" ]; then
    # Forgejo/codeberg doesn't route `releases/latest/download/<asset>` the
    # way GitHub does. The redirect on `/releases/latest` itself does work
    # (303 → /releases/tag/<vX.Y.Z>), so resolve the tag first and use the
    # per-tag URL.
    if have curl; then
        LOC=$(curl -fsSI "$BASE/releases/latest" | tr -d '\r' | awk -F'tag/' 'tolower($1) ~ /location:/ {print $2; exit}')
    else
        LOC=$(wget -qS --max-redirect=0 -O /dev/null "$BASE/releases/latest" 2>&1 | awk -F'tag/' 'tolower($1) ~ /location:/ {print $2; exit}')
    fi
    [ -n "$LOC" ] || die "could not resolve latest release tag from $BASE/releases/latest"
    TAG="$LOC"
    URL="$BASE/releases/download/$TAG/$asset"
else
    case "$VERSION" in v*) ;; *) VERSION="v$VERSION" ;; esac
    URL="$BASE/releases/download/$VERSION/$asset"
    TAG="$VERSION"
fi

# ── download + verify + install ──────────────────────────────────────────
TMP=$(mktemp -d 2>/dev/null || mktemp -d -t nanook)
trap 'rm -rf "$TMP"' EXIT INT TERM

step "$BOLD$BIN$X $TAG $D($OS/$ARCH)$X"
$DL "$TMP/$asset" "$URL" || die "download failed: $URL"

if [ "$SKIP" = 1 ]; then
    warn "sha256 check skipped"
else
    if $DL "$TMP/$asset.sha256" "$URL.sha256" 2>/dev/null; then
        (cd "$TMP" && {
            if have sha256sum;     then sha256sum -c "$asset.sha256"
            elif have shasum;      then shasum -a 256 -c "$asset.sha256"
            else die "no sha256sum or shasum found"; fi
        } >/dev/null 2>&1) || die "sha256 mismatch"
        ok "sha256 verified"
    else
        warn "no .sha256 published — skipping verification"
    fi
fi

tar -xzf "$TMP/$asset" -C "$TMP" || die "extract failed"
src="$TMP/$BIN"
[ -f "$src" ] || src=$(find "$TMP" -name "$BIN" -type f | head -n1)
[ -n "$src" ] && [ -f "$src" ] || die "$BIN not in archive"

mkdir -p "$DIR"
dst="$DIR/$BIN"

if [ -w "$DIR" ]; then
    install -m 0755 "$src" "$dst"
elif have sudo; then
    step "${D}sudo install →$X $dst"
    sudo install -m 0755 "$src" "$dst"
else
    die "$DIR not writable and no sudo (set --dir or NANOOK_INSTALL_DIR)"
fi

case ":$PATH:" in
    *":$DIR:"*) ;;
    *) warn "$DIR not in \$PATH" ;;
esac

ver=$("$dst" --version 2>/dev/null || true)
ok "installed $BOLD$dst$X ${D}${ver:+($ver)}$X"

# ── trust bootstrap ──────────────────────────────────────────────────────
# Fetch the canonical release-signing public key from the source tree
# and import it into `[self.signature].signers`. Operators who would
# rather wire trust manually can pass `--no-trust` (or set
# `NANOOK_NO_TRUST=1`); they can run `nanook self trust add <line-or-path>`
# later to opt back in.
#
# The bootstrap is best-effort: failure to fetch or write the key
# warns but does not abort the install. The downloaded binary still
# verified against its `.sha256`; signature trust is the next layer
# up, and operators can add it whenever they like.
if [ "$NO_TRUST" = 1 ]; then
    warn "trust bootstrap skipped (--no-trust)"
else
    KEY_URL="$BASE/raw/branch/main/keys/release.pub"
    KEY_TMP="$TMP/release.pub"
    # Pin the trust list to the global config (~/.nanook/nanook.toml) so
    # the bootstrap is independent of the install.sh cwd. `nanook run`
    # picks the same file up from a service unit's working dir later.
    GLOBAL_CFG="${HOME:-/}/.nanook/nanook.toml"
    if $DL "$KEY_TMP" "$KEY_URL" 2>/dev/null && [ -s "$KEY_TMP" ]; then
        # extract the first non-comment line that looks like ssh-ed25519
        KEY_LINE=$(awk '/^ssh-ed25519 /{print; exit}' "$KEY_TMP" 2>/dev/null || true)
        if [ -n "$KEY_LINE" ]; then
            printf '%s\n' "$KEY_LINE" > "$KEY_TMP"
            if "$dst" -c "$GLOBAL_CFG" self trust add "$KEY_TMP" >/dev/null 2>&1; then
                ok "trusted release key (added to $GLOBAL_CFG)"
            else
                warn "could not import release key. paste it into [self.signature].signers manually:"
                cat "$KEY_TMP" >&2
            fi
        else
            warn "release key not yet published : trust bootstrap is a no-op"
        fi
    else
        warn "release key not published yet at $KEY_URL : skipping trust bootstrap"
    fi
fi
