#!/usr/bin/env bash
# =====================================================================
# import-merchant-catalog.sh — onboard a merchant on a LogShip instance and import a product
# catalog (JSON) WITH images, prices and opening stock, all through the app's own routes.
#
#   ADMIN_USER=demo ADMIN_PASS=… MERCHANT_PASS=… \
#   MERCHANT_NAME="Kazaar Fragrances" MERCHANT_SHORT=kazaar CATALOG=/root/demo-setup/kazaar/catalog.json \
#   ./import-merchant-catalog.sh
#
# Catalog entry (see demo-instance/kazaar/build-catalog.py):
#   { value, name, grossPrice, netPrice, upc, sku, weight, length, width, height, group1, group2,
#     classification, description, documentNote, imageUrl, stock }
#   value = search key (SKU), group1 = brand (later the iDempiere brand entity), group2 = reference /
#   "inspired by", classification = product kind. imageUrl is downloaded (cached in IMG_DIR) and sent
#   to /api/materials/products/quick-store as the product image (Strapi).
#
# Env (defaults in brackets): BASE [http://127.0.0.1:3001], ADMIN_USER/ADMIN_PASS, MERCHANT_PASS,
# MERCHANT_NAME, MERCHANT_SHORT, MERCHANT_CEO [""], MERCHANT_EMAIL [logistik@<short>.example],
# MERCHANT_STREET/ZIP/CITY, MERCHANT_TAXID, BINS [40], BIN_PREFIX [KA], CLIENT_ID [1000000],
# ROLE_ID [1000000], ORG_ID [1000000], WH_ID [1000000], PRICE_LIST_ID [1000003], PLV_NET [1000002],
# PLV_GROSS [1000003], DOCTYPE_INV [1000023], SHIPPER_ID [1000000], LOCATOR_TYPE_SHELF [1000002],
# BP_GROUP_MERCHANT [1000001], COUNTRY_ID [101], IMG_DIR [/root/demo-setup/images/<short>],
# IMG_DELAY [0.5] seconds between image downloads, LIMIT [0 = all entries].
#
# Idempotent: merchant, bins and every article (by value) are looked up first; opening stock is one
# completed Physical Inventory per run for the articles that have no stock yet. Re-run after a
# failure to continue. Needs curl + jq + python3.
# =====================================================================
set -euo pipefail
BASE="${BASE:-http://127.0.0.1:3001}"
ADMIN_USER="${ADMIN_USER:?}"; ADMIN_PASS="${ADMIN_PASS:?}"; MERCHANT_PASS="${MERCHANT_PASS:?}"
MERCHANT_NAME="${MERCHANT_NAME:?}"; MERCHANT_SHORT="${MERCHANT_SHORT:?}"; CATALOG="${CATALOG:?}"
MERCHANT_CEO="${MERCHANT_CEO:-}"; MERCHANT_EMAIL="${MERCHANT_EMAIL:-logistik@${MERCHANT_SHORT}.example}"
MERCHANT_STREET="${MERCHANT_STREET:-Musterstraße 1}"; MERCHANT_ZIP="${MERCHANT_ZIP:-10115}"; MERCHANT_CITY="${MERCHANT_CITY:-Berlin}"
MERCHANT_TAXID="${MERCHANT_TAXID:-}"
BINS="${BINS:-40}"; BIN_PREFIX="${BIN_PREFIX:-KA}"
CLIENT_ID="${CLIENT_ID:-1000000}"; ROLE_ID="${ROLE_ID:-1000000}"; ORG_ID="${ORG_ID:-1000000}"; WH_ID="${WH_ID:-1000000}"
PRICE_LIST_ID="${PRICE_LIST_ID:-1000003}"; PLV_NET="${PLV_NET:-1000002}"; PLV_GROSS="${PLV_GROSS:-1000003}"
DOCTYPE_INV="${DOCTYPE_INV:-1000023}"; SHIPPER_ID="${SHIPPER_ID:-1000000}"; LOCATOR_TYPE_SHELF="${LOCATOR_TYPE_SHELF:-1000002}"
BP_GROUP_MERCHANT="${BP_GROUP_MERCHANT:-1000001}"; COUNTRY_ID="${COUNTRY_ID:-101}"
IMG_DIR="${IMG_DIR:-/root/demo-setup/images/$MERCHANT_SHORT}"; IMG_DELAY="${IMG_DELAY:-0.5}"; LIMIT="${LIMIT:-0}"
TODAY="$(date +%Y-%m-%d)"
JAR="$(mktemp)"; trap 'rm -f "$JAR"' EXIT
mkdir -p "$IMG_DIR"
say() { printf '\n== %s\n' "$*"; }
api() { if [ $# -ge 3 ]; then curl -sS -b "$JAR" -c "$JAR" -X "$1" "$BASE$2" -H 'Content-Type: application/json' -d "$3"; else curl -sS -b "$JAR" -c "$JAR" -X "$1" "$BASE$2"; fi; }
odata() { local enc; enc="$(jq -rn --arg s "\$filter=$2&\$top=${3:-50}" '$s|@uri')"; api GET "/api/filters/$1/$enc"; }
first_id() { jq -r '(.records // .)[0].id // empty'; }
ok() { local st; st="$(echo "$1" | jq -r '.status // 200' 2>/dev/null || echo 500)"; if [ "$st" != 200 ]; then echo "  FAILED: $(echo "$1" | cut -c1-300)" >&2; return 1; fi; }
esc() { printf "%s" "$1" | sed "s/'/''/g"; }

# --- 0. sign in --------------------------------------------------------------------------------
say "sign in as $ADMIN_USER"
login="$(api POST /api/idempiere-auth/login "$(jq -n --arg u "$ADMIN_USER" --arg p "$ADMIN_PASS" '{userName:$u,password:$p,selectRole:true}')")"
echo "$login" | jq -e '.token' >/dev/null || { echo "login failed" >&2; exit 1; }
api POST /api/idempiere-auth/select-role "$(jq -n --argjson c "$CLIENT_ID" --argjson r "$ROLE_ID" --argjson o "$ORG_ID" --argjson w "$WH_ID" '{clientId:$c,roleId:$r,organizationId:$o,warehouseId:$w,language:"de_DE"}')" >/dev/null
api GET /api/idempiere-auth/authenticated | jq -e '.user.id' >/dev/null || { echo "session not authenticated" >&2; exit 1; }
echo "  ok"

# --- 1. merchant ----------------------------------------------------------------------------------
say "merchant '$MERCHANT_NAME' (org $MERCHANT_SHORT)"
MBP="$(odata c_bpartner "Name eq '$(esc "$MERCHANT_NAME")'" | first_id)"
if [ -n "$MBP" ]; then
  MORG="$(odata ad_org "Value eq '$MERCHANT_SHORT'" | first_id)"; MWH="$(odata m_warehouse "AD_Org_ID eq $MORG" | first_id)"
  echo "  exists: bpartner $MBP org $MORG warehouse $MWH"
else
  res="$(api POST /api/admin/merchant-onboarding/process "$(jq -n --arg n "$MERCHANT_NAME" --arg ceo "$MERCHANT_CEO" --arg s "$MERCHANT_SHORT" --arg p "$MERCHANT_PASS" \
    --arg e "$MERCHANT_EMAIL" --arg st "$MERCHANT_STREET" --arg z "$MERCHANT_ZIP" --arg c "$MERCHANT_CITY" --arg tax "$MERCHANT_TAXID" \
    --argjson g "$BP_GROUP_MERCHANT" --argjson pl "$PRICE_LIST_ID" --argjson sh "$SHIPPER_ID" --argjson co "$COUNTRY_ID" '{
      companyName:$n, ceoName:$ceo, shortName:$s, isFulfillmentCustomer:true, taxId:$tax, companyEmail:$e,
      street:$st, zip:$z, city:$c, countryId:$co, partnerGroupId:$g, priceListId:$pl, shipperOrderDefaultId:$sh,
      deliveryViaRuleId:"D", password:$p,
      fulfillmentOrderBaseprice:1.99, fulfillmentOrderPickprice:0.35, fulfillmentOrderQtyPickfree:1,
      fulfillmentOrderReturnBaseprice:2.49, fulfillmentOrderReturnPickprice:0.35,
      shelfRentLargePrice:5.5, shelfRentSmallPrice:3.9, rentPalettSpacePrice:12.9, fulfillmentServiceHourlyRate:39,
      paketSPrice:0.45, paketMPrice:0.65, paketLPrice:0.95, paketXLPrice:1.45, logshipMonthlyFee:99 }')")"
  echo "$res" | jq -r '.report[] | "  step \(.step) \(.entity): \(.status) \(.id // "") \(.message // "")"'
  ok "$res" || exit 1
  MBP="$(echo "$res" | jq -r .ctx.bpartnerId)"; MORG="$(echo "$res" | jq -r .ctx.newOrgId)"; MWH="$(echo "$res" | jq -r .ctx.warehouseId)"
  echo "  created: bpartner $MBP org $MORG warehouse $MWH"
fi
[ -n "$MORG" ] && [ -n "$MWH" ] || { echo "merchant org/warehouse unresolved" >&2; exit 1; }

# --- 2. bins ---------------------------------------------------------------------------------------
say "$BINS bins in warehouse $MWH"
LOC_IDS=()
for n in $(seq 1 "$BINS"); do
  aisle=$(printf '%02d' $(( (n-1)/5 + 1 ))); pos=$(printf '%02d' $(( (n-1)%5 + 1 ))); b="$BIN_PREFIX-$aisle-$pos"
  id="$(odata m_locator "M_Warehouse_ID eq $MWH and Value eq '$b'" | first_id)"
  if [ -z "$id" ]; then
    res="$(api POST /api/materials/locators/store "$(jq -n --argjson o "$MORG" --argjson w "$MWH" --arg v "$b" --arg x "$BIN_PREFIX" --arg y "$aisle" --arg z "$pos" --argjson t "$LOCATOR_TYPE_SHELF" \
      '{organizationId:$o, warehouseId:$w, value:$v, priorityNo:50, isActive:true, isDefault:false, x:$x, y:$y, z:$z, locatorTypeId:$t}')")"
    ok "$res" || exit 1; id="$(echo "$res" | jq -r .id)"
  fi
  LOC_IDS+=("$id")
done
echo "  ${#LOC_IDS[@]} bins ready"

# --- 3. articles ------------------------------------------------------------------------------------
say "articles from $CATALOG (org $MORG)"
TOTAL="$(jq length "$CATALOG")"; [ "$LIMIT" -gt 0 ] && TOTAL=$(( LIMIT < TOTAL ? LIMIT : TOTAL ))
created=0; existed=0; failed=0; NEW_IDS=(); NEW_STOCK=()
for i in $(seq 0 $((TOTAL-1))); do
  e="$(jq -c ".[$i]" "$CATALOG")"
  value="$(echo "$e" | jq -r .value)"; name="$(echo "$e" | jq -r .name)"
  id="$(odata m_product "AD_Org_ID eq $MORG and Value eq '$(esc "$value")'" | first_id)"
  if [ -n "$id" ]; then existed=$((existed+1)); continue; fi
  # image (cached per URL hash)
  imgurl="$(echo "$e" | jq -r '.imageUrl // empty')"; imgarg=()
  if [ -n "$imgurl" ]; then
    f="$IMG_DIR/$(printf '%s' "$imgurl" | md5sum | cut -c1-16).img"
    if [ ! -s "$f" ]; then
      curl -sSL -A "Mozilla/5.0" --max-time 30 -o "$f" "$imgurl" || true; sleep "$IMG_DELAY"
    fi
    if [ -s "$f" ]; then
      mime="$(file -b --mime-type "$f" 2>/dev/null || echo image/png)"
      case "$mime" in image/*) imgarg=(-F "image=@$f;type=$mime;filename=$value.${mime#image/}");; *) rm -f "$f";; esac
    fi
  fi
  gross="$(echo "$e" | jq -r .grossPrice)"; net="$(echo "$e" | jq -r .netPrice)"
  res="$(curl -sS -b "$JAR" -c "$JAR" -X POST "$BASE/api/materials/products/quick-store" \
    -F "name=$name" -F "price=$gross" -F "organizationId=$MORG" \
    -F "priceListVersionIds=[$PLV_NET,$PLV_GROSS]" -F "priceListVersionPrices={\"$PLV_NET\":$net,\"$PLV_GROSS\":$gross}" "${imgarg[@]}")"
  if ! ok "$res" 2>/dev/null; then failed=$((failed+1)); echo "  FAILED $value: $(echo "$res" | cut -c1-200)"; continue; fi
  id="$(echo "$res" | jq -r '.id // .product.id')"
  # master data (fail-soft, but reported)
  upd="$(api PUT /api/materials/products/update "$(echo "$e" | jq -c --argjson id "$id" --argjson o "$MORG" \
    '{id:$id, organizationId:$o, value:.value, sku:(.sku // ""), upc:(.upc // ""), weight:.weight, length:.length, width:.width, height:.height,
      group1:(.group1 // ""), group2:(.group2 // ""), classification:(.classification // ""), description:(.description // ""), documentNote:(.documentNote // "")}')")"
  ok "$upd" 2>/dev/null || echo "  (details not saved for $value: $(echo "$upd" | cut -c1-120))"
  created=$((created+1)); NEW_IDS+=("$id"); NEW_STOCK+=("$(echo "$e" | jq -r '.stock // 25')")
  printf '  %4d/%d %s (%s) img=%s prices=%s\n' "$((i+1))" "$TOTAL" "$value" "$id" "$(echo "$res" | jq -r .imageUploaded)" "$(echo "$res" | jq -c '[.prices[]?.ok]')"
done
echo "  created $created, existed $existed, failed $failed"

# --- 4. opening stock for the new articles (chunks of 100 lines) ----------------------------------------
if [ "${#NEW_IDS[@]}" -gt 0 ]; then
  say "opening stock for ${#NEW_IDS[@]} new articles"
  chunk=0
  for start in $(seq 0 100 $(( ${#NEW_IDS[@]} - 1 ))); do
    chunk=$((chunk+1))
    lines="$(for n in $(seq "$start" $(( start+99 ))); do [ "$n" -lt "${#NEW_IDS[@]}" ] || break; jq -n --argjson p "${NEW_IDS[$n]}" --argjson l "${LOC_IDS[$(( n % ${#LOC_IDS[@]} ))]}" --argjson q "${NEW_STOCK[$n]}" --argjson ln "$(( (n-start+1)*10 ))" \
      '{M_Product_ID:{id:$p}, M_Locator_ID:{id:$l}, qtyCount:$q, qtyBook:0, line:$ln}'; done | jq -s .)"
    res="$(api POST /api/materials/stock-takes/store "$(jq -n --argjson o "$MORG" --argjson w "$MWH" --argjson d "$DOCTYPE_INV" --arg dt "$TODAY" --arg desc "Erstbestand $MERCHANT_NAME ($TODAY) #$chunk" --argjson lines "$lines" \
      '{organizationId:$o, warehouseId:$w, docTypeId:$d, movementDate:$dt, isActive:true, description:$desc, inventoryLines:$lines}')")"
    ok "$res" || exit 1; inv="$(echo "$res" | jq -r .id)"
    res="$(api POST "/api/materials/stock-takes/$inv/document-action" '{"documentAction":"CO"}')"; ok "$res" || exit 1
    echo "  inventory $inv: $(echo "$res" | jq -r '.DocStatus.identifier // .DocStatus.id // "?"')"
  done
fi
say "done — merchant org $MORG, warehouse $MWH, articles created $created / existing $existed / failed $failed"
