#!/usr/bin/env bash
# Launch-day Magento one-shot: push what manual taxonomy already defines — never invent.
#
# Source of truth = manual taxonomy assignments + existing remotes:
#   Categories  → link SKUs to manually defined L3 / collection / all-products only
#   Collections → update existing collection landings only (no remote create)
#   SKUs        → placements + shopping_* come from manual overrides; no invented paths
#
# This script NEVER:
#   - rebuilds / invents category or collection hierarchy (taxonomy-mode=link-only)
#   - provisions unmatched Magento categories
#   - creates missing collection remotes / shopping facet categories
#
# Fastest *safe* path for today. Order is intentional — do not reorder casually:
#   1) Lock (daemon-safe)
#   2) Master hygiene from manual taxonomy (optional import; cleanup + reconcile)
#   3) Per Magento connection:
#        a) Pull live catalog state
#        b) Hard-delete RTA-* / Unassembled (never sell RTA on Magento)
#        c) Prune polluted/orphan categories & collections under kitchen hub
#        d) Link products to existing manual taxonomy remotes (link-only; no invent)
#        e) Relink listing-path targets + refresh collection landings + Samples hub
#        f) Repair live category_links (add manual expected, REMOVE others)
#        g) Force products-only upsert (category_links + shopping_* + pim_avail_*)
#        h) Explicit shopping + pim_avail attribute repair (belt & suspenders)
#        i) Status/visibility repair (enable products; children not visible individually)
#        j) Orphan image cleanup, then images-only force push
#
# Magento contract after this run:
#   category_links = L3 leaf + collection + all-products (+ Samples for *-SD)
#   shopping_*     = from manual taxonomy (shopping_collection / shopping_l1 / shopping_l2)
#   pim_avail_*    = pim_avail_grab_go / pim_avail_available / pim_avail_special
#   RTA-*          = gone from Magento
#
# Image-ordering assumption for this run:
#   master image ordering and shared-SKU image selection are already correct upstream.
#   This script cleans Magento galleries and re-pushes images, but it does not invent
#   a new image order policy or suppress shared-SKU vignettes by itself.
#
# ALWAYS dry-run first:
#   DRY_RUN=1 bash scripts/launch_day_magento_push.sh
#
# Live (primary store then secondary):
#   bash scripts/launch_day_magento_push.sh
#   nohup bash scripts/launch_day_magento_push.sh \
#     >logs/launch_day_magento_push_nohup.out 2>&1 &
#
# Env:
#   APP_ROOT / VENV_DIR / PYTHON / LOG_DIR
#   MAGENTO_CONNECTION_IDS   default: "4 1"
#   HUB_PATH_SLUG            default: kitchen-cabinets
#   PATH_PREFIX              default: kitchen-cabinets
#   DRY_RUN                  1 = preview only (default: 0)
#   USE_LOCK                 1 = flock so daemon cannot double-run (default: 1)
#   WAIT_TIMEOUT             default: 21600
#   IMAGE_PUSH_LIMIT         default: 10000
#   REPAIR_DELAY_MS          category-link API delay (default: 200)
#   TAXONOMY_MODE            forced to link-only (invent/rebuild disabled)
#   ALLOW_REMOTE_PROVISION   0 = never create Magento cats/collections (default; DO NOT set 1 on launch)
#
# Step toggles (defaults tuned for launch — all ON except re-import):
#   RUN_IMPORT=0                  re-import kitchen_cabinets_taxonomy.json + brochure
#   RUN_MASTER_HYGIENE=1          cleanup manual assignments + inactive collections + facets + reconcile
#   RUN_PULL=1
#   RUN_RTA_WIPE=1
#   RUN_PRUNE=1                   purge wrong remote cats/collections (not invent)
#   RUN_REMOTE_TAXONOMY_SYNC=1    link-only product↔taxonomy (no hierarchy rebuild)
#   RUN_LISTING_REPAIR=1          relink to existing remotes only (no provision-unmatched)
#   LISTING_FAIL_ON_UNMATCHED=0   1 = hard-fail when manual paths lack Magento remotes
#   RUN_LANDING_REPAIR=1          update existing collection remotes only
#   LANDING_SYNC_PRODUCTS=0
#   RUN_SAMPLE_HUB=1
#   RUN_CATEGORY_LINK_REPAIR=1    remove wrong category_links
#   RUN_TAXONOMY_OUTBOUND=1       force products from manual listing paths
#   RUN_ATTR_PUSH=1               explicit shopping_* + pim_avail_* push
#   RUN_STATUS_VISIBILITY_FIX=1   enforce Enabled + visibility contract after attribute/taxonomy repair
#   RUN_IMAGE_CLEANUP=1           orphan gallery cleanup
#   RUN_IMAGE_PUSH=1              images-only force push
#   RUN_WORKER=1
#   OUTBOUND_LIMIT=               optional SKU cap
#   TAXONOMY_JSON / BROCHURE_FOLDER  only needed when RUN_IMPORT=1

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
APP_ROOT="$(cd "${APP_ROOT:-$SCRIPT_DIR/..}" && pwd)"
VENV_DIR="${VENV_DIR:-$APP_ROOT/.venv}"
if [[ -z "${PYTHON:-}" ]]; then
  if [[ -x "$VENV_DIR/bin/python" ]]; then
    PYTHON="$VENV_DIR/bin/python"
  else
    PYTHON="python"
  fi
fi

LOG_DIR="${LOG_DIR:-$APP_ROOT/logs}"
MAGENTO_CONNECTION_IDS="${MAGENTO_CONNECTION_IDS:-4 1}"
HUB_PATH_SLUG="${HUB_PATH_SLUG:-kitchen-cabinets}"
PATH_PREFIX="${PATH_PREFIX:-kitchen-cabinets}"
DRY_RUN="${DRY_RUN:-0}"
USE_LOCK="${USE_LOCK:-1}"
WAIT_TIMEOUT="${WAIT_TIMEOUT:-21600}"
IMAGE_PUSH_LIMIT="${IMAGE_PUSH_LIMIT:-10000}"
REPAIR_DELAY_MS="${REPAIR_DELAY_MS:-200}"
TAXONOMY_JSON="${TAXONOMY_JSON:-../plytixftp.dev.piesol.com/kitchen_cabinets_taxonomy.json}"
BROCHURE_FOLDER="${BROCHURE_FOLDER:-../plytixftp.dev.piesol.com/brochure_json_3}"

# Manual-only contract: never invent hierarchy or provision missing remotes on launch.
TAXONOMY_MODE="link-only"
ALLOW_REMOTE_PROVISION="${ALLOW_REMOTE_PROVISION:-0}"

RUN_IMPORT="${RUN_IMPORT:-0}"
RUN_MASTER_HYGIENE="${RUN_MASTER_HYGIENE:-1}"
RUN_PULL="${RUN_PULL:-1}"
RUN_RTA_WIPE="${RUN_RTA_WIPE:-1}"
RUN_PRUNE="${RUN_PRUNE:-1}"
RUN_REMOTE_TAXONOMY_SYNC="${RUN_REMOTE_TAXONOMY_SYNC:-1}"
RUN_LISTING_REPAIR="${RUN_LISTING_REPAIR:-1}"
RUN_LANDING_REPAIR="${RUN_LANDING_REPAIR:-1}"
LANDING_SYNC_PRODUCTS="${LANDING_SYNC_PRODUCTS:-0}"
RUN_SAMPLE_HUB="${RUN_SAMPLE_HUB:-1}"
RUN_CATEGORY_LINK_REPAIR="${RUN_CATEGORY_LINK_REPAIR:-1}"
RUN_TAXONOMY_OUTBOUND="${RUN_TAXONOMY_OUTBOUND:-1}"
RUN_ATTR_PUSH="${RUN_ATTR_PUSH:-1}"
RUN_STATUS_VISIBILITY_FIX="${RUN_STATUS_VISIBILITY_FIX:-1}"
RUN_IMAGE_CLEANUP="${RUN_IMAGE_CLEANUP:-1}"
RUN_IMAGE_PUSH="${RUN_IMAGE_PUSH:-1}"
RUN_WORKER="${RUN_WORKER:-1}"
OUTBOUND_LIMIT="${OUTBOUND_LIMIT:-}"

# Explicit storefront attrs for launch (ride force upsert too; this is belt & suspenders)
ATTR_FIELDS="${ATTR_FIELDS:-shopping_collection shopping_l1 shopping_l2 pim_avail_grab_go pim_avail_available pim_avail_special}"

mkdir -p "$LOG_DIR"
STAMP="$(date -u +%Y%m%dT%H%M%SZ)"
LOG_FILE="$LOG_DIR/launch_day_magento_push_${STAMP}.log"
STATUS_FILE="$LOG_DIR/launch_day_magento_push_latest.status"
LOCK_FILE="$LOG_DIR/launch_day_magento_push.lock"

cd "$APP_ROOT"
export PYTHONUNBUFFERED=1
# Hard lock: invent + remote create OFF for this launch script.
export MASTER_TAXONOMY_AUTO_INVENT=false
if [[ "$ALLOW_REMOTE_PROVISION" == "1" ]]; then
  echo "REFUSING: ALLOW_REMOTE_PROVISION=1 invents Magento categories/collections."
  echo "Launch day must follow manually defined taxonomy only."
  echo "failed:allow_remote_provision_refused:$(date -u +%Y%m%dT%H%M%SZ)" >"$STATUS_FILE"
  exit 20
fi

exec > >(tee -a "$LOG_FILE") 2>&1

echo "=== LAUNCH DAY Magento push (manual taxonomy only; connections: $MAGENTO_CONNECTION_IDS) ==="
echo "started_utc=$STAMP"
echo "app_root=$APP_ROOT"
echo "dry_run=$DRY_RUN"
echo "hub_path_slug=$HUB_PATH_SLUG"
echo "path_prefix=$PATH_PREFIX"
echo "taxonomy_mode=$TAXONOMY_MODE (forced; no invent/rebuild)"
echo "allow_remote_provision=$ALLOW_REMOTE_PROVISION (must stay 0)"
echo "wait_timeout=$WAIT_TIMEOUT"
echo "image_push_limit=$IMAGE_PUSH_LIMIT"
echo "master_taxonomy_auto_invent=$MASTER_TAXONOMY_AUTO_INVENT"
echo "run_import=$RUN_IMPORT"
echo "run_master_hygiene=$RUN_MASTER_HYGIENE"
echo "run_pull=$RUN_PULL"
echo "run_rta_wipe=$RUN_RTA_WIPE"
echo "run_prune=$RUN_PRUNE"
echo "run_remote_taxonomy_sync=$RUN_REMOTE_TAXONOMY_SYNC"
echo "run_listing_repair=$RUN_LISTING_REPAIR"
echo "run_landing_repair=$RUN_LANDING_REPAIR"
echo "run_sample_hub=$RUN_SAMPLE_HUB"
echo "run_category_link_repair=$RUN_CATEGORY_LINK_REPAIR"
echo "run_taxonomy_outbound=$RUN_TAXONOMY_OUTBOUND"
echo "run_attr_push=$RUN_ATTR_PUSH"
echo "run_status_visibility_fix=$RUN_STATUS_VISIBILITY_FIX"
echo "attr_fields=$ATTR_FIELDS"
echo "run_image_cleanup=$RUN_IMAGE_CLEANUP"
echo "run_image_push=$RUN_IMAGE_PUSH"
echo "run_worker=$RUN_WORKER"
echo "outbound_limit=${OUTBOUND_LIMIT:-<none>}"
echo "log_file=$LOG_FILE"
echo "status_file=$STATUS_FILE"
echo "python=$PYTHON ($("$PYTHON" --version 2>&1 || true))"
echo

fail() {
  local step="$1"
  local code="$2"
  echo "FAILED step=$step exit=$code"
  echo "failed:$step:$code:$(date -u +%Y%m%dT%H%M%SZ)" >"$STATUS_FILE"
  exit "$code"
}

run_step() {
  local name="$1"
  shift
  echo
  echo "------------------------------------------------------------"
  echo "STEP: $name"
  echo "CMD: $*"
  echo "started: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
  echo "------------------------------------------------------------"
  if "$@"; then
    echo "OK: $name ($(date -u +%Y-%m-%dT%H:%M:%SZ))"
  else
    local code=$?
    fail "$name" "$code"
  fi
}

if [[ "$USE_LOCK" == "1" ]]; then
  exec 9>"$LOCK_FILE"
  if command -v flock >/dev/null 2>&1; then
    if ! flock -n 9; then
      echo "Another launch_day_magento_push instance holds $LOCK_FILE — exiting."
      echo "already_running:$(date -u +%Y%m%dT%H%M%SZ)" >"$STATUS_FILE"
      exit 0
    fi
    echo "lock_acquired=$LOCK_FILE"
  else
    echo "WARNING: flock not available; USE_LOCK ignored"
  fi
fi

echo "running:$(date -u +%Y%m%dT%H%M%SZ)" >"$STATUS_FILE"

APPLY_ARGS=()
REMOVE_APPLY_ARGS=()
PRUNE_APPLY_ARGS=()
if [[ "$DRY_RUN" == "1" ]]; then
  echo "DRY_RUN=1 — preview only (no Magento writes / no hard deletes)"
else
  APPLY_ARGS=(--apply)
  REMOVE_APPLY_ARGS=(--apply --magento-action hard)
  PRUNE_APPLY_ARGS=(--purge-remote --apply)
fi

# ---------------------------------------------------------------------------
# MASTER (shared across Magento connections)
# ---------------------------------------------------------------------------
if [[ "$RUN_IMPORT" == "1" ]]; then
  IMPORT_CMD=(
    "$PYTHON" -m app.jobs.import_kitchen_cabinets_taxonomy
    --file "$TAXONOMY_JSON"
    --brochure-folder "$BROCHURE_FOLDER"
  )
  IMPORT_CMD+=("${APPLY_ARGS[@]+"${APPLY_ARGS[@]}"}")
  run_step "import_kitchen_cabinets_taxonomy" "${IMPORT_CMD[@]}"

  SEED_CMD=(
    "$PYTHON" -m app.jobs.seed_collection_brochure
    --folder "$BROCHURE_FOLDER"
  )
  SEED_CMD+=("${APPLY_ARGS[@]+"${APPLY_ARGS[@]}"}")
  run_step "seed_collection_brochure" "${SEED_CMD[@]}"
fi

if [[ "$RUN_MASTER_HYGIENE" == "1" ]]; then
  CLEAN_MANUAL_CMD=(
    "$PYTHON" -m app.jobs.cleanup_manual_taxonomy_assignments
    --path-prefix "$PATH_PREFIX"
  )
  CLEAN_MANUAL_CMD+=("${APPLY_ARGS[@]+"${APPLY_ARGS[@]}"}")
  run_step "cleanup_manual_taxonomy_assignments" "${CLEAN_MANUAL_CMD[@]}"

  CLEAN_INACTIVE_CMD=(
    "$PYTHON" -m app.jobs.cleanup_inactive_collection_registry
  )
  CLEAN_INACTIVE_CMD+=("${APPLY_ARGS[@]+"${APPLY_ARGS[@]}"}")
  run_step "cleanup_inactive_collection_registry" "${CLEAN_INACTIVE_CMD[@]}"

  FACET_CMD=(
    "$PYTHON" -m app.jobs.deactivate_facet_rooted_seo_intersections
    --hub-path-slug "$HUB_PATH_SLUG"
  )
  FACET_CMD+=("${APPLY_ARGS[@]+"${APPLY_ARGS[@]}"}")
  run_step "deactivate_facet_rooted_seo_intersections" "${FACET_CMD[@]}"

  RECONCILE_CMD=(
    "$PYTHON" -m app.jobs.reconcile_master_product_taxonomies
  )
  RECONCILE_CMD+=("${APPLY_ARGS[@]+"${APPLY_ARGS[@]}"}")
  run_step "reconcile_master_product_taxonomies" "${RECONCILE_CMD[@]}"
fi

if [[ "$RUN_REMOTE_TAXONOMY_SYNC" == "1" ]]; then
  # link-only: assign SKUs to existing manual taxonomy remotes — never rebuild hierarchy.
  SYNC_CMD=(
    "$PYTHON" -m app.jobs.sync_master_taxonomy_pipeline
    --skip-pull-remotes
    --taxonomy-mode "$TAXONOMY_MODE"
    --skip-product-push
  )
  for magento_id in $MAGENTO_CONNECTION_IDS; do
    SYNC_CMD+=(--magento-connection-id "$magento_id")
  done
  SYNC_CMD+=("${APPLY_ARGS[@]+"${APPLY_ARGS[@]}"}")
  run_step "sync_master_taxonomy_pipeline_link_only" "${SYNC_CMD[@]}"
fi

# ---------------------------------------------------------------------------
# PER MAGENTO CONNECTION
# ---------------------------------------------------------------------------
for magento_id in $MAGENTO_CONNECTION_IDS; do
  echo
  echo "############################################################"
  echo "# Magento connection $magento_id"
  echo "############################################################"

  if [[ "$RUN_PULL" == "1" ]]; then
    run_step "magento_pull_full_conn_${magento_id}" \
      "$PYTHON" -m app.jobs.magento_pull \
        --connection-id "$magento_id" \
        --full
  fi

  if [[ "$RUN_RTA_WIPE" == "1" ]]; then
    run_step "remove_rta_products_conn_${magento_id}" \
      "$PYTHON" -m app.jobs.remove_channel_products \
        --channel magento \
        --connection-id "$magento_id" \
        --selection sku_filter \
        --sku-filter "RTA-" \
        --scan-remote \
        "${REMOVE_APPLY_ARGS[@]+"${REMOVE_APPLY_ARGS[@]}"}"

    run_step "remove_unassembled_products_conn_${magento_id}" \
      "$PYTHON" -m app.jobs.remove_channel_products \
        --channel magento \
        --connection-id "$magento_id" \
        --selection master_filter \
        --master-filter "assembled or rta=Unassembled" \
        --scan-remote \
        "${REMOVE_APPLY_ARGS[@]+"${REMOVE_APPLY_ARGS[@]}"}"
  fi

  if [[ "$RUN_PRUNE" == "1" ]]; then
    run_step "prune_polluted_collections_conn_${magento_id}" \
      "$PYTHON" -m app.jobs.prune_polluted_collections \
        --hub-path-slug "$HUB_PATH_SLUG" \
        --magento-connection-id "$magento_id" \
        --channels magento \
        --include-orphans \
        --orphan-tier test \
        --orphan-tier empty_unlinked \
        --forget-stale-taxonomy-links \
        "${PRUNE_APPLY_ARGS[@]+"${PRUNE_APPLY_ARGS[@]}"}"
  fi

  if [[ "$RUN_LISTING_REPAIR" == "1" ]]; then
    # Relink to existing Magento remotes only — do NOT --provision-unmatched (that invents cats).
    # Unmatched manual paths are reported; we do not invent remotes for them.
    LISTING_CMD=(
      "$PYTHON" -m app.jobs.repair_magento_listing_path_targets
      --connection-id "$magento_id"
    )
    LISTING_CMD+=("${APPLY_ARGS[@]+"${APPLY_ARGS[@]}"}")
    echo
    echo "------------------------------------------------------------"
    echo "STEP: repair_magento_listing_path_targets_conn_${magento_id}"
    echo "CMD: ${LISTING_CMD[*]}"
    echo "started: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
    echo "------------------------------------------------------------"
    if "${LISTING_CMD[@]}"; then
      echo "OK: repair_magento_listing_path_targets_conn_${magento_id} ($(date -u +%Y-%m-%dT%H:%M:%SZ))"
    else
      local_code=$?
      if [[ "${LISTING_FAIL_ON_UNMATCHED:-0}" == "1" ]]; then
        fail "repair_magento_listing_path_targets_conn_${magento_id}" "$local_code"
      fi
      echo "WARNING: listing repair reported unmatched paths (exit=$local_code)."
      echo "         Manual taxonomy remotes were NOT invented — continue with existing remotes only."
      echo "         Set LISTING_FAIL_ON_UNMATCHED=1 to hard-fail instead."
    fi
  fi

  if [[ "$RUN_LANDING_REPAIR" == "1" ]]; then
    # Update existing collection remotes only — never create missing Magento categories.
    LANDING_CMD=(
      "$PYTHON" -m app.jobs.magento_collection_landing_repair
      --connection-id "$magento_id"
      --category-l1 "Kitchen Cabinets"
      --no-provision-missing-remote
      --no-shopping-categories
    )
    if [[ "$LANDING_SYNC_PRODUCTS" == "1" ]]; then
      LANDING_CMD+=(--sync-products)
    fi
    LANDING_CMD+=("${APPLY_ARGS[@]+"${APPLY_ARGS[@]}"}")
    run_step "magento_collection_landing_repair_conn_${magento_id}" "${LANDING_CMD[@]}"
  fi

  if [[ "$RUN_SAMPLE_HUB" == "1" ]]; then
    SAMPLE_CMD=(
      "$PYTHON" -m app.jobs.magento_sample_category_sync
      --connection-id "$magento_id"
    )
    SAMPLE_CMD+=("${APPLY_ARGS[@]+"${APPLY_ARGS[@]}"}")
    run_step "magento_sample_category_sync_conn_${magento_id}" "${SAMPLE_CMD[@]}"
  fi

  if [[ "$RUN_CATEGORY_LINK_REPAIR" == "1" ]]; then
    LINK_CMD=(
      "$PYTHON" -m app.jobs.repair_magento_product_category_links
      --connection-id "$magento_id"
      --all-assigned
      --delay-ms "$REPAIR_DELAY_MS"
    )
    if [[ "$DRY_RUN" == "1" ]]; then
      LINK_CMD+=(--dry-run)
    else
      LINK_CMD+=(--apply)
    fi
    run_step "repair_magento_product_category_links_conn_${magento_id}" "${LINK_CMD[@]}"
  fi

  if [[ "$RUN_TAXONOMY_OUTBOUND" == "1" ]]; then
    OUTBOUND_CMD=(
      "$PYTHON" -m app.jobs.push_magento_taxonomy_outbound
      --connection-id "$magento_id"
      --path-prefix "$PATH_PREFIX"
      --wait
      --wait-timeout "$WAIT_TIMEOUT"
    )
    if [[ "$RUN_WORKER" == "1" ]]; then
      OUTBOUND_CMD+=(--run-worker)
    fi
    if [[ -n "$OUTBOUND_LIMIT" ]]; then
      OUTBOUND_CMD+=(--limit "$OUTBOUND_LIMIT")
    fi
    OUTBOUND_CMD+=("${APPLY_ARGS[@]+"${APPLY_ARGS[@]}"}")
    run_step "push_magento_taxonomy_outbound_conn_${magento_id}" "${OUTBOUND_CMD[@]}"
  fi

  if [[ "$RUN_ATTR_PUSH" == "1" ]]; then
    ATTR_CMD=(
      "$PYTHON" -m app.jobs.push_magento_attributes
      --connection-id "$magento_id"
      --wait
      --wait-timeout "$WAIT_TIMEOUT"
      --sync-options
    )
    for field in $ATTR_FIELDS; do
      ATTR_CMD+=(--field "$field")
    done
    if [[ "$RUN_WORKER" == "1" ]]; then
      ATTR_CMD+=(--run-worker)
    fi
    ATTR_CMD+=("${APPLY_ARGS[@]+"${APPLY_ARGS[@]}"}")
    run_step "push_shopping_pim_avail_attrs_conn_${magento_id}" "${ATTR_CMD[@]}"
  fi

  if [[ "$RUN_STATUS_VISIBILITY_FIX" == "1" ]]; then
    STATUS_VIS_CMD=(
      "$PYTHON" -m app.jobs.magento_fix_status_visibility
      --connection-id "$magento_id"
    )
    if [[ "$DRY_RUN" == "1" ]]; then
      STATUS_VIS_CMD+=(--dry-run)
    else
      STATUS_VIS_CMD+=(--apply)
    fi
    run_step "magento_fix_status_visibility_conn_${magento_id}" "${STATUS_VIS_CMD[@]}"
  fi

  if [[ "$RUN_IMAGE_CLEANUP" == "1" ]]; then
    IMG_CLEAN_CMD=(
      "$PYTHON" -m app.jobs.magento_media_cleanup
      --connection-id "$magento_id"
      --all-assigned
    )
    if [[ "$DRY_RUN" == "1" ]]; then
      IMG_CLEAN_CMD+=(--dry-run)
    else
      IMG_CLEAN_CMD+=(--apply)
    fi
    run_step "magento_media_cleanup_orphans_conn_${magento_id}" "${IMG_CLEAN_CMD[@]}"
  fi

  if [[ "$RUN_IMAGE_PUSH" == "1" ]]; then
    IMG_PUSH_CMD=(
      "$PYTHON" -m app.jobs.push_channel_sample
      --channels magento
      --magento-connection-id "$magento_id"
      --images-only
      --limit "$IMAGE_PUSH_LIMIT"
      --wait
      --wait-timeout "$WAIT_TIMEOUT"
    )
    if [[ "$RUN_WORKER" == "1" ]]; then
      IMG_PUSH_CMD+=(--run-worker)
    fi
    if [[ "$DRY_RUN" == "1" ]]; then
      # push_channel_sample defaults to dry-run; omit --no-dry-run
      :
    else
      IMG_PUSH_CMD+=(--no-dry-run)
    fi
    run_step "push_magento_images_conn_${magento_id}" "${IMG_PUSH_CMD[@]}"
  fi
done

echo
echo "=== LAUNCH DAY Magento push completed $(date -u +%Y-%m-%dT%H:%M:%SZ) ==="
echo "ok:$(date -u +%Y%m%dT%H%M%SZ)" >"$STATUS_FILE"
echo "status_file=$STATUS_FILE"
echo "log_file=$LOG_FILE"
echo
echo "Next checks:"
echo "  - Spot-check category_links match MANUAL taxonomy (L3 + collection + all-products only)"
echo "  - Spot-check shopping_* match manual overrides; pim_avail_* present"
echo "  - Confirm no invented categories/collections were created"
echo "  - Confirm no RTA-* products remain on Magento"
echo "  - Confirm upstream master image ordering and shared-SKU image selection were correct before image push"
echo "  - Spot-check galleries after orphan cleanup + image push"
