#!/bin/sh
# whisper - send messages via transport (telegram)
#
# Exit codes:
#   0  success (message sent, or --help)
#   1  usage error     (bad/missing arguments, unknown option, unknown transport)
#   2  dependency error (required external command not found)
#   3  config error    (required environment variable not set)
#   4  send error      (transport request failed or API rejected the message)
#   5  rate limited    (called too soon; retry after RATE_LIMIT_MS ms)
#   6  template error  (template not found, missing params, or file read failure)
set -e

die() {
    printf 'whisper: %s\n' "$1" >&2
    exit "${2:-1}"
}

usage() {
    printf 'Usage: whisper -m MESSAGE [--via TRANSPORT]\n'
    printf '       whisper -t TEMPLATE [-p VALUE ...] [--via TRANSPORT]\n\n'
    printf '  -m, --message    Message to send\n'
    printf '  -t, --template   Use a named template from ~/.config/whisper/templates/\n'
    printf '  -p               Positional params for template ({1}, {2}, ...)\n'
    printf '                   Prefix with @ to inject file contents\n'
    printf '  --via, --transport, -T\n'
    printf '                   Transport to use (default: telegram)\n'
    printf '  -h, --help       Show this help\n'
    printf '\nExamples:\n'
    printf '  whisper -m "Server is down"\n'
    printf '  whisper -t deploy -p "myapp" "v1.2.3" "success"\n'
    printf '  whisper -t alert -p "disk full" "web-01" @/tmp/df-output.txt\n'
    exit 0
}

# --- template engine ---
TMPL_DIR="$HOME/.config/whisper/templates"

resolve_template() {
    _tmpl_path="$TMPL_DIR/$TEMPLATE"
    [ -f "$_tmpl_path" ] || die "template not found: $TEMPLATE (looked in $TMPL_DIR/)" 6

    _content=$(cat "$_tmpl_path")

    # Resolve @file references in positional params
    _i=1
    while [ "$_i" -le "$PARAM_COUNT" ]; do
        eval "_val=\$_P${_i}"
        case "$_val" in
            @*)
                _fpath="${_val#@}"
                [ -f "$_fpath" ] || die "file not found for param ${_i}: $_fpath" 6
                eval "_P${_i}=\$(cat \"\$_fpath\")"
                ;;
        esac
        _i=$((_i + 1))
    done

    # Substitute {N} placeholders (awk handles special chars safely)
    _i=1
    while [ "$_i" -le "$PARAM_COUNT" ]; do
        eval "_val=\$_P${_i}"
        _placeholder="{${_i}}"
        _content=$(printf '%s\n' "$_content" | _WHISPER_REP="$_val" awk \
            -v pat="$_placeholder" \
            'BEGIN {
                rep = ENVIRON["_WHISPER_REP"]
                # Escape braces so pat is treated as literal, not regex quantifier
                gsub(/\{/, "\\{", pat)
                gsub(/\}/, "\\}", pat)
                gsub(/\\/, "\\\\", rep)
                gsub(/&/, "\\&", rep)
            }
            {
                gsub(pat, rep)
                printf "%s%s", sep, $0
                sep = "\n"
            }')
        _i=$((_i + 1))
    done

    # Check for unfilled placeholders
    case "$_content" in
        *"{"[0-9]*"}"*)
            die "template '$TEMPLATE' has unfilled placeholders (got $PARAM_COUNT param(s))" 6 ;;
    esac

    MESSAGE="$_content"
}

# --- config loading ---
# source .env files if they exist (later file wins, env vars win over both)
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
for _envfile in /etc/whisper/.env "$HOME/.config/whisper/.env" "$SCRIPT_DIR/.env"; do
    [ -f "$_envfile" ] && . "$_envfile"
done
unset _envfile

# --- dependency check ---
command -v curl >/dev/null 2>&1 || die "curl is required but not found" 2

# --- rate limiter ---
RATE_LIMIT_MS="${RATE_LIMIT_MS:-1000}"
_RATE_STATE_FILE="$HOME/.config/whisper/.last_send"

_now_ms() {
    # GNU date supports %s%3N; macOS date does not (%3N prints literally)
    _t=$(date +%s%3N 2>/dev/null)
    case "$_t" in
        *N) # macOS fallback via python3
            python3 -c "import time; print(int(time.time()*1000))" 2>/dev/null \
                || expr "$(date +%s)" \* 1000 ;;
        *)  printf '%s\n' "$_t" ;;
    esac
}

_enforce_rate_limit() {
    [ "$RATE_LIMIT_MS" -le 0 ] 2>/dev/null && return 0
    if [ -f "$_RATE_STATE_FILE" ]; then
        _last=$(cat "$_RATE_STATE_FILE" 2>/dev/null || printf '0')
        _now=$(_now_ms)
        _elapsed=$(( _now - _last ))
        if [ "$_elapsed" -lt "$RATE_LIMIT_MS" ]; then
            _remaining=$(( RATE_LIMIT_MS - _elapsed ))
            printf 'whisper: rate limited — retry in %s ms\n' "$_remaining" >&2
            exit 5
        fi
    fi
    mkdir -p "$(dirname "$_RATE_STATE_FILE")"
    _now_ms > "$_RATE_STATE_FILE"
}

# --- arg parsing ---
TRANSPORT="telegram"
MESSAGE=""
TEMPLATE=""
PARAM_COUNT=0

while [ $# -gt 0 ]; do
    case "$1" in
        -m|--message)
            [ -n "${2+x}" ] || die "missing value for $1" 1
            MESSAGE="$2"; shift 2 ;;
        -t|--template)
            [ -n "${2+x}" ] || die "missing value for $1" 1
            TEMPLATE="$2"; shift 2 ;;
        --via|--transport|-T)
            [ -n "${2+x}" ] || die "missing value for $1" 1
            TRANSPORT="$2"; shift 2 ;;
        -p)
            shift
            while [ $# -gt 0 ]; do
                case "$1" in
                    -*) break ;;
                    *)
                        PARAM_COUNT=$((PARAM_COUNT + 1))
                        eval "_P${PARAM_COUNT}=\$1"
                        shift ;;
                esac
            done
            ;;
        -h|--help)
            usage ;;
        *)
            die "unknown option: $1" 1 ;;
    esac
done

if [ -n "$MESSAGE" ] && [ -n "$TEMPLATE" ]; then
    die "cannot use both --message and --template" 1
fi
if [ -z "$MESSAGE" ] && [ -z "$TEMPLATE" ]; then
    die "either --message or --template is required" 1
fi
if [ "$PARAM_COUNT" -gt 0 ] && [ -z "$TEMPLATE" ]; then
    die "-p can only be used with --template" 1
fi

# --- env validation ---
validate_telegram_env() {
    [ -n "$TELEGRAM_BOT_TOKEN" ] || die "TELEGRAM_BOT_TOKEN is not set" 3
    [ -n "$TELEGRAM_CHAT_ID" ]   || die "TELEGRAM_CHAT_ID is not set" 3
}

# --- transports ---
send_telegram() {
    validate_telegram_env
    local url="https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage"

    local response
    response=$(curl -s -X POST "$url" \
        -d chat_id="$TELEGRAM_CHAT_ID" \
        --data-urlencode text="$MESSAGE" \
        -d parse_mode="HTML") || die "curl request failed" 4

    if echo "$response" | grep -q '"ok":true'; then
        printf 'whisper: message sent\n' >&2
        return 0
    else
        local err
        err=$(echo "$response" | sed -n 's/.*"description":"\([^"]*\)".*/\1/p')
        printf 'whisper: send failed: %s\n' "${err:-unknown error}" >&2
        exit 4
    fi
}

# --- resolve template if used ---
if [ -n "$TEMPLATE" ]; then
    resolve_template
fi

# --- dispatch ---
_enforce_rate_limit
case "$TRANSPORT" in
    telegram) send_telegram ;;
    *)        die "unknown transport: $TRANSPORT" 1 ;;
esac
