#!/bin/sh
#
# fil - Filature client for SliTaz developers
#
# Filature - SliTaz developer platform
# Copyright (C) 2026 SliTaz GNU/Linux - BSD License, see LICENSE
#
# Forwards fil commands to the Filature server over SSH, so devs never
# type ssh themselves. Messages are written locally in $EDITOR, then
# piped to the server. Same commands as on the server: fil help.
#
# Config: ~/.config/filature/client.conf, sourced, for example:
#   FIL_HOST="pankso@foyer.slitaz.org"
# FIL_HOST set in the environment wins over the file.
#

conf="${XDG_CONFIG_HOME:-$HOME/.config}/filature/client.conf"
scissors="# ------------------------ >8 ------------------------"

env_host="$FIL_HOST"
[ -f "$conf" ] && . "$conf"
[ -n "$env_host" ] && FIL_HOST="$env_host"
FIL_REMOTE="${FIL_REMOTE:-fil}"

if [ -z "$FIL_HOST" ]; then
    cat >&2 << EOT
fil: no server configured. Put your login and the Filature host in
$conf, for example:

    FIL_HOST="$(id -un)@foyer.slitaz.org"

or set FIL_HOST in the environment.
EOT
    exit 1
fi

# One SSH connection for a whole session of fil commands: Foyer drops a
# source that opens more than 10 SSH connections in 5 minutes. OpenSSH
# only, dropbear's dbclient has no connection sharing.
ssh_opts=""
if ssh -V 2>&1 | grep -q OpenSSH; then
    ctl_dir="${conf%/*}"
    mkdir -p -m 700 "$ctl_dir"
    ssh_opts="-o ControlMaster=auto -o ControlPath=$ctl_dir/ssh-%C -o ControlPersist=10m"
fi

# Single-quote each word for the remote shell: arguments reach the
# server exactly as typed, quotes and dollars included.
quote() {
    printf "'%s'" "$(printf '%s' "$1" | sed "s/'/'\\\\''/g")"
}

remote_command() {
    cmd="$FIL_REMOTE"
    for arg in "$@"; do
        cmd="$cmd $(quote "$arg")"
    done
    printf '%s' "$cmd"
}

# Run on the server, stdin not read
run() {
    ssh $ssh_opts -q -n -- "$FIL_HOST" "$(remote_command "$@")"
}

# Run on the server, the message on stdin
run_with_body() {
    ssh $ssh_opts -q -- "$FIL_HOST" "$(remote_command "$@")"
}

# compose CONTEXT_COMMAND...: open $EDITOR on a draft, print the path of
# the message. Everything below the scissors line is help or context
# (the thread being answered) and is cut out.
compose() {
    draft=$(mktemp "${TMPDIR:-/tmp}/fil-draft.XXXXXX") || exit 1
    {
        echo
        echo "$scissors"
        echo "# Write your message above this line, plain text like a mail."
        echo "# Everything from this line on is ignored. An empty message aborts."
        if [ $# -gt 0 ]; then
            echo "#"
            run "$@" 2>&1 | sed 's/^/# /'
        fi
    } > "$draft"
    ${VISUAL:-${EDITOR:-vi}} "$draft" < /dev/tty > /dev/tty
    msg="$draft.msg"
    sed "/^$scissors\$/,\$d" "$draft" > "$msg"
    rm -f "$draft"
    if ! grep -q '[^[:space:]]' "$msg"; then
        rm -f "$msg"
        echo "fil: empty message, nothing sent" >&2
        exit 1
    fi
    printf '%s' "$msg"
}

# send MESSAGE_FILE ARGS...: a failed send keeps the draft
send() {
    msg="$1"
    shift
    if run_with_body "$@" < "$msg"; then
        rm -f "$msg"
    else
        echo "fil: not sent, your message is saved in $msg" >&2
        exit 1
    fi
}

case "$1" in
    new)
        # No title: let the server print the usage, no editor for nothing
        if [ -t 0 ] && [ $# -ge 2 ] && { [ "$2" != -p ] || [ $# -ge 4 ]; }; then
            msg=$(compose) || exit 1
            send "$msg" "$@"
        else
            run_with_body "$@"
        fi ;;
    reply)
        if [ -t 0 ]; then
            [ -n "$2" ] || { run "$@"; exit $?; }
            msg=$(compose read "$2") || exit 1
            send "$msg" "$@"
        else
            run_with_body "$@"
        fi ;;
    read)
        if [ -t 1 ]; then
            run "$@" | ${PAGER:-less}
        else
            run "$@"
        fi ;;
    *)
        run "$@" ;;
esac
