#!/usr/bin/env bash
set -euo pipefail

readonly SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
readonly PLUGIN_DIR="$(cd -- "${SCRIPT_DIR}/../.." && pwd)"
readonly ADMIN_DIR="${PLUGIN_DIR}/admin"
readonly FIXTURE="${SCRIPT_DIR}/fixtures/acf-field-table-fixture.php"
readonly MANIFEST_FIXTURE="${SCRIPT_DIR}/manifest-fault-injection.php"
# shellcheck source=e2e-snapshot-contract.sh
source "${SCRIPT_DIR}/e2e-snapshot-contract.sh"

run_id_source="${IPZ_E2E_RUN_ID:-pid-$$}"
RUN_ID="$(printf '%s' "${run_id_source}" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9_.-' '-')"
RUN_ID="${RUN_ID#-}"
RUN_ID="${RUN_ID%-}"
[[ -n "${RUN_ID}" ]] || {
    printf 'IPZ_E2E_RUN_ID must contain at least one ASCII letter or digit.\n' >&2
    exit 1
}
readonly RUN_ID
readonly LOCK_FILE="${TMPDIR:-/tmp}/ipz-e2e-$(id -u)-8080.lock"
readonly OWNERSHIP_LABEL="io.presszone.ipz-e2e.run=${RUN_ID}"
readonly NETWORK="acf-table-e2e-network-${RUN_ID}"
readonly DATABASE_CONTAINER="acf-table-e2e-database-${RUN_ID}"
readonly WORDPRESS_CONTAINER="acf-table-e2e-wordpress-${RUN_ID}"
readonly DATABASE_VOLUME="acf-table-e2e-database-data-${RUN_ID}"
readonly WORDPRESS_VOLUME="acf-table-e2e-wordpress-data-${RUN_ID}"
readonly MARIADB_IMAGE="${IPZ_MARIADB_IMAGE}"
readonly WORDPRESS_IMAGE="${IPZ_WORDPRESS_IMAGE}"
readonly WORDPRESS_CLI_IMAGE="${IPZ_WORDPRESS_CLI_IMAGE}"
readonly DATABASE_NAME="wordpress"
readonly DATABASE_USER="wordpress"
readonly DATABASE_PASSWORD="wordpress"
readonly ADMIN_USER="admin"
readonly ADMIN_PASSWORD="admin123"
readonly SITE_URL="http://127.0.0.1:8080"
readonly STACK_NO_PLUGIN_MOUNT="${IPZ_STACK_NO_PLUGIN_MOUNT:-0}"
readonly STACK_INSTALL_ZIP="${IPZ_STACK_INSTALL_ZIP:-}"
readonly STACK_WP_DEBUG="${IPZ_STACK_WP_DEBUG:-0}"

[[ "${STACK_NO_PLUGIN_MOUNT}" == '0' || "${STACK_NO_PLUGIN_MOUNT}" == '1' ]] || {
    printf 'IPZ_STACK_NO_PLUGIN_MOUNT must be 0 or 1.\n' >&2
    exit 1
}
[[ "${STACK_WP_DEBUG}" == '0' || "${STACK_WP_DEBUG}" == '1' ]] || {
    printf 'IPZ_STACK_WP_DEBUG must be 0 or 1.\n' >&2
    exit 1
}
if [[ -n "${STACK_INSTALL_ZIP}" ]]; then
    [[ "${STACK_NO_PLUGIN_MOUNT}" == '1' ]] || {
        printf 'IPZ_STACK_INSTALL_ZIP requires IPZ_STACK_NO_PLUGIN_MOUNT=1.\n' >&2
        exit 1
    }
    [[ "${STACK_INSTALL_ZIP}" == /* && "${STACK_INSTALL_ZIP}" == *.zip && -f "${STACK_INSTALL_ZIP}" && -r "${STACK_INSTALL_ZIP}" ]] || {
        printf 'IPZ_STACK_INSTALL_ZIP must be an absolute path to an existing readable .zip file on this host.\n' >&2
        exit 1
    }
fi

require_command() {
    command -v "$1" >/dev/null 2>&1 || {
        printf 'Required command is unavailable: %s\n' "$1" >&2
        exit 1
    }
}

assert_candidate_admin_assets() {
    local node_bin=$1 dist_dir manifest

    dist_dir=${2:-"${ADMIN_DIR}/dist"}
    manifest="${dist_dir}/asset-manifest.json"
    [[ -f "${manifest}" ]] || {
        printf 'Candidate admin manifest is missing after build: %s\n' "${manifest}" >&2
        return 1
    }

    "${node_bin}" - "${dist_dir}" "${manifest}" <<'NODE'
const fs = require('fs');
const path = require('path');
const [distDir, manifestPath] = process.argv.slice(2);
const fail = (message) => {
    console.error(message);
    process.exit(1);
};
let manifest;
let distReal;
try {
    manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
    distReal = fs.realpathSync(distDir);
} catch (error) {
    fail(`Candidate admin manifest or dist directory is invalid: ${error.message}`);
}
const isInsideDist = (realPath) => {
    const relative = path.relative(distReal, realPath);
    return relative !== '' && !relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative);
};
const validateAsset = (asset, pattern) => {
    if (typeof asset !== 'string' || !pattern.test(asset) || path.posix.isAbsolute(asset) || path.win32.isAbsolute(asset)) {
        fail(`Candidate admin asset has an unsafe path: ${String(asset)}`);
    }
    const candidate = path.resolve(distDir, asset);
    let stats;
    let realPath;
    try {
        stats = fs.lstatSync(candidate);
        realPath = fs.realpathSync(candidate);
    } catch (error) {
        fail(`Candidate admin asset is missing: ${asset}`);
    }
    if (!stats.isFile() || !isInsideDist(realPath) || fs.statSync(candidate).size === 0) {
        fail(`Candidate admin asset is not a nonempty regular file under dist: ${asset}`);
    }
    return path.posix.basename(asset, path.posix.extname(asset));
};
const requireAssets = (entrypoint, assetType, pattern) => {
    const entry = manifest[entrypoint];
    if (!entry || typeof entry !== 'object' || !Array.isArray(entry[assetType]) || entry[assetType].length === 0) {
        fail(`Candidate admin manifest lacks ${entrypoint} ${assetType} assets.`);
    }
    return entry[assetType].map((asset) => validateAsset(asset, pattern));
};
const requireScriptOrder = (entrypoint, terminal) => {
    const names = requireAssets(entrypoint, 'js', /^js\/[A-Za-z0-9._-]+\.js$/);
    if (names.length < 2 || names[0] !== 'runtime' || names[names.length - 1] !== terminal) {
        fail(`Candidate admin manifest ${entrypoint} scripts must start with runtime.js and end with ${terminal}.js.`);
    }
    if (new Set(names).size !== names.length) {
        fail(`Candidate admin manifest ${entrypoint} scripts must have unique enqueue handles.`);
    }
};
requireScriptOrder('main', 'main');
requireScriptOrder('editor', 'editor');
const editor = manifest.editor;
if (Object.prototype.hasOwnProperty.call(editor, 'css')) {
    if (!Array.isArray(editor.css)) {
        fail('Candidate admin manifest editor css assets must be an array.');
    }
    editor.css.forEach((asset) => validateAsset(asset, /^css\/[A-Za-z0-9._-]+\.css$/));
}
validateAsset('css/main.css', /^css\/main\.css$/);
NODE
}

install_e2e_dependencies() {
    local npm_bin=$1

    [[ -f "${SCRIPT_DIR}/package.json" && -f "${SCRIPT_DIR}/package-lock.json" ]] || {
        printf 'E2E package.json or package-lock.json is missing: %s\n' "${SCRIPT_DIR}" >&2
        return 1
    }

    (
        cd "${SCRIPT_DIR}"
        "${npm_bin}" ci --ignore-scripts --no-audit --no-fund
    )
}

build_candidate_admin_assets() {
    local npm_bin=$1 node_bin=$2

    [[ -f "${ADMIN_DIR}/package.json" && -f "${ADMIN_DIR}/package-lock.json" ]] || {
        printf 'Candidate admin package.json or package-lock.json is missing: %s\n' "${ADMIN_DIR}" >&2
        return 1
    }

    (
        cd "${ADMIN_DIR}"
        "${npm_bin}" ci
        "${npm_bin}" run build
    )
    assert_candidate_admin_assets "${node_bin}"
}

inventory_owned_resources() {
    local containers networks volumes

    if ! containers="$(podman ps --all --filter "label=${OWNERSHIP_LABEL}" --format '{{.Names}}')"; then
        printf 'Failed to inventory current-run containers for label %s.\n' "${OWNERSHIP_LABEL}" >&2
        return 1
    fi
    if ! networks="$(podman network ls --filter "label=${OWNERSHIP_LABEL}" --format '{{.Name}}')"; then
        printf 'Failed to inventory current-run networks for label %s.\n' "${OWNERSHIP_LABEL}" >&2
        return 1
    fi
    if ! volumes="$(podman volume ls --filter "label=${OWNERSHIP_LABEL}" --format '{{.Name}}')"; then
        printf 'Failed to inventory current-run volumes for label %s.\n' "${OWNERSHIP_LABEL}" >&2
        return 1
    fi

    printf '%s\037%s\037%s\n' "${containers}" "${networks}" "${volumes}"
}

resource_names() {
    local resource_type=$1 label_filter=${2:-}
    local -a filter=()

    [[ -z "${label_filter}" ]] || filter=(--filter "label=${label_filter}")
    case "${resource_type}" in
        container)
            podman ps --all "${filter[@]}" --format '{{.Names}}'
            ;;
        network)
            podman network ls "${filter[@]}" --format '{{.Name}}'
            ;;
        volume)
            podman volume ls "${filter[@]}" --format '{{.Name}}'
            ;;
        *)
            printf 'Unknown Podman resource type: %s\n' "${resource_type}" >&2
            return 1
            ;;
    esac
}

resource_name_exists() {
    local resource_type=$1 resource_name=$2 names

    if ! names="$(resource_names "${resource_type}")"; then
        printf 'Failed to inventory %s names before acquiring E2E ownership.\n' "${resource_type}" >&2
        return 1
    fi
    if printf '%s\n' "${names}" | grep --fixed-strings --line-regexp --quiet "${resource_name}"; then
        printf 'IPZ E2E ownership collision: existing %s named %s. No Podman resources were changed.\n' \
            "${resource_type}" "${resource_name}" >&2
        return 2
    fi
}

assert_ownership_available() {
    local inventory containers networks volumes

    if ! inventory="$(inventory_owned_resources)"; then
        printf 'Cannot verify requested E2E ownership namespace. No Podman resources were changed.\n' >&2
        return 1
    fi
    IFS=$'\037' read -r containers networks volumes <<<"${inventory}"
    if [[ -n "${containers}${networks}${volumes}" ]]; then
        printf 'IPZ E2E ownership collision for label %s: containers=[%s] networks=[%s] volumes=[%s]. No Podman resources were changed.\n' \
            "${OWNERSHIP_LABEL}" "${containers}" "${networks}" "${volumes}" >&2
        return 1
    fi

    resource_name_exists container "${DATABASE_CONTAINER}" || return $?
    resource_name_exists container "${WORDPRESS_CONTAINER}" || return $?
    resource_name_exists network "${NETWORK}" || return $?
    resource_name_exists volume "${DATABASE_VOLUME}" || return $?
    resource_name_exists volume "${WORDPRESS_VOLUME}" || return $?
}

remove_owned_resources() {
    local resource_type=$1 names resource
    shift

    if ! names="$(resource_names "${resource_type}" "${OWNERSHIP_LABEL}")"; then
        printf 'Failed to inventory current-run %s resources for removal.\n' "${resource_type}" >&2
        return 1
    fi
    while IFS= read -r resource; do
        [[ -z "${resource}" ]] && continue
        if ! podman "$@" "${resource}" >/dev/null; then
            printf 'Failed to remove current-run %s resource %s.\n' "${resource_type}" "${resource}" >&2
            return 1
        fi
    done <<<"${names}"
}

cleanup_resources() {
    local inventory containers networks volumes

    if ! inventory="$(inventory_owned_resources)"; then
        return 1
    fi
    IFS=$'\037' read -r containers networks volumes <<<"${inventory}"

    remove_owned_resources container rm --force || return 1
    remove_owned_resources network network rm || return 1
    remove_owned_resources volume volume rm || return 1

    if ! inventory="$(inventory_owned_resources)"; then
        return 1
    fi
    IFS=$'\037' read -r containers networks volumes <<<"${inventory}"
    if [[ -n "${containers}${networks}${volumes}" ]]; then
        printf 'Cleanup failed; current-run resources survive (label %s): containers=[%s] networks=[%s] volumes=[%s]\n' \
            "${OWNERSHIP_LABEL}" "${containers}" "${networks}" "${volumes}" >&2
        return 1
    fi
}

cleanup() {
    local status=$?
    trap - EXIT INT TERM
    if ! cleanup_resources; then
        status=1
    fi
    exit "${status}"
}

run_with_port_lock() {
    local status

    if flock --nonblock --close --conflict-exit-code 75 "${LOCK_FILE}" "${BASH}" -c \
        'source "$1"; shift; main "$@"' _ "$0" "$@"; then
        return 0
    else
        status=$?
    fi
    if [[ ${status} -eq 75 ]]; then
        printf 'IPZ E2E port 8080 is busy for UID %s; another run holds %s. No Podman resources were created.\n' \
            "$(id -u)" "${LOCK_FILE}" >&2
        return 1
    fi
    return "${status}"
}

wait_for_database() {
    # First-boot datadir initialization on a loaded host can exceed three
    # minutes; the TCP probe below only succeeds once the real server (not
    # the socket-only init server) accepts connections, so the window must
    # cover the full initialization, not just process start.
    local attempt
    for attempt in $(seq 1 180); do
        if podman run --rm --label "${OWNERSHIP_LABEL}" --network "${NETWORK}" "${MARIADB_IMAGE}" \
            mariadb-admin ping --host="${DATABASE_CONTAINER}" --user="${DATABASE_USER}" --password="${DATABASE_PASSWORD}" --silent >/dev/null 2>&1; then
            return 0
        fi
        sleep 2
    done

    printf 'MariaDB did not become ready.\n' >&2
    podman logs "${DATABASE_CONTAINER}" >&2 || true
    return 1
}

wait_for_wordpress_files() {
    local attempt
    for attempt in $(seq 1 60); do
        if podman exec "${WORDPRESS_CONTAINER}" test -f /var/www/html/wp-includes/version.php \
            && podman exec "${WORDPRESS_CONTAINER}" test -f /var/www/html/wp-config.php; then
            return 0
        fi
        sleep 1
    done

    printf 'WordPress core or generated wp-config.php was not initialized.\n' >&2
    podman logs "${WORDPRESS_CONTAINER}" >&2 || true
    return 1
}

wp() {
    podman run --rm --label "${OWNERSHIP_LABEL}" --user 0 --network "${NETWORK}" --volumes-from "${WORDPRESS_CONTAINER}" \
        --env "WORDPRESS_DB_HOST=${DATABASE_CONTAINER}" \
        --env "WORDPRESS_DB_NAME=${DATABASE_NAME}" \
        --env "WORDPRESS_DB_USER=${DATABASE_USER}" \
        --env "WORDPRESS_DB_PASSWORD=${DATABASE_PASSWORD}" \
        "${WORDPRESS_CLI_IMAGE}" wp "$@" --allow-root
}

wp_with_connect_fixture_env() {
    local -a fixture_env=()

    [[ -n "${IPZ_E2E_SITE_API_KEY:-}" ]] && fixture_env+=(--env IPZ_E2E_SITE_API_KEY)
    [[ -n "${IPZ_E2E_SITE_ID:-}" ]] && fixture_env+=(--env IPZ_E2E_SITE_ID)
    [[ -n "${IPZ_E2E_ACCOUNT_EMAIL:-}" ]] && fixture_env+=(--env IPZ_E2E_ACCOUNT_EMAIL)
    [[ -n "${IPZ_E2E_ACCOUNT_DISPLAY_NAME:-}" ]] && fixture_env+=(--env IPZ_E2E_ACCOUNT_DISPLAY_NAME)

    podman run --rm --label "${OWNERSHIP_LABEL}" --user 0 --network "${NETWORK}" --volumes-from "${WORDPRESS_CONTAINER}" \
        --env "WORDPRESS_DB_HOST=${DATABASE_CONTAINER}" \
        --env "WORDPRESS_DB_NAME=${DATABASE_NAME}" \
        --env "WORDPRESS_DB_USER=${DATABASE_USER}" \
        --env "WORDPRESS_DB_PASSWORD=${DATABASE_PASSWORD}" \
        "${fixture_env[@]}" \
        "${WORDPRESS_CLI_IMAGE}" wp "$@" --allow-root
}

configure_translation_api_base() {
    local api_url=${IPZ_E2E_TRANSLATION_API_URL:-} actual

    [[ -n "${api_url}" ]] || return 0
    case "${api_url}" in
        https://*|http://127.0.0.1:*|http://localhost:*|http://\[::1\]:*) ;;
        *)
            printf 'IPZ_E2E_TRANSLATION_API_URL must use HTTPS, or HTTP on a loopback host.\n' >&2
            return 1
            ;;
    esac

    api_url=${api_url%/}
    wp config set IPZ_API_BASE_URL "${api_url}" --type=constant --quiet
    actual="$(wp eval 'echo defined("IPZ_API_BASE_URL") ? rtrim((string) IPZ_API_BASE_URL, "/") : "";')"
    [[ "${actual}" == "${api_url}" ]] || {
        printf 'E2E translation API base was not applied.\n' >&2
        return 1
    }
}

provision_connect_credential() {
    local has_key=0 has_site_id=0

    [[ -n "${IPZ_E2E_SITE_API_KEY:-}" ]] && has_key=1
    [[ -n "${IPZ_E2E_SITE_ID:-}" ]] && has_site_id=1
    if ((has_key != has_site_id)); then
        printf 'IPZ_E2E_SITE_API_KEY and IPZ_E2E_SITE_ID must be supplied together.\n' >&2
        return 1
    fi
    ((has_key == 1)) || return 0

    wp_with_connect_fixture_env eval '
$api_key = getenv("IPZ_E2E_SITE_API_KEY");
$site_id = getenv("IPZ_E2E_SITE_ID");
$email = getenv("IPZ_E2E_ACCOUNT_EMAIL");
$display_name = getenv("IPZ_E2E_ACCOUNT_DISPLAY_NAME");
if (!is_string($api_key) || $api_key === "" || !is_string($site_id) || $site_id === "") {
    fwrite(STDERR, "E2E Connect credential environment is incomplete.\n");
    exit(1);
}
$store = new \InternationalPressZone\Connect\CredentialStore();
if (!$store->save($api_key, $site_id, array(
    "email" => is_string($email) ? $email : "",
    "display_name" => is_string($display_name) ? $display_name : "",
))) {
    fwrite(STDERR, "E2E Connect credential could not be stored.\n");
    exit(1);
}
$stored = $store->get_api_key();
if (!is_string($stored) || !hash_equals($api_key, $stored)) {
    fwrite(STDERR, "E2E Connect credential verification failed.\n");
    exit(1);
}
$metadata = $store->metadata();
if (($metadata["site_id"] ?? "") !== strtolower($site_id)) {
    fwrite(STDERR, "E2E Connect site metadata verification failed.\n");
    exit(1);
}
$service = new \InternationalPressZone\Connect\ConnectService($store);
$status = $service->status(true);
if (is_wp_error($status) || empty($status["connected"])) {
    $code = is_wp_error($status) ? $status->get_error_code() : "disconnected";
    fwrite(STDERR, "E2E Connect credential failed backend status verification: " . $code . "\n");
    exit(1);
}
'
    printf 'Connect E2E credential provisioned and backend-verified for site fixture (secret withheld).\n'
}

# Deferred surfaces (Team/Workflow/My Assignments) are hidden behind the
# ipz_team_workflow kill switch per FIRST-CUSTOMER-SCOPE (f0bc60bed). The E2E
# fixture stack still exercises those surfaces (universal-admin-component-
# redesign.spec.js), so the option must be enabled for this stack only —
# production defaults in FeatureFlags.php stay untouched.
provision_e2e_feature_flags() {
    wp eval '
$flags = get_option("ipz_feature_flags", array());
if (!is_array($flags)) {
    $flags = array();
}
$flags["ipz_team_workflow"] = true;
$flags["ipz_unreleased"] = true;
update_option("ipz_feature_flags", $flags);
if (!\InternationalPressZone\Core\FeatureFlags::isEnabled("ipz_team_workflow")) {
    fwrite(STDERR, "E2E team workflow feature flag was not enabled.\n");
    exit(1);
}
if (method_exists("\\InternationalPressZone\\Core\\FeatureFlags", "isEnabled") && !\InternationalPressZone\Core\FeatureFlags::isEnabled("ipz_unreleased")) {
    fwrite(STDERR, "E2E ipz_unreleased feature flag was not enabled.\n");
    exit(1);
}
'
    printf 'E2E team workflow and shared unreleased feature flags enabled for deferred-surface fixtures.\n'
}

provision_manifest_e2e_fixture() {
    [[ -f "${MANIFEST_FIXTURE}" ]] || {
        printf 'Manifest proof fixture is missing: %s\n' "${MANIFEST_FIXTURE}" >&2
        return 1
    }

    wp config set IPZ_E2E_MANIFEST_PROOF true --raw --type=constant --quiet
    podman exec "${WORDPRESS_CONTAINER}" bash -c 'mkdir -p /var/www/html/wp-content/mu-plugins'
    podman exec -i "${WORDPRESS_CONTAINER}" tee /var/www/html/wp-content/mu-plugins/ipz-e2e-manifest-proof.php >/dev/null < "${MANIFEST_FIXTURE}"
    podman exec "${WORDPRESS_CONTAINER}" chown www-data:www-data /var/www/html/wp-content/mu-plugins/ipz-e2e-manifest-proof.php
    wp eval '
if (!defined("IPZ_E2E_MANIFEST_PROOF") || IPZ_E2E_MANIFEST_PROOF !== true) {
    fwrite(STDERR, "Manifest proof fixture constant was not applied.\\n");
    exit(1);
}
$required_actions = array(
    "ipz_e2e_manifest_worker_tick",
    "ipz_e2e_manifest_reconcile_tick",
    "ipz_e2e_manifest_callback_control",
);
$required_filters = array(
    "ipz_e2e_manifest_tick_metrics",
    "ipz_e2e_manifest_callback_events",
);
foreach ($required_actions as $hook) {
    if (!has_action($hook)) {
        fwrite(STDERR, "Required manifest proof action is not registered: " . $hook . "\\n");
        exit(1);
    }
}
foreach ($required_filters as $hook) {
    if (!has_filter($hook)) {
        fwrite(STDERR, "Required manifest proof filter is not registered: " . $hook . "\\n");
        exit(1);
    }
}
$self_check = array(
    "self_check" => true,
    "tick_id" => "e2e-fixture-self-check",
    "max_manifests" => 1,
    "max_expansions" => 100,
    "max_preparations" => 8,
    "max_submissions" => 4,
);
do_action("ipz_e2e_manifest_worker_tick", $self_check);
do_action("ipz_e2e_manifest_reconcile_tick", $self_check);
$metrics = apply_filters("ipz_e2e_manifest_tick_metrics", array(), $self_check);
$invocations = is_array($metrics) ? ($metrics["invocations"] ?? null) : null;
if (!is_array($invocations) || count($invocations) !== 1 || (int) ($invocations[0]["expansions"] ?? -1) !== 0 || (int) ($invocations[0]["preparations"] ?? -1) !== 0 || (int) ($invocations[0]["submissions"] ?? -1) !== 0) {
    fwrite(STDERR, "Manifest proof worker/metrics adapter self-check failed.\\n");
    exit(1);
}
$callback_context = array("self_check" => true, "mode" => "normal");
do_action("ipz_e2e_manifest_callback_control", $callback_context);
$events = apply_filters("ipz_e2e_manifest_callback_events", array(), $callback_context);
if (!is_array($events) || count($events) !== 1 || ($events[0]["submission_id"] ?? "") !== "e2e-self-check-submission" || (int) ($events[0]["event_sequence"] ?? 0) !== 1) {
    fwrite(STDERR, "Manifest proof callback adapter self-check failed.\\n");
    exit(1);
}
do_action("rest_api_init");
$routes = rest_get_server()->get_routes();
foreach (array("reset", "control", "seed", "cleanup", "tick", "callbacks", "assertions") as $route) {
    $key = "/ipz-e2e/v1/manifest/" . $route;
    if (!isset($routes[$key])) {
        fwrite(STDERR, "Required manifest proof route is not registered: " . $key . "\\n");
        exit(1);
    }
}
'
}

# WordPress spawns cron by looping back to the site URL. In this stack that URL is
# 127.0.0.1:8080 on the host while Apache listens on :80 inside the container, so the
# loopback connects to nothing -- and spawn_cron() sets the `doing_cron` transient
# BEFORE it makes that doomed request. The lock then blocks every /wp-cron.php hit for
# WP_CRON_LOCK_TIMEOUT (60s), and any page load re-arms it the moment it expires, so a
# spec that ticks cron itself never wins the race and its scheduled job never advances.
#
# Disabling the spawn removes the phantom lock. Nothing in this stack could ever run
# cron automatically anyway; specs that need a scheduled event drive /wp-cron.php
# directly, and that path ignores DISABLE_WP_CRON.
# Specs that need a scheduled event drive /wp-cron.php directly, and that runs every
# event that is due -- not only the one the spec cares about. WordPress ships
# wp_version_check, wp_update_plugins and wp_maybe_auto_update on that schedule, and an
# automatic update puts the whole site behind a .maintenance file: every request in
# flight comes back 503 "Briefly unavailable for scheduled maintenance". A journey
# watching an async job then records that 503 as a runtime failure of the feature under
# test, which it is not.
#
# Nothing in an ephemeral test stack should be updating itself, so the updater is
# switched off outright and any maintenance flag a previous run left behind is cleared.
provision_disabled_auto_updates() {
    wp config set AUTOMATIC_UPDATER_DISABLED true --raw --type=constant
    wp config set WP_AUTO_UPDATE_CORE false --raw --type=constant
    wp eval 'if (!defined("AUTOMATIC_UPDATER_DISABLED") || !AUTOMATIC_UPDATER_DISABLED) { fwrite(STDERR, "AUTOMATIC_UPDATER_DISABLED was not applied.\n"); exit(1); }'
    wp eval 'if (file_exists(ABSPATH . ".maintenance")) { unlink(ABSPATH . ".maintenance"); }'
}

provision_disabled_cron_spawn() {
    wp config set DISABLE_WP_CRON true --raw --type=constant
    wp transient delete doing_cron || true
    wp eval 'if (!defined("DISABLE_WP_CRON") || !DISABLE_WP_CRON) { fwrite(STDERR, "DISABLE_WP_CRON was not applied.\n"); exit(1); }'
}

provision_pretty_permalinks() {
    local permalink_structure

    podman exec "${WORDPRESS_CONTAINER}" touch /var/www/html/.htaccess
    podman exec "${WORDPRESS_CONTAINER}" chown www-data:www-data /var/www/html/.htaccess
    podman exec "${WORDPRESS_CONTAINER}" chmod 664 /var/www/html/.htaccess
    wp rewrite structure '/%postname%/'
    wp rewrite flush
    wp eval '
global $wp_rewrite;
$rules = $wp_rewrite->mod_rewrite_rules();
if (!is_string($rules) || $rules === "") {
    fwrite(STDERR, "Apache rewrite rule generation failed.\\n");
    exit(1);
}
if (file_put_contents(ABSPATH . ".htaccess", $rules) === false) {
    fwrite(STDERR, "Apache .htaccess write failed.\\n");
    exit(1);
}
'
    podman exec "${WORDPRESS_CONTAINER}" chown www-data:www-data /var/www/html/.htaccess
    podman exec "${WORDPRESS_CONTAINER}" chmod 664 /var/www/html/.htaccess
    permalink_structure="$(wp option get permalink_structure)"
    [[ "${permalink_structure}" == '/%postname%/' ]] || {
        printf 'Pretty permalink provisioning failed: expected /%%postname%%/, got %s.\n' "${permalink_structure}" >&2
        return 1
    }
}

provision_release_zip() {
    local extractor

    [[ -n "${STACK_INSTALL_ZIP}" ]] || return 0

    if podman exec "${WORDPRESS_CONTAINER}" bash -c 'command -v unzip >/dev/null 2>&1'; then
        extractor=unzip
    elif podman exec "${WORDPRESS_CONTAINER}" php -r 'exit(class_exists("ZipArchive") ? 0 : 1);'; then
        extractor=php
    else
        printf 'Release ZIP provisioning failed: the WordPress container has neither unzip nor PHP ZipArchive.\n' >&2
        return 1
    fi

    if ! podman exec -i "${WORDPRESS_CONTAINER}" bash -c 'cat > /tmp/ipz.zip' < "${STACK_INSTALL_ZIP}"; then
        printf 'Release ZIP provisioning failed: could not stream %s into the WordPress container.\n' "${STACK_INSTALL_ZIP}" >&2
        return 1
    fi

    if [[ "${extractor}" == 'unzip' ]]; then
        if ! podman exec "${WORDPRESS_CONTAINER}" bash -c 'unzip -oq /tmp/ipz.zip -d /var/www/html/wp-content/plugins/'; then
            printf 'Release ZIP provisioning failed: unzip could not extract the archive.\n' >&2
            return 1
        fi
    elif ! podman exec "${WORDPRESS_CONTAINER}" php -r '
$zip = new ZipArchive();
if ($zip->open("/tmp/ipz.zip") !== true || !$zip->extractTo("/var/www/html/wp-content/plugins/") || !$zip->close()) {
    fwrite(STDERR, "ZipArchive extraction failed.\\n");
    exit(1);
}
'; then
        printf 'Release ZIP provisioning failed: PHP ZipArchive could not extract the archive.\n' >&2
        return 1
    fi

    if ! podman exec "${WORDPRESS_CONTAINER}" bash -c 'test -d /var/www/html/wp-content/plugins/international-press-zone && chown -R www-data:www-data /var/www/html/wp-content/plugins/international-press-zone'; then
        printf 'Release ZIP provisioning failed: extracted international-press-zone tree is missing or could not be owned by www-data.\n' >&2
        return 1
    fi
    if ! wp plugin activate international-press-zone; then
        printf 'Release ZIP provisioning failed: could not activate international-press-zone.\n' >&2
        return 1
    fi
    wp plugin is-active international-press-zone
    provision_e2e_feature_flags
    # The snapshot database already records the plugin as active, so activating the
    # freshly extracted release never fires the activation hook that creates plugin
    # tables; run the idempotent creator and fail closed, matching the mounted branch.
    wp eval 'global $wpdb; if (!(new \InternationalPressZone\Core\Database())->create_tables()) { fwrite(STDERR, "IPZ table creation failed.\n"); exit(1); } $table = $wpdb->prefix . "ipz_workflow_states"; if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table)) !== $table) { fwrite(STDERR, "ipz_workflow_states table missing after create_tables.\n"); exit(1); }'
    provision_connect_credential
}

provision_wp_debug() {
    [[ "${STACK_WP_DEBUG}" == '1' ]] || return 0

    if ! podman exec "${WORDPRESS_CONTAINER}" bash -c 'mkdir -p /var/www/html/wp-content && chown www-data:www-data /var/www/html/wp-content && chmod 775 /var/www/html/wp-content'; then
        printf 'WP_DEBUG provisioning failed: wp-content could not be made writable by www-data.\n' >&2
        return 1
    fi
    if ! podman exec --user www-data "${WORDPRESS_CONTAINER}" test -w /var/www/html/wp-content; then
        printf 'WP_DEBUG provisioning failed: wp-content is not writable by www-data.\n' >&2
        return 1
    fi
    if ! wp config set WP_DEBUG true --type=constant --raw; then
        printf 'WP_DEBUG provisioning failed: could not set WP_DEBUG.\n' >&2
        return 1
    fi
    if ! wp config set WP_DEBUG_LOG true --type=constant --raw; then
        printf 'WP_DEBUG provisioning failed: could not set WP_DEBUG_LOG.\n' >&2
        return 1
    fi
    if ! wp config set WP_DEBUG_DISPLAY false --type=constant --raw; then
        printf 'WP_DEBUG provisioning failed: could not set WP_DEBUG_DISPLAY.\n' >&2
        return 1
    fi
}

assert_rest_json_routing() {
    if podman exec "${WORDPRESS_CONTAINER}" php -r '
$url = "http://127.0.0.1/wp-json/";
$context = stream_context_create(array("http" => array("header" => "Host: 127.0.0.1:8080\r\n", "ignore_errors" => true, "timeout" => 5)));
$body = @file_get_contents($url, false, $context);
$headers = $http_response_header ?? array();
$content_type = "";
foreach ($headers as $header) {
    if (stripos($header, "Content-Type:") === 0) {
        $content_type = trim(substr($header, strlen("Content-Type:")));
        break;
    }
}
if (!is_string($body) || stripos($content_type, "application/json") !== 0 || json_decode($body, true) === null) {
    fwrite(STDERR, sprintf("REST rewrite smoke failed: /wp-json/ content-type=%s body-prefix=%s\\n", $content_type ?: "missing", is_string($body) ? substr($body, 0, 80) : "unavailable"));
    exit(1);
}
'; then
        return 0
    fi

    printf 'REST rewrite smoke failed: /wp-json/ did not return JSON before port publication.\n' >&2
    return 1
}

assert_acf_fixture_graph() {
    wp eval '
$required_fields = array(
    "field_acf_table_e2e_intro" => "text",
    "field_acf_table_e2e_repeater" => "repeater",
    "field_acf_table_e2e_repeater_title" => "text",
    "field_acf_table_e2e_repeater_copy" => "textarea",
    "field_acf_table_e2e_flexible" => "flexible_content",
    "field_acf_table_e2e_hero_heading" => "text",
    "field_acf_table_e2e_hero_items" => "repeater",
    "field_acf_table_e2e_hero_item_label" => "text",
    "field_acf_table_e2e_callout_message" => "textarea",
    "field_acf_table_e2e_secondary_note" => "textarea",
);
foreach ($required_fields as $key => $type) {
    $field = acf_get_field($key);
    if (!is_array($field) || $field["key"] !== $key || $field["type"] !== $type) {
        fwrite(STDERR, sprintf("ACF fixture field assertion failed: %s\\n", $key));
        exit(1);
    }
}
$field_keys = static function ($fields): array {
    $keys = array();
    foreach ((array) $fields as $field) {
        if (is_array($field) && isset($field["key"])) {
            $keys[] = $field["key"];
        }
    }
    return $keys;
};
$assert_child_keys = static function ($fields, array $expected, string $parent) use ($field_keys): void {
    if ($field_keys($fields) !== $expected) {
        fwrite(STDERR, sprintf("ACF fixture child graph assertion failed: %s\\n", $parent));
        exit(1);
    }
};
$repeater = acf_get_field("field_acf_table_e2e_repeater");
$assert_child_keys($repeater["sub_fields"] ?? array(), array("field_acf_table_e2e_repeater_title", "field_acf_table_e2e_repeater_copy"), $repeater["key"]);
$hero_items = acf_get_field("field_acf_table_e2e_hero_items");
$assert_child_keys($hero_items["sub_fields"] ?? array(), array("field_acf_table_e2e_hero_item_label"), $hero_items["key"]);
$flexible = acf_get_field("field_acf_table_e2e_flexible");
$layouts = array();
foreach ((array) $flexible["layouts"] as $layout) {
    if (is_array($layout) && isset($layout["key"])) {
        $layouts[$layout["key"]] = $layout;
    }
}
foreach (array("layout_acf_table_e2e_hero", "layout_acf_table_e2e_callout") as $key) {
    if (!isset($layouts[$key]) || $layouts[$key]["key"] !== $key) {
        fwrite(STDERR, sprintf("ACF fixture layout assertion failed: %s\\n", $key));
        exit(1);
    }
}
$assert_child_keys($layouts["layout_acf_table_e2e_hero"]["sub_fields"] ?? array(), array("field_acf_table_e2e_hero_heading", "field_acf_table_e2e_hero_items"), "layout_acf_table_e2e_hero");
$assert_child_keys($layouts["layout_acf_table_e2e_callout"]["sub_fields"] ?? array(), array("field_acf_table_e2e_callout_message"), "layout_acf_table_e2e_callout");
'
}

run_candidate_admin_asset_tests() {
    local node_bin

    node_bin="$(command -v node)"
    [[ "${node_bin}" == /* && -x "${node_bin}" ]] || {
        printf 'Self-test requires an executable absolute node path: %s\n' "${node_bin}" >&2
        return 1
    }

    (
        local workspace
        workspace="$(mktemp -d)"
        trap 'rm -rf -- "${workspace}"' EXIT

        prepare_fixture() {
            local directory=$1 manifest=$2

            mkdir -p "${directory}/js" "${directory}/css"
            printf 'runtime' >"${directory}/js/runtime.js"
            printf 'chunk' >"${directory}/js/512.chunk.js"
            printf 'chunk' >"${directory}/js/101.chunk.js"
            printf 'main' >"${directory}/js/main.js"
            printf 'editor' >"${directory}/js/editor.js"
            printf 'editor css' >"${directory}/css/editor.css"
            printf 'main css' >"${directory}/css/main.css"
            printf '%s\n' "${manifest}" >"${directory}/asset-manifest.json"
        }

        assert_rejected_fixture() {
            local name=$1 directory=$2

            if assert_candidate_admin_assets "${node_bin}" "${directory}" >/dev/null 2>&1; then
                printf 'Candidate admin asset self-test accepted invalid %s fixture.\n' "${name}" >&2
                exit 1
            fi
        }

        valid_manifest='{"main":{"js":["js/runtime.js","js/512.chunk.js","js/101.chunk.js","js/main.js"]},"editor":{"js":["js/runtime.js","js/101.chunk.js","js/512.chunk.js","js/editor.js"],"css":["css/editor.css"]}}'
        prepare_fixture "${workspace}/pass" "${valid_manifest}"
        assert_candidate_admin_assets "${node_bin}" "${workspace}/pass" || {
            printf 'Candidate admin asset self-test rejected valid fixture.\n' >&2
            exit 1
        }

        prepare_fixture "${workspace}/editor-only" '{"main":{"js":["js/runtime.js","js/main.js"]},"editor":{"js":["js/editor.js"]}}'
        assert_rejected_fixture 'editor-only' "${workspace}/editor-only"

        prepare_fixture "${workspace}/main-before-runtime" '{"main":{"js":["js/main.js","js/runtime.js"]},"editor":{"js":["js/runtime.js","js/editor.js"]}}'
        assert_rejected_fixture 'main-before-runtime' "${workspace}/main-before-runtime"

        prepare_fixture "${workspace}/editor-before-runtime" '{"main":{"js":["js/runtime.js","js/main.js"]},"editor":{"js":["js/editor.js","js/runtime.js"]}}'
        assert_rejected_fixture 'editor-before-runtime' "${workspace}/editor-before-runtime"

        prepare_fixture "${workspace}/runtime-terminal" '{"main":{"js":["js/runtime.js","js/main.js","js/runtime.js"]},"editor":{"js":["js/runtime.js","js/editor.js"]}}'
        assert_rejected_fixture 'runtime duplicated in terminal position' "${workspace}/runtime-terminal"

        prepare_fixture "${workspace}/runtime-middle" '{"main":{"js":["js/runtime.js","js/runtime.js","js/512.chunk.js","js/main.js"]},"editor":{"js":["js/runtime.js","js/editor.js"]}}'
        assert_rejected_fixture 'runtime duplicated in middle position' "${workspace}/runtime-middle"

        prepare_fixture "${workspace}/chunk-adjacent" '{"main":{"js":["js/runtime.js","js/512.chunk.js","js/512.chunk.js","js/main.js"]},"editor":{"js":["js/runtime.js","js/editor.js"]}}'
        assert_rejected_fixture 'adjacent duplicate chunk' "${workspace}/chunk-adjacent"

        prepare_fixture "${workspace}/chunk-nonadjacent" '{"main":{"js":["js/runtime.js","js/512.chunk.js","js/101.chunk.js","js/512.chunk.js","js/main.js"]},"editor":{"js":["js/runtime.js","js/editor.js"]}}'
        assert_rejected_fixture 'nonadjacent duplicate chunk' "${workspace}/chunk-nonadjacent"

        prepare_fixture "${workspace}/non-js-entry" '{"main":{"js":["js/runtime.js","css/editor.css","js/main.js"]},"editor":{"js":["js/runtime.js","js/editor.js"]}}'
        assert_rejected_fixture 'non-js entry' "${workspace}/non-js-entry"

        prepare_fixture "${workspace}/terminal-drift" '{"main":{"js":["js/runtime.js","js/main.js"]},"editor":{"js":["js/runtime.js","js/512.chunk.js"]}}'
        assert_rejected_fixture 'terminal drift' "${workspace}/terminal-drift"

        prepare_fixture "${workspace}/missing-hardcoded-css" "${valid_manifest}"
        rm "${workspace}/missing-hardcoded-css/css/main.css"
        assert_rejected_fixture 'missing hardcoded CSS' "${workspace}/missing-hardcoded-css"

        prepare_fixture "${workspace}/traversal" '{"main":{"js":["js/../runtime.js","js/main.js"]},"editor":{"js":["js/runtime.js","js/editor.js"]}}'
        assert_rejected_fixture 'traversal' "${workspace}/traversal"

        prepare_fixture "${workspace}/absent-file" '{"main":{"js":["js/runtime.js","js/main.js"]},"editor":{"js":["js/runtime.js","js/missing.js","js/editor.js"]}}'
        assert_rejected_fixture 'absent file' "${workspace}/absent-file"

        prepare_fixture "${workspace}/empty-file" "${valid_manifest}"
        : >"${workspace}/empty-file/js/editor.js"
        assert_rejected_fixture 'empty file' "${workspace}/empty-file"
    )
}

run_port_lock_self_test() (
    local workspace lock_file child_pid

    workspace="$(mktemp -d)"
    lock_file="${workspace}/port.lock"
    trap '[[ -z "${child_pid:-}" ]] || kill "${child_pid}" >/dev/null 2>&1 || true; rm -rf -- "${workspace}"' EXIT

    flock --nonblock --close "${lock_file}" bash -c 'sleep 30 & printf "%s\n" "$!" >"$1"; disown' _ "${workspace}/child.pid"
    child_pid="$(<"${workspace}/child.pid")"
    kill -0 "${child_pid}" >/dev/null 2>&1 || {
        printf 'Port-lock self-test did not leave a live descendant.\n' >&2
        return 1
    }
    if ! flock --nonblock "${lock_file}" true; then
        printf 'Port-lock self-test found the lock inherited by a descendant.\n' >&2
        return 1
    fi
)

run_mocked_tests() {
    run_candidate_admin_asset_tests
    run_port_lock_self_test

    (
        podman() { return 77; }
        if cleanup_resources >/dev/null 2>&1; then
            printf 'Mocked inventory failure incorrectly succeeded.\n' >&2
            exit 1
        fi
    )
    (
        log="$(mktemp)"
        trap 'rm -f -- "${log}"' EXIT
        owned_container=1
        owned_network=1
        owned_volume=1
        podman() {
            local filter= argument
            case "$1" in
                ps)
                    shift
                    while (($#)); do
                        argument=$1
                        shift
                        [[ "${argument}" == '--filter' && $# -gt 0 ]] && {
                            filter=$1
                            shift
                        }
                    done
                    printf 'inventory container %s\n' "${filter}" >>"${log}"
                    if [[ "${filter}" == "label=${OWNERSHIP_LABEL}" && ${owned_container} -eq 1 ]]; then
                        printf 'owned-container\n'
                    elif [[ "${filter}" == "label=${RUN_ID}" ]]; then
                        printf 'foreign-container\n'
                    fi
                    ;;
                network)
                    if [[ "${2:-}" == 'ls' ]]; then
                        shift 2
                        while (($#)); do
                            argument=$1
                            shift
                            [[ "${argument}" == '--filter' && $# -gt 0 ]] && {
                                filter=$1
                                shift
                            }
                        done
                        printf 'inventory network %s\n' "${filter}" >>"${log}"
                        if [[ "${filter}" == "label=${OWNERSHIP_LABEL}" && ${owned_network} -eq 1 ]]; then
                            printf 'owned-network\n'
                        elif [[ "${filter}" == "label=${RUN_ID}" ]]; then
                            printf 'foreign-network\n'
                        fi
                    elif [[ "${2:-}" == 'rm' ]]; then
                        printf 'remove network %s\n' "${3:-}" >>"${log}"
                        [[ "${3:-}" == 'owned-network' ]] && owned_network=0
                    fi
                    ;;
                volume)
                    if [[ "${2:-}" == 'ls' ]]; then
                        shift 2
                        while (($#)); do
                            argument=$1
                            shift
                            [[ "${argument}" == '--filter' && $# -gt 0 ]] && {
                                filter=$1
                                shift
                            }
                        done
                        printf 'inventory volume %s\n' "${filter}" >>"${log}"
                        if [[ "${filter}" == "label=${OWNERSHIP_LABEL}" && ${owned_volume} -eq 1 ]]; then
                            printf 'owned-volume\n'
                        elif [[ "${filter}" == "label=${RUN_ID}" ]]; then
                            printf 'foreign-volume\n'
                        fi
                    elif [[ "${2:-}" == 'rm' ]]; then
                        printf 'remove volume %s\n' "${3:-}" >>"${log}"
                        [[ "${3:-}" == 'owned-volume' ]] && owned_volume=0
                    fi
                    ;;
                rm)
                    printf 'remove container %s\n' "${3:-}" >>"${log}"
                    [[ "${3:-}" == 'owned-container' ]] && owned_container=0
                    ;;
            esac
        }
        cleanup_resources >/dev/null || {
            printf 'Mocked exact-label cleanup failed.\n' >&2
            exit 1
        }
        for resource_type in container network volume; do
            [[ "$(grep --fixed-strings --line-regexp --count "inventory ${resource_type} label=${OWNERSHIP_LABEL}" "${log}")" -eq 3 ]] || {
                printf 'Mocked %s inventory did not use exact ownership label.\n' "${resource_type}" >&2
                exit 1
            }
            grep --fixed-strings --line-regexp --quiet "remove ${resource_type} owned-${resource_type}" "${log}" || {
                printf 'Mocked owned %s was not removed.\n' "${resource_type}" >&2
                exit 1
            }
        done
        if grep --fixed-strings --quiet 'foreign-' "${log}"; then
            printf 'Mocked foreign bare-run-ID resource was selected.\n' >&2
            exit 1
        fi
    )
    (
        local deleted=0
        podman() {
            if [[ "$1" == 'rm' || ( "$1" == 'network' && "${2:-}" == 'rm' ) || ( "$1" == 'volume' && "${2:-}" == 'rm' ) ]]; then
                deleted=1
            fi
            if [[ "$1" == 'info' ]]; then
                return 0
            fi
            if [[ "$1" == 'ps' ]]; then
                printf 'existing-owned-container\n'
            fi
            return 0
        }
        require_command() { :; }
        build_candidate_admin_assets() { :; }
        if main >/dev/null 2>&1; then
            printf 'Mocked reused-ID collision incorrectly succeeded.\n' >&2
            exit 1
        fi
        [[ -z "$(trap -p EXIT)" && "${deleted}" -eq 0 ]] || {
            printf 'Mocked reused-ID collision installed cleanup or deleted resources.\n' >&2
            exit 1
        }
    )
    (
        log="$(mktemp)"
        trap 'rm -f -- "${log}"' EXIT
        podman() {
            printf 'podman %s\n' "$*" >>"${log}"
        }
        wp() {
            if [[ "$*" == 'option get permalink_structure' ]]; then
                printf '/%%postname%%/\n'
            else
                printf 'wp %s\n' "$*" >>"${log}"
            fi
        }
        provision_pretty_permalinks
        grep --fixed-strings --quiet 'podman exec acf-table-e2e-wordpress-' "${log}" || {
            printf 'Mocked permalink provisioning did not prepare the WordPress container.\n' >&2
            exit 1
        }
        grep --fixed-strings --line-regexp --quiet 'wp rewrite structure /%postname%/' "${log}" || {
            printf 'Mocked permalink provisioning did not set postname structure.\n' >&2
            exit 1
        }
        grep --fixed-strings --line-regexp --quiet 'wp rewrite flush' "${log}" || {
            printf 'Mocked permalink provisioning did not flush rewrite rules.\n' >&2
            exit 1
        }
        grep --fixed-strings --quiet 'wp eval ' "${log}" || {
            printf 'Mocked permalink provisioning did not write Apache rewrite rules.\n' >&2
            exit 1
        }
    )
    (
        log="$(mktemp)"
        trap 'rm -f -- "${log}"' EXIT
        export IPZ_E2E_TRANSLATION_API_URL='https://worker.example.test/'
        wp() {
            if [[ "${1:-}" == 'eval' ]]; then
                printf 'https://worker.example.test'
            else
                printf 'wp %s\n' "$*" >>"${log}"
            fi
        }
        configure_translation_api_base
        grep --fixed-strings --line-regexp --quiet 'wp config set IPZ_API_BASE_URL https://worker.example.test --type=constant --quiet' "${log}" || {
            printf 'Mocked translation API base provisioning did not write the expected constant.\n' >&2
            exit 1
        }
        export IPZ_E2E_TRANSLATION_API_URL='http://worker.example.test:8787'
        if configure_translation_api_base >/dev/null 2>&1; then
            printf 'Mocked unsafe HTTP translation API base incorrectly succeeded.\n' >&2
            exit 1
        fi
    )
    (
        unset IPZ_E2E_SITE_API_KEY IPZ_E2E_SITE_ID IPZ_E2E_ACCOUNT_EMAIL IPZ_E2E_ACCOUNT_DISPLAY_NAME
        podman() {
            printf 'Credential provisioning unexpectedly invoked podman without a fixture.\n' >&2
            return 1
        }
        provision_connect_credential
        export IPZ_E2E_SITE_API_KEY='fixture-secret-must-not-appear'
        if provision_connect_credential >/dev/null 2>&1; then
            printf 'Mocked incomplete Connect credential fixture incorrectly succeeded.\n' >&2
            exit 1
        fi
        export IPZ_E2E_SITE_ID='00000000-0000-4000-8000-000000000001'
        log="$(mktemp)"
        trap 'rm -f -- "${log}"' EXIT
        podman() {
            printf '%s\n' "$*" >>"${log}"
        }
        provision_connect_credential >/dev/null
        grep --fixed-strings --quiet -- '--env IPZ_E2E_SITE_API_KEY' "${log}" || {
            printf 'Mocked Connect fixture did not forward the API-key environment by name.\n' >&2
            exit 1
        }
        grep --fixed-strings --quiet -- '--env IPZ_E2E_SITE_ID' "${log}" || {
            printf 'Mocked Connect fixture did not forward the site-ID environment by name.\n' >&2
            exit 1
        }
        if grep --fixed-strings --quiet 'fixture-secret-must-not-appear' "${log}"; then
            printf 'Mocked Connect fixture exposed the API key in the container argv.\n' >&2
            exit 1
        fi
    )
}

start_wordpress_container() {
    local exposure=$1
    local -a publish=() plugin_mount=()
    [[ "${exposure}" == 'published' ]] && publish=(--publish 127.0.0.1:8080:80)

    if [[ "${STACK_NO_PLUGIN_MOUNT}" != '1' ]]; then
        plugin_mount=(--volume "${PLUGIN_DIR}:/var/www/html/wp-content/plugins/international-press-zone:ro,Z")
    fi

    podman run --detach --name "${WORDPRESS_CONTAINER}" --label "${OWNERSHIP_LABEL}" --network "${NETWORK}" \
        "${publish[@]}" \
        --volume "${WORDPRESS_VOLUME}:/var/www/html:Z" \
        "${plugin_mount[@]}" \
        --env "WORDPRESS_DB_HOST=${DATABASE_CONTAINER}" --env "WORDPRESS_DB_NAME=${DATABASE_NAME}" \
        --env "WORDPRESS_DB_USER=${DATABASE_USER}" --env "WORDPRESS_DB_PASSWORD=${DATABASE_PASSWORD}" \
        "${WORDPRESS_IMAGE}" >/dev/null
}

main() {
local npm_bin node_bin

require_command podman
require_command flock
require_command npm
require_command node
npm_bin="$(command -v npm)"
node_bin="$(command -v node)"
[[ "${npm_bin}" == /* && "${node_bin}" == /* ]] || {
    printf 'Resolved npm/node entrypoints must be absolute: npm=%s node=%s\n' "${npm_bin}" "${node_bin}" >&2
    exit 1
}
[[ -x "${npm_bin}" && -x "${node_bin}" ]] || {
    printf 'Resolved npm/node entrypoint is not executable: npm=%s node=%s\n' "${npm_bin}" "${node_bin}" >&2
    exit 1
}
[[ -f "${PLUGIN_DIR}/international-press-zone.php" ]] || {
    printf 'Candidate plugin entrypoint is missing: %s\n' "${PLUGIN_DIR}/international-press-zone.php" >&2
    exit 1
}
[[ -f "${FIXTURE}" ]] || {
    printf 'ACF fixture is missing: %s\n' "${FIXTURE}" >&2
    exit 1
}
[[ -f "${MANIFEST_FIXTURE}" ]] || {
    printf 'Manifest proof fixture is missing: %s\n' "${MANIFEST_FIXTURE}" >&2
    exit 1
}
install_e2e_dependencies "${npm_bin}"
if [[ "${STACK_NO_PLUGIN_MOUNT}" == '1' ]]; then
    # A clean-install run uploads its release ZIP, so no checkout admin build is mounted or needed.
    :
else
    build_candidate_admin_assets "${npm_bin}" "${node_bin}"
fi

podman info >/dev/null
assert_ownership_available || return 1
trap cleanup EXIT INT TERM

snapshot_dir="$(ipz_snapshot_dir)"
ipz_validate_snapshot "${snapshot_dir}"
podman network create --label "${OWNERSHIP_LABEL}" "${NETWORK}" >/dev/null
podman volume create --label "${OWNERSHIP_LABEL}" "${DATABASE_VOLUME}" >/dev/null
podman volume create --label "${OWNERSHIP_LABEL}" "${WORDPRESS_VOLUME}" >/dev/null
podman run --rm --label "${OWNERSHIP_LABEL}" --volume "${DATABASE_VOLUME}:/restore:Z" --volume "${snapshot_dir}:/snapshot:ro,Z" \
    docker.io/library/alpine:3.20 tar -C /restore -xf /snapshot/database.tar
podman run --rm --label "${OWNERSHIP_LABEL}" --volume "${WORDPRESS_VOLUME}:/restore:Z" --volume "${snapshot_dir}:/snapshot:ro,Z" \
    docker.io/library/alpine:3.20 tar -C /restore -xf /snapshot/wordpress.tar

podman run --detach --name "${DATABASE_CONTAINER}" --label "${OWNERSHIP_LABEL}" --network "${NETWORK}" \
    --volume "${DATABASE_VOLUME}:/var/lib/mysql:Z" \
    --env "MARIADB_DATABASE=${DATABASE_NAME}" --env "MARIADB_USER=${DATABASE_USER}" \
    --env "MARIADB_PASSWORD=${DATABASE_PASSWORD}" --env "MARIADB_ROOT_PASSWORD=${DATABASE_PASSWORD}" \
    "${MARIADB_IMAGE}" >/dev/null
wait_for_database

start_wordpress_container private
wait_for_wordpress_files
configure_translation_api_base
wp core is-installed
wp user set-role "${ADMIN_USER}" administrator
wp cache flush
wp eval '$user = get_user_by("login", "admin"); if (!$user || !$user->has_cap("manage_options")) { fwrite(STDERR, "E2E administrator lacks manage_options.\n"); exit(1); }'
wp plugin is-active advanced-custom-fields
if [[ "${STACK_NO_PLUGIN_MOUNT}" != '1' ]]; then
    wp plugin activate international-press-zone
    wp plugin is-active international-press-zone
    provision_e2e_feature_flags
    # The snapshot ships with the plugin already active, so the activation hook that
    # creates plugin tables never fires on a fresh stack; run the idempotent
    # creator and fail closed on the workflow-states table the specs exercise.
    wp eval 'global $wpdb; if (!(new \InternationalPressZone\Core\Database())->create_tables()) { fwrite(STDERR, "IPZ table creation failed.\n"); exit(1); } $table = $wpdb->prefix . "ipz_workflow_states"; if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table)) !== $table) { fwrite(STDERR, "ipz_workflow_states table missing after create_tables.\n"); exit(1); }'
    provision_connect_credential
else
    # A clean-install run uploads the release ZIP through wp-admin, which stages the
    # file in wp-content/uploads and unpacks into wp-content/plugins via
    # wp-content/upgrade. The snapshot tar restores those trees root-owned, so
    # Apache (www-data) cannot write them; hand them over before the run.
    podman exec "${WORDPRESS_CONTAINER}" bash -c 'mkdir -p /var/www/html/wp-content/uploads /var/www/html/wp-content/upgrade && chown -R www-data:www-data /var/www/html/wp-content/uploads /var/www/html/wp-content/upgrade /var/www/html/wp-content/plugins'
    # PG-34 only: provide authenticated seed/readback endpoints without mounting
    # the checkout into WordPress. This file lives solely in the disposable E2E volume.
    podman exec "${WORDPRESS_CONTAINER}" bash -c 'mkdir -p /var/www/html/wp-content/mu-plugins'
    podman exec -i "${WORDPRESS_CONTAINER}" tee /var/www/html/wp-content/mu-plugins/ipz-e2e-state.php >/dev/null <<'PHP'
<?php
/**
 * Plugin Name: IPZ E2E upgrade state helper
 * Description: Test-only authenticated state seed/readback endpoints for PG-34.
 */

declare(strict_types=1);

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

const IPZ_E2E_PLUGIN_OPTION = 'ipz_feature_flags';
const IPZ_E2E_CUSTOM_OPTION = 'ipz_e2e_upgrade_marker';
const IPZ_E2E_MARKER = 'ipz-e2e-upgrade-marker';
const IPZ_E2E_WORKFLOW_TRANSLATION_ID = 987654321;
const IPZ_E2E_POST_TITLE = 'IPZ E2E upgrade customer post';
const IPZ_E2E_POST_CONTENT = 'Customer-edited IPZ upgrade content: preserve exactly.';

function ipz_e2e_can_manage_options(): bool {
	return current_user_can( 'manage_options' );
}

add_action( 'rest_api_init', static function (): void {
	register_rest_route( 'ipz-e2e/v1', '/seed', array( 'methods' => 'POST', 'permission_callback' => 'ipz_e2e_can_manage_options', 'callback' => 'ipz_e2e_seed_state' ) );
	register_rest_route( 'ipz-e2e/v1', '/readback', array( 'methods' => 'GET', 'permission_callback' => 'ipz_e2e_can_manage_options', 'callback' => 'ipz_e2e_read_state' ) );
} );

add_action( 'admin_init', static function (): void {
		if ( isset( $_GET['ipz_e2e_nonce'] ) && '1' === sanitize_text_field( wp_unslash( $_GET['ipz_e2e_nonce'] ) ) ) {
			if ( ! ipz_e2e_can_manage_options() ) {
				wp_send_json_error( array( 'message' => 'Forbidden.' ), 403 );
			}
			wp_send_json_success( array( 'nonce' => wp_create_nonce( 'wp_rest' ) ) );
		}
	} );

function ipz_e2e_seed_state( WP_REST_Request $request ): WP_REST_Response {
	unset( $request );
	$post_id = wp_insert_post( array( 'post_title' => IPZ_E2E_POST_TITLE, 'post_content' => IPZ_E2E_POST_CONTENT, 'post_status' => 'publish', 'post_type' => 'post' ), true );
	if ( is_wp_error( $post_id ) ) {
		return new WP_REST_Response( array( 'message' => $post_id->get_error_message() ), 500 );
	}

	$options_written = array();
	$flags           = get_option( IPZ_E2E_PLUGIN_OPTION, null );
	if ( is_array( $flags ) ) {
		$flags['ipz_e2e_marker'] = IPZ_E2E_MARKER;
		update_option( IPZ_E2E_PLUGIN_OPTION, $flags );
		$options_written[] = IPZ_E2E_PLUGIN_OPTION;
	}
	update_option( IPZ_E2E_CUSTOM_OPTION, IPZ_E2E_MARKER );
	$options_written[] = IPZ_E2E_CUSTOM_OPTION;

	global $wpdb;
	$table        = $wpdb->prefix . 'ipz_workflow_states';
	$table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ) === $table;
	$workflow_row = false;
	if ( $table_exists ) {
		$columns = $wpdb->get_col( "SHOW COLUMNS FROM `{$table}`", 0 ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
		$values  = array( 'translation_id' => IPZ_E2E_WORKFLOW_TRANSLATION_ID );
		foreach ( array( 'state' => 'draft', 'notes' => IPZ_E2E_MARKER, 'metadata' => wp_json_encode( array( 'marker' => IPZ_E2E_MARKER ) ), 'created_at' => current_time( 'mysql', true ), 'updated_at' => current_time( 'mysql', true ) ) as $column => $value ) {
			if ( in_array( $column, $columns, true ) ) {
				$values[ $column ] = $value;
			}
		}
		$wpdb->delete( $table, array( 'translation_id' => IPZ_E2E_WORKFLOW_TRANSLATION_ID ) );
		$workflow_row = false !== $wpdb->insert( $table, $values );
		if ( ! $workflow_row ) {
			return new WP_REST_Response( array( 'message' => 'workflow row insert failed: ' . $wpdb->last_error ), 500 );
		}
	}

	return new WP_REST_Response( array( 'post_id' => (int) $post_id, 'options_written' => $options_written, 'table_exists' => $table_exists, 'workflow_row' => $workflow_row ) );
}

function ipz_e2e_read_state( WP_REST_Request $request ): WP_REST_Response {
	$post_id = absint( $request->get_param( 'post_id' ) );
	$post    = get_post( $post_id );
	global $wpdb;
	$table = $wpdb->prefix . 'ipz_workflow_states';
	$count = 0;
	if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ) === $table ) {
		$count = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM `{$table}` WHERE translation_id = %d", IPZ_E2E_WORKFLOW_TRANSLATION_ID ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
	}

	return new WP_REST_Response( array( 'post' => $post ? array( 'title' => $post->post_title, 'content' => $post->post_content ) : null, 'options' => array( IPZ_E2E_PLUGIN_OPTION => get_option( IPZ_E2E_PLUGIN_OPTION, null ), IPZ_E2E_CUSTOM_OPTION => get_option( IPZ_E2E_CUSTOM_OPTION, null ) ), 'workflow_row_count' => $count ) );
}
PHP
    podman exec "${WORDPRESS_CONTAINER}" bash -c 'chown -R www-data:www-data /var/www/html/wp-content/mu-plugins'
    provision_release_zip
fi
provision_manifest_e2e_fixture
provision_disabled_cron_spawn
provision_disabled_auto_updates
provision_wp_debug
assert_acf_fixture_graph
assert_rest_json_routing

podman stop "${WORDPRESS_CONTAINER}" >/dev/null
podman rm "${WORDPRESS_CONTAINER}" >/dev/null
start_wordpress_container published

printf 'ACF table E2E stack ready at %s (run %s)\n' "${SITE_URL}" "${RUN_ID}"
podman wait "${WORDPRESS_CONTAINER}" >/dev/null
}

if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
    if [[ "${1:-}" == '--self-test' ]]; then
        run_mocked_tests
    else
        run_with_port_lock "$@"
    fi
fi
