#!/usr/bin/env bash
set -Eeuo pipefail
umask 077

BASE="${ROUTER_OPS_BASE:-/opt/router-ops}"
STATE_ROOT="${ROUTER_DETACHED_ROOT:-$BASE/state/detached-step-runs}"
SELF="$(readlink -f "$0")"

usage() {
    echo 'Usage: router-detached-step start --step STEP --zip ZIP --sha256 SHA --entry REL | control --step STEP status|follow|result' >&2
    exit 2
}

valid_step() {
    [[ "$1" =~ ^[A-Za-z0-9_.-]+$ ]]
}

active_run() {
    local run_dir="$1" pid token current_token
    [[ -f "$run_dir/pid" && -f "$run_dir/process-token" ]] || return 1
    pid="$(cat "$run_dir/pid")"
    token="$(cat "$run_dir/process-token")"
    [[ "$pid" =~ ^[0-9]+$ && -r "/proc/$pid/stat" ]] || return 1
    current_token="$(awk '{print $22}' "/proc/$pid/stat" 2>/dev/null || true)"
    [[ -n "$current_token" && "$current_token" == "$token" ]]
}

current_run_dir() {
    local step="$1" step_root="$STATE_ROOT/$step" run_id
    [[ -f "$step_root/current-run" ]] || return 1
    run_id="$(cat "$step_root/current-run")"
    [[ -n "$run_id" && -d "$step_root/runs/$run_id" ]] || return 1
    printf '%s\n' "$step_root/runs/$run_id"
}

make_control() {
    local step="$1" step_root="$STATE_ROOT/$step" quoted_self quoted_step
    printf -v quoted_self '%q' "$SELF"
    printf -v quoted_step '%q' "$step"
    cat > "$step_root/control.sh" <<EOF
#!/usr/bin/env bash
set -Eeuo pipefail
[[ \$# -eq 1 ]] || { echo 'Usage: control.sh status|follow|result' >&2; exit 2; }
exec $quoted_self control --step $quoted_step "\$1"
EOF
    chmod 700 "$step_root/control.sh"
}

safe_extract() {
    local zip="$1" destination="$2" expected_step="$3"
    python3 - "$zip" "$destination" "$expected_step" <<'PY'
import sys
import zipfile
from pathlib import Path

archive = Path(sys.argv[1])
destination = Path(sys.argv[2]).resolve()
expected = sys.argv[3]
with zipfile.ZipFile(archive) as handle:
    roots = set()
    for member in handle.infolist():
        name = member.filename.replace("\\", "/")
        parts = Path(name).parts
        if not parts:
            continue
        if name.startswith("/") or ".." in parts or ":" in parts[0]:
            raise SystemExit(f"unsafe member: {member.filename}")
        roots.add(parts[0])
        target = (destination / name).resolve()
        if target != destination and destination not in target.parents:
            raise SystemExit(f"escape: {member.filename}")
    if roots != {expected}:
        raise SystemExit(f"root mismatch: {sorted(roots)}")
    handle.extractall(destination)
PY
}

start_run() {
    local step='' zip='' sha='' entry=''
    while (($#)); do
        case "$1" in
            --step) step="$2"; shift 2 ;;
            --zip) zip="$2"; shift 2 ;;
            --sha256) sha="$2"; shift 2 ;;
            --entry) entry="$2"; shift 2 ;;
            *) usage ;;
        esac
    done

    valid_step "$step" || usage
    [[ -f "$zip" ]] || usage
    [[ "$sha" =~ ^[0-9a-f]{64}$ ]] || usage
    [[ -n "$entry" && "$entry" != /* && "$entry" != *'..'* ]] || usage

    local step_root="$STATE_ROOT/$step"
    mkdir -p "$step_root/runs"
    chmod 700 "$step_root" "$step_root/runs"
    exec 9>"$step_root/runner.lock"
    flock 9

    local previous=''
    previous="$(current_run_dir "$step" 2>/dev/null || true)"
    if [[ -n "$previous" ]] && active_run "$previous"; then
        echo RESULT=STOP_DETACHED_RUNNER_ALREADY_ACTIVE
        echo "RUN_DIR=$previous"
        echo "CONTROL=$step_root/control.sh"
        exit 20
    fi

    local run_id run_dir immutable extracted entry_absolute
    run_id="$(date -u +%Y%m%d-%H%M%S)-$$-${RANDOM}"
    run_dir="$step_root/runs/$run_id"
    immutable="$run_dir/immutable-step.zip"
    extracted="$run_dir/extracted"
    mkdir -p "$run_dir" "$extracted"
    chmod 700 "$run_dir" "$extracted"

    cp "$zip" "$immutable"
    echo "$sha  $immutable" | sha256sum -c - >/dev/null
    safe_extract "$immutable" "$extracted" "$step"
    entry_absolute="$extracted/$step/$entry"
    [[ -f "$entry_absolute" ]] || {
        echo RESULT=STOP_DETACHED_ENTRY_MISSING
        echo "ENTRY=$entry"
        exit 21
    }
    chmod 700 "$entry_absolute"

    find /home/ops/incoming -maxdepth 1 -type f -name 'WG_PAID_MGTS_LATEST_*.zip' -printf '%f\n' 2>/dev/null |
        LC_ALL=C sort > "$run_dir/previous-latest.txt" || true

    cat > "$run_dir/worker.sh" <<'WORKER'
#!/usr/bin/env bash
set -uo pipefail
run_dir="$1"
step="$2"
entry="$3"
bundle_root="$4"
immutable="$5"
log="$run_dir/run.log"
child_env="$run_dir/child-result.env"
: > "$log"
set +e
ROUTER_DETACHED_RUN_DIR="$run_dir" \
ROUTER_DETACHED_CHILD_RESULT="$child_env" \
BUNDLE_ROOT="$bundle_root" \
BUNDLE_ZIP="$immutable" \
"$entry" >>"$log" 2>&1
rc=$?

find /home/ops/incoming -maxdepth 1 -type f -name 'WG_PAID_MGTS_LATEST_*.zip' -printf '%f\n' 2>/dev/null |
    LC_ALL=C sort > "$run_dir/final-latest.txt" || true

tmp="$run_dir/result.env.tmp.$$"
{
    echo RUNNER_STATE=complete
    echo "STEP=$step"
    echo "RUN_ID=$(basename "$run_dir")"
    echo "RUN_DIR=$run_dir"
    echo "RUNNER_RC=$rc"
    awk '/^RESULT=/{value=$0} END{if(value) print "CHILD_FINAL_" value}' "$log"
    if [[ -f "$child_env" ]]; then
        cat "$child_env"
    fi
    newest="$(comm -13 "$run_dir/previous-latest.txt" "$run_dir/final-latest.txt" | tail -n1)"
    [[ -n "$newest" ]] && echo "FINAL_LATEST_ARCHIVE=/home/ops/incoming/$newest"
} > "$tmp"
chmod 600 "$tmp"
mv -f "$tmp" "$run_dir/result.env"
exit "$rc"
WORKER
    chmod 700 "$run_dir/worker.sh"
    : > "$run_dir/run.log"
    chmod 600 "$run_dir/run.log"

    local pid token='' attempt
    pid="$(
        python3 -I -             "$run_dir/worker.sh" "$run_dir" "$step" "$entry_absolute" "$extracted/$step" "$immutable" <<'PYLAUNCH'
import os
import subprocess
import sys

with open(os.devnull, "rb") as stdin_handle, open(os.devnull, "ab", buffering=0) as output_handle:
    process = subprocess.Popen(
        sys.argv[1:],
        stdin=stdin_handle,
        stdout=output_handle,
        stderr=output_handle,
        close_fds=True,
        start_new_session=True,
    )
print(process.pid)
PYLAUNCH
    )"
    [[ "$pid" =~ ^[0-9]+$ ]]
    echo "$pid" > "$run_dir/pid"
    for attempt in $(seq 1 50); do
        if [[ -r "/proc/$pid/stat" ]]; then
            token="$(awk '{print $22}' "/proc/$pid/stat" 2>/dev/null || true)"
        fi
        [[ -n "$token" ]] && break
        sleep 0.02
    done
    [[ -n "$token" ]] || {
        echo RESULT=STOP_DETACHED_WORKER_PID_NOT_TRACKABLE
        echo "PID=$pid"
        echo "RUN_DIR=$run_dir"
        exit 22
    }
    echo "$token" > "$run_dir/process-token"

    printf '%s\n' "$run_id" > "$step_root/current-run.tmp.$$"
    mv -f "$step_root/current-run.tmp.$$" "$step_root/current-run"
    make_control "$step"

    echo "RESULT=PASS_${step}_DETACHED_RUNNER_STARTED"
    echo "RUN_ID=$run_id"
    echo "RUN_DIR=$run_dir"
    echo "PID=$pid"
    echo "LOG=$run_dir/run.log"
    echo "CONTROL=$step_root/control.sh"
    echo "STATUS_COMMAND=$step_root/control.sh status"
    echo "FOLLOW_COMMAND=$step_root/control.sh follow"
    echo "RESULT_COMMAND=$step_root/control.sh result"
}

control_run() {
    local step=''
    [[ "${1:-}" == --step ]] || usage
    step="$2"
    shift 2
    [[ $# -eq 1 ]] || usage
    local action="$1"
    valid_step "$step" || usage

    local run_dir
    run_dir="$(current_run_dir "$step")" || {
        echo RESULT=STOP_DETACHED_RUN_NOT_FOUND
        exit 30
    }

    case "$action" in
        status)
            if [[ -f "$run_dir/result.env" ]]; then
                echo DETACHED_STATUS=complete
                cat "$run_dir/result.env"
            else
                if active_run "$run_dir"; then
                    echo DETACHED_STATUS=running
                else
                    echo DETACHED_STATUS=stale
                fi
                echo "RUN_DIR=$run_dir"
                tail -n 20 "$run_dir/run.log" 2>/dev/null || true
            fi
            ;;
        follow)
            if [[ -f "$run_dir/result.env" ]]; then
                cat "$run_dir/run.log"
                cat "$run_dir/result.env"
                exit 0
            fi
            local pid
            pid="$(cat "$run_dir/pid")"
            tail --pid="$pid" -n +1 -f "$run_dir/run.log" || true
            [[ -f "$run_dir/result.env" ]] && cat "$run_dir/result.env"
            ;;
        result)
            [[ -f "$run_dir/result.env" ]] || {
                echo RESULT=STOP_DETACHED_RESULT_NOT_READY
                echo "RUN_DIR=$run_dir"
                exit 31
            }
            cat "$run_dir/result.env"
            ;;
        *)
            usage
            ;;
    esac
}

[[ $# -ge 1 ]] || usage
command="$1"
shift
case "$command" in
    start) start_run "$@" ;;
    control) control_run "$@" ;;
    *) usage ;;
esac
