#!/usr/bin/env bash
# Clean master taxonomy assignments first, then provision channel schema
# (attributes/categories/collections), then push product data SKUs to every
# requested outbound channel connection.
#
# Usage:
#   ./scripts/clean_master_then_publish.sh
#   DRY_RUN=1 ./scripts/clean_master_then_publish.sh
#   TARGET_SKUS="ANRO-BF3 ACH-BF3" ./scripts/clean_master_then_publish.sh
#   SKU_FILE=/home/app/fix_skus.txt ./scripts/clean_master_then_publish.sh
#
# Env:
#   APP_ROOT               PlytixMage root (default: script parent/..)
#   VENV_DIR               Virtualenv directory (default: $APP_ROOT/.venv)
#   PYTHON                 Python interpreter override
#   ALEMBIC                Alembic executable override
#   LOG_DIR                Log directory (default: $APP_ROOT/logs)
#   DRY_RUN                1 = preview only, 0 = apply (default: 0)
#   RUN_ALEMBIC            1 = run alembic upgrade head first (default: 0)
#   TARGET_SKUS            Space/comma/newline separated master SKUs to repair/push
#   SKU_FILE               Optional file containing one or more SKUs
#   INCLUDE_UNASSIGNED     1 = publish all active SKUs when no TARGET_SKUS provided
#   CHANNELS               Space-separated channels: "magento", "shopify" (default: "magento shopify")
#   MAGENTO_CONNECTION_IDS Space-separated Magento connection ids (preferred)
#   MAGENTO_CONNECTION_ID  Single Magento connection id fallback
#   SHOPIFY_CONNECTION_ID  Native Shopify connection id (optional)
#   WAIT_TIMEOUT           channel_full_publish wait timeout seconds (default: 14400)
#   BATCH_SIZE             channel_full_publish batch size (default: 500)
#   SKIP_PROJECTION_FIX    1 = skip inspect_channel_projection --fix (default: 0)
#   NO_RUN_WORKER          1 = do not run Magento worker in-process; wait for external worker/systemd instead (default: 1)
#   NO_WAIT                1 = enqueue Magento publish work only; do not wait for queue completion (default: 0)
#   SKIP_IMAGES            1 = skip product image upserts during channel_full_publish (default: 0)
#   SKIP_COLLECTION_ASSETS 1 = skip collection PDFs/hero images/thumbnails/icons during publish (default: 0)
#   FORCE_PRODUCTS         1 = force product refresh on publish (default: 1)
#   FORCE_RELATIONS        1 = force Magento relation refresh (default: 0)
#   RUN_PULL_AFTER         1 = run pull after Magento data push (default: 1)
#   RUN_MANUAL_TAXONOMY_CLEANUP       1 = cleanup canonical manual taxonomy assignments first (default: 1)
#   RUN_INACTIVE_COLLECTION_CLEANUP   1 = rewrite away from inactive collection registry rows (default: 1)
#   RUN_POLLUTION_PRUNE               1 = prune polluted taxonomy/category rows first (default: 1)
#   RUN_MAGENTO_LISTING_REPAIR        1 = relink Magento listing-path targets before publish (default: 1)
#   CLEANUP_PATH_PREFIX               manual taxonomy cleanup scope (default: kitchen-cabinets)
#   PRUNE_HUB_PATH_SLUG               polluted taxonomy prune hub (default: kitchen-cabinets)

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
if [[ -z "${ALEMBIC:-}" ]]; then
  if [[ -x "$VENV_DIR/bin/alembic" ]]; then
    ALEMBIC="$VENV_DIR/bin/alembic"
  else
    ALEMBIC="alembic"
  fi
fi

LOG_DIR="${LOG_DIR:-$APP_ROOT/logs}"
DRY_RUN="${DRY_RUN:-0}"
RUN_ALEMBIC="${RUN_ALEMBIC:-0}"
TARGET_SKUS="${TARGET_SKUS:-}"
SKU_FILE="${SKU_FILE:-}"
INCLUDE_UNASSIGNED="${INCLUDE_UNASSIGNED:-0}"
CHANNELS="${CHANNELS:-magento shopify}"
MAGENTO_CONNECTION_IDS="${MAGENTO_CONNECTION_IDS:-}"
MAGENTO_CONNECTION_ID="${MAGENTO_CONNECTION_ID:-}"
SHOPIFY_CONNECTION_ID="${SHOPIFY_CONNECTION_ID:-}"
WAIT_TIMEOUT="${WAIT_TIMEOUT:-14400}"
BATCH_SIZE="${BATCH_SIZE:-500}"
SKIP_PROJECTION_FIX="${SKIP_PROJECTION_FIX:-0}"
NO_RUN_WORKER="${NO_RUN_WORKER:-1}"
NO_WAIT="${NO_WAIT:-0}"
SKIP_IMAGES="${SKIP_IMAGES:-0}"
SKIP_COLLECTION_ASSETS="${SKIP_COLLECTION_ASSETS:-0}"
FORCE_PRODUCTS="${FORCE_PRODUCTS:-1}"
FORCE_RELATIONS="${FORCE_RELATIONS:-0}"
RUN_PULL_AFTER="${RUN_PULL_AFTER:-1}"
RUN_MANUAL_TAXONOMY_CLEANUP="${RUN_MANUAL_TAXONOMY_CLEANUP:-1}"
RUN_INACTIVE_COLLECTION_CLEANUP="${RUN_INACTIVE_COLLECTION_CLEANUP:-1}"
RUN_POLLUTION_PRUNE="${RUN_POLLUTION_PRUNE:-1}"
RUN_MAGENTO_LISTING_REPAIR="${RUN_MAGENTO_LISTING_REPAIR:-1}"
CLEANUP_PATH_PREFIX="${CLEANUP_PATH_PREFIX:-kitchen-cabinets}"
PRUNE_HUB_PATH_SLUG="${PRUNE_HUB_PATH_SLUG:-kitchen-cabinets}"

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

cd "$APP_ROOT"
export PYTHONUNBUFFERED=1

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

echo "=== clean master then publish ==="
echo "started_utc=$STAMP"
echo "app_root=$APP_ROOT"
echo "dry_run=$DRY_RUN"
echo "channels=$CHANNELS"
echo "magento_connection_ids=${MAGENTO_CONNECTION_IDS:-${MAGENTO_CONNECTION_ID:-auto}}"
echo "shopify_connection_id=${SHOPIFY_CONNECTION_ID:-auto}"
echo "include_unassigned=$INCLUDE_UNASSIGNED"
echo "wait_timeout=$WAIT_TIMEOUT"
echo "batch_size=$BATCH_SIZE"
echo "skip_projection_fix=$SKIP_PROJECTION_FIX"
echo "no_run_worker=$NO_RUN_WORKER"
echo "no_wait=$NO_WAIT"
echo "skip_images=$SKIP_IMAGES"
echo "skip_collection_assets=$SKIP_COLLECTION_ASSETS"
echo "force_products=$FORCE_PRODUCTS"
echo "force_relations=$FORCE_RELATIONS"
echo "run_pull_after=$RUN_PULL_AFTER"
echo "run_manual_taxonomy_cleanup=$RUN_MANUAL_TAXONOMY_CLEANUP"
echo "run_inactive_collection_cleanup=$RUN_INACTIVE_COLLECTION_CLEANUP"
echo "run_pollution_prune=$RUN_POLLUTION_PRUNE"
echo "run_magento_listing_repair=$RUN_MAGENTO_LISTING_REPAIR"
echo "cleanup_path_prefix=$CLEANUP_PATH_PREFIX"
echo "prune_hub_path_slug=$PRUNE_HUB_PATH_SLUG"
echo "log_file=$LOG_FILE"
echo "python=$PYTHON ($("$PYTHON" --version 2>&1 || true))"
echo "alembic=$ALEMBIC ($("$ALEMBIC" --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
}

contains_channel() {
  local wanted="$1"
  for channel in $CHANNELS; do
    if [[ "$channel" == "$wanted" ]]; then
      return 0
    fi
  done
  return 1
}

declare -a SKU_ARGS=()

collect_skus() {
  local raw=""
  if [[ -n "$TARGET_SKUS" ]]; then
    raw+=$'\n'"$TARGET_SKUS"
  fi
  if [[ -n "$SKU_FILE" && -f "$SKU_FILE" ]]; then
    raw+=$'\n'"$(cat "$SKU_FILE")"
  fi
  if [[ -z "$raw" ]]; then
    return
  fi

  declare -A seen=()
  while IFS= read -r item; do
    item="$(echo "$item" | tr ',' '\n')"
    while IFS= read -r token; do
      token="$(echo "$token" | xargs)"
      if [[ -z "$token" ]]; then
        continue
      fi
      token="${token^^}"
      if [[ -n "${seen[$token]:-}" ]]; then
        continue
      fi
      seen["$token"]=1
      SKU_ARGS+=(--sku "$token")
    done <<< "$item"
  done <<< "$raw"
}

collect_skus

declare -a APPLY_ARGS=()
if [[ "$DRY_RUN" != "1" ]]; then
  APPLY_ARGS=(--apply)
fi

declare -a CHANNEL_ARGS=()
for channel in $CHANNELS; do
  CHANNEL_ARGS+=(--channel "$channel")
done

declare -a MAGENTO_IDS=()
if [[ -n "$MAGENTO_CONNECTION_IDS" ]]; then
  while IFS= read -r magento_id; do
    magento_id="$(echo "$magento_id" | xargs)"
    if [[ -n "$magento_id" ]]; then
      MAGENTO_IDS+=("$magento_id")
    fi
  done < <(printf '%s\n' "$MAGENTO_CONNECTION_IDS" | tr ', ' '\n\n')
elif [[ -n "$MAGENTO_CONNECTION_ID" ]]; then
  MAGENTO_IDS+=("$MAGENTO_CONNECTION_ID")
fi

declare -a SHARED_CONNECTION_ARGS=()
if [[ -n "$SHOPIFY_CONNECTION_ID" ]]; then
  SHARED_CONNECTION_ARGS+=(--shopify-connection-id "$SHOPIFY_CONNECTION_ID")
fi

declare -a INCLUDE_ARGS=()
if [[ "$INCLUDE_UNASSIGNED" == "1" ]]; then
  INCLUDE_ARGS+=(--include-unassigned)
  SCHEMA_SCOPE_ARGS=(--all-products)
else
  SCHEMA_SCOPE_ARGS=()
fi

if [[ "$RUN_ALEMBIC" == "1" ]]; then
  run_step "alembic_upgrade_head" "$ALEMBIC" upgrade head
fi

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

if [[ "$RUN_INACTIVE_COLLECTION_CLEANUP" == "1" ]]; then
  CLEAN_INACTIVE_COLLECTION_CMD=(
    "$PYTHON" -m app.jobs.cleanup_inactive_collection_registry
    "${APPLY_ARGS[@]}"
  )
  run_step "cleanup_inactive_collection_registry" "${CLEAN_INACTIVE_COLLECTION_CMD[@]}"
fi

if contains_channel "magento" && [[ "$RUN_POLLUTION_PRUNE" == "1" ]]; then
  if [[ ${#MAGENTO_IDS[@]} -gt 0 ]]; then
    for magento_id in "${MAGENTO_IDS[@]}"; do
      PRUNE_CMD=(
        "$PYTHON" -m app.jobs.prune_polluted_collections
        "${APPLY_ARGS[@]}"
        --hub-path-slug "$PRUNE_HUB_PATH_SLUG"
        --magento-connection-id "$magento_id"
        --channels magento
        --forget-stale-taxonomy-links
      )
      run_step "prune_polluted_collections_magento_conn_${magento_id}" "${PRUNE_CMD[@]}"
    done
  else
    PRUNE_CMD=(
      "$PYTHON" -m app.jobs.prune_polluted_collections
      "${APPLY_ARGS[@]}"
      --hub-path-slug "$PRUNE_HUB_PATH_SLUG"
      --channels magento
      --forget-stale-taxonomy-links
    )
    run_step "prune_polluted_collections_magento_default" "${PRUNE_CMD[@]}"
  fi
fi

if contains_channel "magento" && [[ "$RUN_MAGENTO_LISTING_REPAIR" == "1" ]]; then
  if [[ ${#MAGENTO_IDS[@]} -gt 0 ]]; then
    for magento_id in "${MAGENTO_IDS[@]}"; do
      REPAIR_LISTING_CMD=(
        "$PYTHON" -m app.jobs.repair_magento_listing_path_targets
        "${APPLY_ARGS[@]}"
        --connection-id "$magento_id"
        --provision-unmatched
      )
      run_step "repair_magento_listing_path_targets_conn_${magento_id}" "${REPAIR_LISTING_CMD[@]}"
    done
  else
    REPAIR_LISTING_CMD=(
      "$PYTHON" -m app.jobs.repair_magento_listing_path_targets
      "${APPLY_ARGS[@]}"
      --provision-unmatched
    )
    run_step "repair_magento_listing_path_targets_default" "${REPAIR_LISTING_CMD[@]}"
  fi
fi

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

if contains_channel "magento"; then
  if [[ ${#MAGENTO_IDS[@]} -gt 0 ]]; then
    for magento_id in "${MAGENTO_IDS[@]}"; do
      SCHEMA_CMD=(
        "$PYTHON" -m app.jobs.channel_schema_sync
        "${APPLY_ARGS[@]}"
        --channel magento
        --magento-connection-id "$magento_id"
        "${SCHEMA_SCOPE_ARGS[@]}"
      )
      run_step "channel_schema_sync_magento_conn_${magento_id}" "${SCHEMA_CMD[@]}"
    done
  else
    SCHEMA_CMD=(
      "$PYTHON" -m app.jobs.channel_schema_sync
      "${APPLY_ARGS[@]}"
      --channel magento
      "${SCHEMA_SCOPE_ARGS[@]}"
    )
    echo "No MAGENTO_CONNECTION_IDS provided; channel_schema_sync will use the active/default Magento connection."
    run_step "channel_schema_sync_magento_default" "${SCHEMA_CMD[@]}"
  fi
fi

if contains_channel "shopify"; then
  SCHEMA_CMD=(
    "$PYTHON" -m app.jobs.channel_schema_sync
    "${APPLY_ARGS[@]}"
    --channel shopify
    "${SCHEMA_SCOPE_ARGS[@]}"
  )
  if [[ -n "$SHOPIFY_CONNECTION_ID" ]]; then
    SCHEMA_CMD+=(--shopify-connection-id "$SHOPIFY_CONNECTION_ID")
    run_step "channel_schema_sync_shopify_conn_${SHOPIFY_CONNECTION_ID}" "${SCHEMA_CMD[@]}"
  else
    run_step "channel_schema_sync_shopify_default" "${SCHEMA_CMD[@]}"
  fi
fi

declare -a PROJECTION_FIX_ARGS=()
if [[ "$DRY_RUN" != "1" && "$SKIP_PROJECTION_FIX" != "1" ]]; then
  PROJECTION_FIX_ARGS+=(--fix)
fi

declare -a COMMON_PUBLISH_ARGS=(
  "${APPLY_ARGS[@]}"
  "${SKU_ARGS[@]}"
  "${INCLUDE_ARGS[@]}"
  --skip-pull
  --taxonomy-mode link-only
  --skip-attributes
  --wait-timeout "$WAIT_TIMEOUT"
  --batch-size "$BATCH_SIZE"
)
if [[ "$FORCE_PRODUCTS" == "1" ]]; then
  COMMON_PUBLISH_ARGS+=(--force-products)
fi
if [[ "$NO_RUN_WORKER" == "1" ]]; then
  COMMON_PUBLISH_ARGS+=(--no-run-worker)
fi
if [[ "$NO_WAIT" == "1" ]]; then
  COMMON_PUBLISH_ARGS+=(--no-wait)
fi
if [[ "$SKIP_IMAGES" == "1" ]]; then
  COMMON_PUBLISH_ARGS+=(--skip-images)
fi
if [[ "$SKIP_COLLECTION_ASSETS" == "1" ]]; then
  COMMON_PUBLISH_ARGS+=(--skip-collection-assets)
fi

if contains_channel "magento"; then
  if [[ "$SKIP_PROJECTION_FIX" != "1" ]]; then
    if [[ ${#MAGENTO_IDS[@]} -gt 0 ]]; then
      for magento_id in "${MAGENTO_IDS[@]}"; do
        PROJECTION_CMD=(
          "$PYTHON" -m app.jobs.inspect_channel_projection
          --channel magento
          --connection-id "$magento_id"
          "${SKU_ARGS[@]}"
          "${PROJECTION_FIX_ARGS[@]}"
        )
        run_step "inspect_channel_projection_magento_conn_${magento_id}" "${PROJECTION_CMD[@]}"
      done
    else
      PROJECTION_CMD=(
        "$PYTHON" -m app.jobs.inspect_channel_projection
        --channel magento
        "${SKU_ARGS[@]}"
        "${PROJECTION_FIX_ARGS[@]}"
      )
      run_step "inspect_channel_projection_magento_default" "${PROJECTION_CMD[@]}"
    fi
  fi

  if [[ ${#MAGENTO_IDS[@]} -gt 0 ]]; then
    for magento_id in "${MAGENTO_IDS[@]}"; do
      MAGENTO_PUBLISH_CMD=(
        "$PYTHON" -m app.jobs.channel_full_publish
        --channel magento
        --magento-connection-id "$magento_id"
        "${COMMON_PUBLISH_ARGS[@]}"
      )
      if [[ "$FORCE_RELATIONS" == "1" ]]; then
        MAGENTO_PUBLISH_CMD+=(--force-relations)
      fi
      if [[ "$RUN_PULL_AFTER" == "1" ]]; then
        MAGENTO_PUBLISH_CMD+=(--run-pull-after)
      fi
      run_step "channel_full_publish_magento_conn_${magento_id}" "${MAGENTO_PUBLISH_CMD[@]}"
    done
  else
    MAGENTO_PUBLISH_CMD=(
      "$PYTHON" -m app.jobs.channel_full_publish
      --channel magento
      "${COMMON_PUBLISH_ARGS[@]}"
    )
    if [[ "$FORCE_RELATIONS" == "1" ]]; then
      MAGENTO_PUBLISH_CMD+=(--force-relations)
    fi
    if [[ "$RUN_PULL_AFTER" == "1" ]]; then
      MAGENTO_PUBLISH_CMD+=(--run-pull-after)
    fi
    run_step "channel_full_publish_magento_default" "${MAGENTO_PUBLISH_CMD[@]}"
  fi
fi

if contains_channel "shopify"; then
  if [[ "$SKIP_PROJECTION_FIX" != "1" ]]; then
    SHOPIFY_PROJECTION_CMD=(
      "$PYTHON" -m app.jobs.inspect_channel_projection
      --channel shopify
      "${SKU_ARGS[@]}"
      "${PROJECTION_FIX_ARGS[@]}"
    )
    if [[ -n "$SHOPIFY_CONNECTION_ID" ]]; then
      SHOPIFY_PROJECTION_CMD+=(--connection-id "$SHOPIFY_CONNECTION_ID")
      run_step "inspect_channel_projection_shopify_conn_${SHOPIFY_CONNECTION_ID}" "${SHOPIFY_PROJECTION_CMD[@]}"
    else
      run_step "inspect_channel_projection_shopify_default" "${SHOPIFY_PROJECTION_CMD[@]}"
    fi
  fi

  SHOPIFY_PUBLISH_CMD=(
    "$PYTHON" -m app.jobs.channel_full_publish
    --channel shopify
    "${COMMON_PUBLISH_ARGS[@]}"
  )
  if [[ -n "$SHOPIFY_CONNECTION_ID" ]]; then
    SHOPIFY_PUBLISH_CMD+=(--shopify-connection-id "$SHOPIFY_CONNECTION_ID")
  fi
  run_step "channel_full_publish_shopify" "${SHOPIFY_PUBLISH_CMD[@]}"
fi

echo
echo "=== completed successfully $(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"
