#!/usr/bin/env bash
#
# etlworks-api.sh — Bash starter for the Etlworks REST API
#
# A small wrapper around curl. Edit it freely: it is a starting point, not a
# package. Reference: https://etlworks.com/dev/api/
#
# Authentication, in order of precedence:
#   ETLWORKS_API_KEY   an API key (Settings > Users > edit user > API key)
#   ETLWORKS_TOKEN     a JWT you already have
#   ETLWORKS_LOGIN and ETLWORKS_PASSWORD
#                      exchanged for a JWT via POST /v1/token-auth/issue
#                      (accounts with two-factor authentication need an API key)
# ETLWORKS_URL is the instance, for example https://app.etlworks.com
#
# Usage:
#   export ETLWORKS_URL="https://app.etlworks.com"
#   export ETLWORKS_API_KEY="your-api-key"
#
#   ./etlworks-api.sh flows                              # id and name of every flow
#   ./etlworks-api.sh run 123 '{"date":"2026-10-01"}' --wait
#   ./etlworks-api.sh run "Load orders" --wait            # by name
#   ./etlworks-api.sh status 123 4567                     # one execution
#   ./etlworks-api.sh log 123 4567                        # its log
#   ./etlworks-api.sh stop 123 4567
#   ./etlworks-api.sh history 123
#   ./etlworks-api.sh cli "list-flows select id, name limit 5"
#   ./etlworks-api.sh request GET /v1/schedules           # anything else
#   ./etlworks-api.sh request POST /v1/flows/search '{"query":"orders"}'
#   ./etlworks-api.sh login                               # print a JWT
#
# `run --wait` exits 0 for success or warning and 1 for error or canceled,
# so it can gate a CI step. ETLWORKS_POLL_SECONDS sets the poll interval (5).
#
# Requirements: curl, jq
#

set -euo pipefail

BASE_URL="${ETLWORKS_URL:-http://localhost:8080}"
BASE_URL="${BASE_URL%/}"
BASE_URL="${BASE_URL%/rest}"
API="${BASE_URL}/rest"
POLL="${ETLWORKS_POLL_SECONDS:-5}"

die() { echo "error: $*" >&2; exit 1; }
command -v curl >/dev/null || die "curl is required"
command -v jq >/dev/null || die "jq is required (https://jqlang.github.io/jq/)"

# call METHOD PATH [JSON_BODY] — prints the response body, fails on HTTP >= 400
call() {
    local method="$1" path="$2" body="${3:-}" out code
    local args=(-sS -X "$method" -H "Accept: application/json, text/plain, */*" -w $'\n%{http_code}')
    [ -n "${TOKEN:-}" ] && args+=(-H "Authorization: Bearer ${TOKEN}")
    [ -n "$body" ] && args+=(-H "Content-Type: application/json" --data "$body")
    out=$(curl "${args[@]}" "${API}${path}") || die "$method $path: request failed"
    code="${out##*$'\n'}"
    out="${out%$'\n'*}"
    if [ "$code" -ge 400 ]; then
        die "$method $path -> HTTP $code: ${out:-(empty body)}"
    fi
    printf '%s' "$out"
}

issue_token() {
    [ -n "${ETLWORKS_LOGIN:-}" ] && [ -n "${ETLWORKS_PASSWORD:-}" ] \
        || die "set ETLWORKS_API_KEY, ETLWORKS_TOKEN, or ETLWORKS_LOGIN and ETLWORKS_PASSWORD"
    local body token
    body=$(jq -n --arg l "$ETLWORKS_LOGIN" --arg p "$ETLWORKS_PASSWORD" '{login:$l, password:$p}')
    token=$(TOKEN="" call POST /v1/token-auth/issue "$body" | jq -r '.token // empty')
    [ -n "$token" ] || die "no token returned; accounts with two-factor authentication need an API key"
    printf '%s' "$token"
}

urlencode() { jq -rn --arg v "$1" '$v|@uri'; }

run_path() {
    if [[ "$1" =~ ^[0-9]+$ ]]; then
        printf '/v1/flows/%s/run' "$1"
    else
        printf '/v1/flows/flow-name/%s/run' "$(urlencode "$1")"
    fi
}

cmd="${1:-help}"; shift || true

if [ "$cmd" = "help" ] || [ "$cmd" = "-h" ] || [ "$cmd" = "--help" ]; then
    sed -n '2,36p' "$0" | sed 's/^# \{0,1\}//'
    exit 0
fi

if [ "$cmd" = "login" ]; then
    issue_token; echo
    exit 0
fi

TOKEN="${ETLWORKS_API_KEY:-${ETLWORKS_TOKEN:-}}"
[ -n "$TOKEN" ] || TOKEN=$(issue_token)

case "$cmd" in
    flows)
        call GET /v1/flows | jq -r '.[] | "\(.id)\t\(.name)"'
        ;;
    run)
        [ $# -ge 1 ] || die "usage: run <flowId|name> [params-json] [--wait]"
        flow="$1"; shift
        wait=false; params='{}'
        for a in "$@"; do
            if [ "$a" = "--wait" ]; then wait=true; else params="$a"; fi
        done
        resp=$(call POST "$(run_path "$flow")" "$params")
        status=$(jq -r '.status' <<<"$resp")
        if [ "$status" != "0" ] && [ "$status" != "10" ]; then
            echo "$resp" | jq .; exit 1
        fi
        flow_id=$(jq -r '.flowId' <<<"$resp"); audit_id=$(jq -r '.auditId' <<<"$resp")
        echo "started: flowId=$flow_id auditId=$audit_id" >&2
        if ! $wait; then echo "$resp" | jq .; exit 0; fi
        while :; do
            state=$(call GET "/v1/executions/${flow_id}?auditId=${audit_id}" | jq -r '.status // empty')
            case "$state" in
                success|warning) echo "finished: $state" >&2; exit 0 ;;
                error|canceled)
                    echo "finished: $state" >&2
                    { call GET "/v1/executions/${flow_id}/audit/${audit_id}/console"; echo; } >&2 || true
                    exit 1 ;;
            esac
            sleep "$POLL"
        done
        ;;
    status)
        [ $# -eq 2 ] || die "usage: status <flowId> <auditId>"
        call GET "/v1/executions/$1?auditId=$2" | jq .
        ;;
    log)
        [ $# -eq 2 ] || die "usage: log <flowId> <auditId>"
        call GET "/v1/executions/$1/audit/$2/console"; echo
        ;;
    stop)
        [ $# -ge 1 ] || die "usage: stop <flowId> [auditId]"
        call POST "/v1/flows/$1/stop${2:+?auditId=$2}" | jq .
        ;;
    history)
        [ $# -eq 1 ] || die "usage: history <flowId>"
        call GET "/v1/executions/$1/history" | jq .
        ;;
    cli)
        [ $# -ge 1 ] || die "usage: cli \"<command>\""
        body=$(jq -n --arg c "$1" '{command:$c}')
        resp=$(call POST /v1/cli "$body")
        jq -r '.[] | select(.stdout != null and .stdout != "") | .stdout' <<<"$resp"
        jq -r '.[] | select(.stderr != null and .stderr != "") | .stderr' <<<"$resp" >&2
        exit "$(jq '[.[].exitCode // 0] | max // 0' <<<"$resp")"
        ;;
    request)
        [ $# -ge 2 ] || die "usage: request <METHOD> <path> [json-body]"
        out=$(call "$1" "$2" "${3:-}")
        if jq -e . >/dev/null 2>&1 <<<"$out"; then jq . <<<"$out"; else printf '%s\n' "$out"; fi
        ;;
    *)
        die "unknown command: $cmd (try --help)"
        ;;
esac
