#!/bin/bash

# Receiver-side helpers: invoke peer-shell over SSH and consume its output.
# These run on the local instance (the one initiating the pull) and connect
# OUT to a direct-ssh-direct peer using the keypair from peerKeyEnsure.

# Inner: SSH options. Strict key check is on; the peer's host key gets
# learned/stored in ~/.ssh/known_hosts on first connection. Future hardening
# could bind to the fingerprint we stored at pairing time.
_peerSshOpts()
{
    local priv; priv="${HOME}/.ssh/libreportal-peer"
    echo "-i" "$priv" "-o" "BatchMode=yes" "-o" "StrictHostKeyChecking=accept-new" \
         "-o" "PasswordAuthentication=no" "-o" "ConnectTimeout=10"
}

# Read a peer's connection config from its peers row.
# Echos: "<user>@<host> <port>" or empty on miss.
_peerSshTarget()
{
    local peer_name="$1"
    local row; row=$(peerGet "$peer_name")
    [[ -z "$row" || "$row" == "null" ]] && return 1
    local host port user
    host=$(printf '%s' "$row" | grep -o '"host":"[^"]*"' | head -1 | cut -d'"' -f4)
    port=$(printf '%s' "$row" | grep -o '"port":[0-9]*' | head -1 | cut -d':' -f2)
    user=$(printf '%s' "$row" | grep -o '"user":"[^"]*"' | head -1 | cut -d'"' -f4)
    [[ -z "$host" || -z "$user" ]] && return 1
    [[ -z "$port" ]] && port=22
    printf '%s@%s %s\n' "$user" "$host" "$port"
}

# peerExec <peer-name> <verb> [args...]
# Runs `peer-shell <verb> <args>` on the peer over SSH and prints its output.
# Returns the SSH exit status.
peerExec()
{
    local peer_name="$1"; shift
    local target; target=$(_peerSshTarget "$peer_name") || {
        isError "Peer '$peer_name' has no SSH connection config"
        return 1
    }
    local user_host port
    read -r user_host port <<< "$target"

    # The remote side has the forced-command in authorized_keys, so what we
    # type after `ssh user@host` is ignored as a command and passed as
    # $SSH_ORIGINAL_COMMAND. Stringify the verb + args.
    local remote_cmd="$*"
    ssh $(_peerSshOpts) -p "$port" "$user_host" "$remote_cmd"
}

# Probe a peer by calling its peer-shell ping verb. Updates the peers row
# status + last_seen. Echos the new status.
peerPing()
{
    local peer_name="$1"
    local out
    out=$(peerExec "$peer_name" ping 2>&1)
    local rc=$?
    local status now
    now=$(date -Iseconds)
    if (( rc == 0 )) && [[ "$out" == *'"ok":true'* ]]; then
        status="ok"
    elif (( rc != 0 )); then
        status="unreachable"
    else
        status="protocol-error"
    fi
    sqlite3 "$(_peerDb)" \
        "UPDATE peers SET status='$(peerSqlEscape "$status")', last_seen='$now' WHERE name='$(peerSqlEscape "$peer_name")';" \
        2>/dev/null
    echo "$status"
    [[ "$status" == "ok" ]]
}

# Fetch the peer's app list. Just relays the JSON peer-shell emits.
peerListAppsRemote()
{
    local peer_name="$1"
    peerExec "$peer_name" list-apps
}
