#!/usr/bin/env bash
# Extract run-owned Playwright artifacts before the dispatcher deletes the Job.
#
# Kubernetes hand-off convention (owned by the T2.1/T3.x dispatcher wiring):
#   * submit a Job in namespace ipz-e2e with labels app=ipz-e2e and
#     run=<RUN_ID> on both Job and Pod template;
#   * mount one writable run-workspace volume at /workspace in both the
#     Playwright container and a live regular helper container named
#     artifact-exporter (override with IPZ_E2E_ARTIFACT_CONTAINER); the helper
#     image must contain /bin/sh and tar, and must stay Running after the
#     Playwright container terminates until this command returns;
#   * have Playwright write only the fixed paths
#     /workspace/plugins/international-press-zone/tests/e2e/test-results and
#     /workspace/plugins/international-press-zone/tests/e2e/playwright-report on
#   * invoke this command after the Playwright container has finished but before
#     the helper sidecar exits or the Job/Pod is deleted. The dispatcher then
#     releases/stops the helper and performs its normal run-label cleanup.
#
# kubectl cp necessarily execs tar inside its source container. It therefore
# copies from the live artifact-exporter sidecar, never from the terminated
# Playwright container. This extractor uses the run label to locate exactly one
# Pod, verifies that the helper has a running status, and pulls only the two
# fixed paths. It never accepts a pod name or a source path from the caller, so
# a run cannot make extraction escape the workspace convention. T3.x must add
# the shared-volume/helper-sidecar convention above when wiring this command;
# the current T2.1 dispatcher remains untouched by this task.
set -Eeuo pipefail

readonly NAMESPACE="${IPZ_E2E_NAMESPACE:-ipz-e2e}"
readonly CONTAINER_NAME="${IPZ_E2E_ARTIFACT_CONTAINER:-artifact-exporter}"
readonly RESULTS_ROOT="/workspace/plugins/international-press-zone/tests/e2e"
readonly ARTIFACT_NAMES=(test-results playwright-report)

STUB_MODE=0
RUN_ID=""
LOCAL_E2E_DIR=""
STAGE_DIR=""
STUB_SOURCE_ROOT=""
STUB_SOURCE_OWNED=0

usage() {
    cat <<'USAGE'
Usage: e2e-k8s-artifacts [--stub] <run-id> <local-e2e-dir>

Copy test-results/ (including junit.xml) and the distinct playwright-report/
from the live artifact helper sidecar in the run-owned ipz-e2e Pod into
<local-e2e-dir>. The sidecar must share /workspace with Playwright.

--stub  Create a fake completed results tree locally and exercise extraction
        without contacting Kubernetes. This is intended for acceptance checks.
USAGE
}

die() {
    printf 'e2e-k8s-artifacts: error: %s\n' "$*" >&2
    exit 1
}

cleanup() {
    local status=$?

    if [[ -n "$STAGE_DIR" && -d "$STAGE_DIR" ]]; then
        rm -rf -- "$STAGE_DIR"
    fi
    if (( STUB_SOURCE_OWNED )) && [[ -n "$STUB_SOURCE_ROOT" ]]; then
        rm -rf -- "$STUB_SOURCE_ROOT"
    fi

    exit "$status"
}
trap cleanup EXIT

if [[ ${1:-} == "--help" || ${1:-} == "-h" ]]; then
    usage
    exit 0
fi
if [[ ${1:-} == "--stub" ]]; then
    STUB_MODE=1
    shift
fi
if [[ ${1:-} == "--help" || ${1:-} == "-h" ]]; then
    usage
    exit 0
fi

if (( $# != 2 )); then
    usage >&2
    exit 2
fi

RUN_ID=$1
LOCAL_E2E_DIR=$2

# The dispatcher renders DNS-1123-safe Job names and label values. Keep the
# same restriction here so the selector cannot be changed by shell/selector
# metacharacters supplied by a caller.
if (( ${#RUN_ID} > 63 )) || [[ ! "$RUN_ID" =~ ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ ]]; then
    die "run-id must be a DNS-1123-safe value of at most 63 characters"
fi
if [[ ! "$NAMESPACE" =~ ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ ]]; then
    die "IPZ_E2E_NAMESPACE must be a DNS-1123-safe namespace"
fi
if [[ ! "$CONTAINER_NAME" =~ ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ ]]; then
    die "IPZ_E2E_ARTIFACT_CONTAINER must be a DNS-1123-safe container name"
fi

# Create the destination first, then canonicalize it. All writes below are to
# these two fixed child directory names; never to a caller-provided source.
mkdir -p -- "$LOCAL_E2E_DIR" || die "cannot create local E2E directory: $LOCAL_E2E_DIR"
LOCAL_E2E_DIR=$(cd -- "$LOCAL_E2E_DIR" && pwd -P) || die "cannot resolve local E2E directory"
[[ "$LOCAL_E2E_DIR" != "/" ]] || die "refusing to use filesystem root as local E2E directory"

TMP_ROOT=${TMPDIR:-/tmp}
[[ -d "$TMP_ROOT" && -w "$TMP_ROOT" ]] || die "temporary directory is not writable: $TMP_ROOT"
STAGE_DIR=$(mktemp -d "$TMP_ROOT/ipz-e2e-artifacts-${RUN_ID}.XXXXXX") || die "cannot create staging directory"

make_stub_tree() {
    STUB_SOURCE_ROOT=$(mktemp -d "$TMP_ROOT/ipz-e2e-artifacts-stub-${RUN_ID}.XXXXXX")
    STUB_SOURCE_OWNED=1

    mkdir -p -- \
        "$STUB_SOURCE_ROOT/tests/e2e/test-results" \
        "$STUB_SOURCE_ROOT/tests/e2e/playwright-report/assets"
    cat >"$STUB_SOURCE_ROOT/tests/e2e/test-results/junit.xml" <<'JUNIT'
<?xml version="1.0" encoding="UTF-8"?>
<testsuites tests="1" failures="0"><testsuite name="stub" tests="1" failures="0"><testcase classname="artifact-extraction" name="stub"/></testsuite></testsuites>
JUNIT
    cat >"$STUB_SOURCE_ROOT/tests/e2e/playwright-report/index.html" <<'HTML'
<!doctype html><html><head><meta charset="utf-8"><title>Stub Playwright report</title></head><body>artifact extraction stub</body></html>
HTML
    printf 'stub run %s\n' "$RUN_ID" >"$STUB_SOURCE_ROOT/tests/e2e/playwright-report/assets/README.txt"
}

KUBECTL=()
POD_NAME=""

resolve_kubectl() {
    local configured=${IPZ_E2E_KUBECTL:-}

    if [[ -n "$configured" ]]; then
        # This is an operator-provided command (for example
        # "sudo /usr/local/bin/k3s kubectl"); split it only into argv words so
        # arguments are not re-evaluated through a shell.
        read -r -a KUBECTL <<<"$configured"
        ((${#KUBECTL[@]} > 0)) || die "IPZ_E2E_KUBECTL is empty"
        return
    fi

    if command -v kubectl >/dev/null 2>&1; then
        KUBECTL=(kubectl)
    elif [[ -x /usr/local/bin/kubectl ]]; then
        KUBECTL=(/usr/local/bin/kubectl)
    elif [[ -x /usr/local/bin/k3s ]] && command -v sudo >/dev/null 2>&1; then
        KUBECTL=(sudo /usr/local/bin/k3s kubectl)
    else
        die "kubectl is unavailable (set IPZ_E2E_KUBECTL for the cluster command)"
    fi
}

find_pod() {
    local selector pod_listing
    local -a pods=()

    selector="app=ipz-e2e,run=$RUN_ID"
    if ! pod_listing=$("${KUBECTL[@]}" --namespace "$NAMESPACE" get pods \
        --selector "$selector" \
        --output 'jsonpath={range .items[*]}{.metadata.name}{"\n"}{end}'); then
        die "cannot list Pod for run $RUN_ID"
    fi

    while IFS= read -r pod; do
        [[ -z "$pod" ]] && continue
        pods+=("$pod")
    done <<<"$pod_listing"

    if (( ${#pods[@]} != 1 )); then
        if (( ${#pods[@]} == 0 )); then
            die "no Pod found for run label $RUN_ID; extract before Job cleanup"
        fi
        die "expected exactly one Pod for run label $RUN_ID, found ${#pods[@]}"
    fi

    POD_NAME=${pods[0]}
    if [[ ! "$POD_NAME" =~ ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ ]]; then
        die "cluster returned an unsafe Pod name: $POD_NAME"
    fi
}

verify_helper_running() {
    local container_listing helper_running=0 container started_at

    if ! container_listing=$(
        "${KUBECTL[@]}" --namespace "$NAMESPACE" get pod "$POD_NAME" \
            --output 'jsonpath={range .status.containerStatuses[*]}{.name}{"\t"}{.state.running.startedAt}{"\n"}{end}'
    ); then
        die "cannot inspect Pod $POD_NAME before artifact extraction"
    fi

    while IFS=$'\t' read -r container started_at; do
        if [[ "$container" == "$CONTAINER_NAME" && -n "$started_at" ]]; then
            helper_running=1
            break
        fi
    done <<< "$container_listing"

    if (( ! helper_running )); then
        die "artifact helper container $CONTAINER_NAME is not running; extract before its sidecar exits"
    fi
}

copy_artifact() {
    local artifact=$1
    local source_path="$RESULTS_ROOT/$artifact"

    printf 'Extracting %s from run %s\n' "$artifact" "$RUN_ID"
    if (( STUB_MODE )); then
        cp -a -- "$STUB_SOURCE_ROOT/tests/e2e/$artifact" "$STAGE_DIR/" \
            || die "stub extraction failed for $artifact"
    else
        # The destination is a fresh staging parent. kubectl cp preserves the
        # source directory basename, yielding STAGE_DIR/<artifact>.
        "${KUBECTL[@]}" cp \
            --namespace "$NAMESPACE" \
            --container "$CONTAINER_NAME" \
            "$POD_NAME:$source_path" "$STAGE_DIR" \
            || die "kubectl cp failed for $source_path"
    fi

    [[ -d "$STAGE_DIR/$artifact" ]] || die "extracted artifact is missing: $artifact"
}

install_artifacts() {
    local artifact

    [[ -f "$STAGE_DIR/test-results/junit.xml" ]] \
        || die "extracted test-results is missing junit.xml"
    [[ -f "$STAGE_DIR/playwright-report/index.html" ]] \
        || die "extracted playwright-report is missing index.html"

    for artifact in "${ARTIFACT_NAMES[@]}"; do
        if [[ -e "$LOCAL_E2E_DIR/$artifact" || -L "$LOCAL_E2E_DIR/$artifact" ]]; then
            rm -rf -- "${LOCAL_E2E_DIR:?}/$artifact" \
                || die "cannot replace existing $LOCAL_E2E_DIR/$artifact"
        fi
        mv -- "$STAGE_DIR/$artifact" "${LOCAL_E2E_DIR:?}/" \
            || die "cannot install $LOCAL_E2E_DIR/$artifact"
    done
}

if (( STUB_MODE )); then
    make_stub_tree
    printf 'Stub source created for run %s\n' "$RUN_ID"
else
    resolve_kubectl
    find_pod
    verify_helper_running
    printf 'Using Pod %s in namespace %s\n' "$POD_NAME" "$NAMESPACE"
fi

copy_artifact test-results
copy_artifact playwright-report
install_artifacts

printf 'Artifacts extracted to %s\n' "$LOCAL_E2E_DIR"
printf '  %s\n' "$LOCAL_E2E_DIR/test-results"
printf '  %s\n' "$LOCAL_E2E_DIR/playwright-report"
