#!/usr/bin/env bash
# =====================================================================
# seed-demo-data.sh — fill a fresh LogShip instance (clone of the golden template) with a
# small, realistic demo dataset THROUGH THE APP'S OWN ROUTES (so every record looks exactly
# like one created in the UI): one fulfilment merchant with its org/warehouse/bins, 10
# products with prices + stock, 6 consignees, 6 sales orders in mixed states, shipments
# and one invoice.
#
#   BASE=http://127.0.0.1:3001 ADMIN_USER=demo ADMIN_PASS=… MERCHANT_PASS=… ./seed-demo-data.sh
#
# Env (defaults in brackets): BASE [http://127.0.0.1:3001], ADMIN_USER/ADMIN_PASS (staff admin
# login), MERCHANT_PASS (login password of the merchant user `mustershop`), CLIENT_ID [1000000],
# ROLE_ID [1000000 = "<Client> Admin"], ORG_ID [1000000 operator org], WH_ID [1000000],
# PRICE_LIST_ID [1000003 Verkauf Brutto], PLV_NET [1000002], PLV_GROSS [1000003],
# DOCTYPE_SO [1000032 Standard Order], DOCTYPE_INV [1000023 Physical Inventory],
# SHIPPER_ID [1000000 DHL], LOCATOR_TYPE_SHELF [1000002], BP_GROUP_MERCHANT [1000001],
# BP_GROUP_STD [1000000], COUNTRY_ID [101], CURRENCY_ID [102 EUR], UOM_ID [100 Each],
# PAYMENT_TERM_ID [1000000 Immediate], TAX_ID [1000000 19 %].
#
# Idempotent: every step looks the record up by name/reference first and skips it when it
# exists, so the script can be re-run after a partial failure. Never resubmits a create.
# Needs curl + jq. Run it ON the instance (localhost) or anywhere that reaches BASE.
# =====================================================================
set -euo pipefail
BASE="${BASE:-http://127.0.0.1:3001}"
ADMIN_USER="${ADMIN_USER:?ADMIN_USER required}"; ADMIN_PASS="${ADMIN_PASS:?ADMIN_PASS required}"
MERCHANT_PASS="${MERCHANT_PASS:?MERCHANT_PASS required}"
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_SO="${DOCTYPE_SO:-1000032}"; DOCTYPE_INV="${DOCTYPE_INV:-1000023}"; SHIPPER_ID="${SHIPPER_ID:-1000000}"
LOCATOR_TYPE_SHELF="${LOCATOR_TYPE_SHELF:-1000002}"; BP_GROUP_MERCHANT="${BP_GROUP_MERCHANT:-1000001}"; BP_GROUP_STD="${BP_GROUP_STD:-1000000}"
COUNTRY_ID="${COUNTRY_ID:-101}"; CURRENCY_ID="${CURRENCY_ID:-102}"; UOM_ID="${UOM_ID:-100}"; PAYMENT_TERM_ID="${PAYMENT_TERM_ID:-1000000}"; TAX_ID="${TAX_ID:-1000000}"
TODAY="$(date +%Y-%m-%d)"; TOMORROW="$(date -d tomorrow +%Y-%m-%d 2>/dev/null || date -v+1d +%Y-%m-%d)"
JAR="$(mktemp)"; trap 'rm -f "$JAR"' EXIT
say() { printf '\n== %s\n' "$*"; }
api() { # api METHOD PATH [JSON]
  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() { # odata MODEL "<odata filter>" -> records json  (generic filter endpoint)
  local enc; enc="$(jq -rn --arg s "\$filter=$2&\$top=50" '$s|@uri')"
  api GET "/api/filters/$1/$enc"
}
first_id() { jq -r '(.records // .)[0].id // empty'; }
ok() { # ok JSON -> exits 1 with the message when status != 200 / no id
  local st; st="$(echo "$1" | jq -r '.status // 200')"
  if [ "$st" != 200 ]; then echo "  FAILED: $(echo "$1" | jq -c '{status, message, detail}' 2>/dev/null || echo "$1")" >&2; exit 1; fi
}

# --- 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: $(echo "$login" | cut -c1-300)" >&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 (org + warehouse + user + role) -----------------------------------------------
say "merchant 'Muster Shop GmbH' (org mustershop)"
M_NAME="Muster Shop GmbH"; M_SHORT="mustershop"
MBP="$(odata c_bpartner "Name eq '$M_NAME'" | first_id)"
if [ -n "$MBP" ]; then
  MORG="$(odata ad_org "Value eq '$M_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 "$M_NAME" --arg s "$M_SHORT" --arg p "$MERCHANT_PASS" \
    --argjson g "$BP_GROUP_MERCHANT" --argjson pl "$PRICE_LIST_ID" --argjson sh "$SHIPPER_ID" --argjson c "$COUNTRY_ID" '{
      companyName:$n, ceoName:"Lena Muster", shortName:$s, isFulfillmentCustomer:true, taxId:"DE811223344",
      companyEmail:"logistik@mustershop.example", street:"Hanauer Landstraße 150", zip:"60314", city:"Frankfurt am Main",
      countryId:$c, phoneWired:"+49 69 1234567-0", 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"
  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 role $(echo "$res" | jq -r .ctx.newRoleId)"
fi
[ -n "$MORG" ] && [ -n "$MWH" ] || { echo "merchant org/warehouse unresolved" >&2; exit 1; }

# --- 2. bins -------------------------------------------------------------------------------------
say "bins in warehouse $MWH"
BINS=(); for aisle in 01 02; do for pos in 01 02 03 04 05; do BINS+=("AA-$aisle-$pos"); done; done
LOC_IDS=()
for b in "${BINS[@]}"; do
  id="$(odata m_locator "M_Warehouse_ID eq $MWH and Value eq '$b'" | first_id)"
  if [ -z "$id" ]; then
    IFS=- read -r x y z <<<"$b"
    res="$(api POST /api/materials/locators/store "$(jq -n --argjson o "$MORG" --argjson w "$MWH" --arg v "$b" --arg x "$x" --arg y "$y" --arg z "$z" --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"; id="$(echo "$res" | jq -r .id)"; echo "  created $b ($id)"
  fi
  LOC_IDS+=("$id")
done
echo "  ${#LOC_IDS[@]} bins"

# --- 3. products -----------------------------------------------------------------------------------
say "products (org $MORG)"
# name|net|gross|ean|weight kg|L|W|H cm|stock
PRODUCTS=(
"Edelstahl Trinkflasche 750 ml|16.72|19.90|4260123450017|0.32|26|7|7|180"
"Bambus Schneidebrett 40x30|20.92|24.90|4260123450024|1.10|40|30|2|95"
"Yoga-Matte Premium 6 mm|33.53|39.90|4260123450031|1.25|61|15|15|60"
"LED Schreibtischlampe dimmbar|41.93|49.90|4260123450048|0.90|45|18|12|42"
"Bio-Baumwoll T-Shirt Unisex M|16.72|19.90|4260123450055|0.18|30|22|2|200"
"Kaffeebohnen Hausröstung 1 kg|15.04|17.90|4260123450062|1.02|25|10|8|150"
"Bluetooth Lautsprecher Mini|29.33|34.90|4260123450079|0.45|12|8|8|75"
"Reisekissen Memory-Schaum|21.76|25.90|4260123450086|0.35|30|28|12|88"
"Gewürz-Set 6 Gläser|24.29|28.90|4260123450093|0.95|22|15|9|64"
"Notizbuch A5 Hardcover|8.32|9.90|4260123450109|0.30|21|15|2|320"
)
PROD_IDS=(); PROD_GROSS=(); PROD_STOCK=()
i=0
for p in "${PRODUCTS[@]}"; do
  IFS='|' read -r name net gross ean wt l w h stock <<<"$p"
  id="$(odata m_product "AD_Org_ID eq $MORG and Name eq '$name'" | first_id)"
  if [ -z "$id" ]; then
    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}")"
    ok "$res"; id="$(echo "$res" | jq -r '.id // .product.id')"
    # master data the quick modal does not take (fail-soft)
    api PUT /api/materials/products/update "$(jq -n --argjson id "$id" --argjson o "$MORG" --arg ean "$ean" --argjson wt "$wt" --argjson l "$l" --argjson w "$w" --argjson h "$h" \
      '{id:$id, organizationId:$o, upc:$ean, weight:$wt, length:$l, width:$w, height:$h}')" >/dev/null || echo "  (dims/ean for $name not set)"
    echo "  created $name ($id) prices: $(echo "$res" | jq -c '[.prices[]? | .ok]')"
  fi
  PROD_IDS+=("$id"); PROD_GROSS+=("$gross"); PROD_STOCK+=("$stock"); i=$((i+1))
done
echo "  ${#PROD_IDS[@]} products"

# --- 4. initial stock (physical inventory, completed) --------------------------------------------
say "initial stock"
INV_DESC="Erstbestand Demo"
inv="$(odata m_inventory "M_Warehouse_ID eq $MWH and Description eq '$INV_DESC'" | first_id)"
if [ -n "$inv" ]; then echo "  exists ($inv)"; else
  lines="$(for n in "${!PROD_IDS[@]}"; do jq -n --argjson p "${PROD_IDS[$n]}" --argjson l "${LOC_IDS[$n]}" --argjson q "${PROD_STOCK[$n]}" --argjson ln "$(( (n+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 "$INV_DESC" --argjson lines "$lines" \
    '{organizationId:$o, warehouseId:$w, docTypeId:$d, movementDate:$dt, isActive:true, description:$desc, inventoryLines:$lines}')")"
  ok "$res"; inv="$(echo "$res" | jq -r .id)"
  res="$(api POST "/api/materials/stock-takes/$inv/document-action" '{"documentAction":"CO"}')"; ok "$res"
  echo "  inventory $inv completed: $(echo "$res" | jq -r '.DocStatus.identifier // .docStatus // "?"')"
fi

# --- 5. consignees ------------------------------------------------------------------------------------
say "consignees"
# name|street|zip|city|email
CUSTOMERS=(
"Anna Berger|Lindenstraße 12|10969|Berlin|anna.berger@example.de"
"Jonas Keller|Schellingstraße 44|80799|München|jonas.keller@example.de"
"Miriam Hoffmann|Große Bleichen 8|20354|Hamburg|m.hoffmann@example.de"
"Tobias Wagner|Kaiserstraße 77|60329|Frankfurt am Main|t.wagner@example.de"
"Sarah Neumann|Ehrenstraße 25|50672|Köln|sarah.neumann@example.de"
"Felix Brandt|Königstraße 3|70173|Stuttgart|felix.brandt@example.de"
)
CUST_BP=(); CUST_LOC=()
for c in "${CUSTOMERS[@]}"; do
  IFS='|' read -r name street zip city email <<<"$c"
  bp="$(odata c_bpartner "AD_Org_ID eq $MORG and Name eq '$name'" | first_id)"
  if [ -z "$bp" ]; then
    loc="$(api POST /api/settings/locations/store "$(jq -n --argjson o "$MORG" --arg a "$street" --arg z "$zip" --arg c "$city" --argjson co "$COUNTRY_ID" \
      '{organizationId:$o, address1:$a, postal:$z, city:$c, countryId:$co, isActive:true, isValid:true}')")"; ok "$loc"; locid="$(echo "$loc" | jq -r .id)"
    res="$(api POST /api/partners/partners/store "$(jq -n --argjson o "$MORG" --arg n "$name" --arg e "$email" --argjson g "$BP_GROUP_STD" --argjson pl "$PRICE_LIST_ID" \
      '{organizationId:$o, name:$n, EMail:$e, isActive:true, isCustomer:true, isVendor:false, partnerGroupId:$g, priceListId:$pl, languageId:"de_DE"}')")"; ok "$res"; bp="$(echo "$res" | jq -r .id)"
    bl="$(api POST "/api/partners/partners/$bp/locations" "$(jq -n --argjson o "$MORG" --argjson bp "$bp" --argjson l "$locid" --arg n "$name" \
      '{organizationId:$o, partnerId:$bp, locationId:$l, name:$n, isShipTo:true, isBillTo:true, isPayFrom:true, isRemitTo:true, isActive:true}')")"; ok "$bl"
    echo "  created $name ($bp)"
  fi
  bpl="$(odata c_bpartner_location "C_BPartner_ID eq $bp" | first_id)"
  CUST_BP+=("$bp"); CUST_LOC+=("$bpl")
done

# --- 6. sales orders ----------------------------------------------------------------------------------
say "sales orders"
# ref|customer idx|action|lines "prodIdx:qty,prodIdx:qty"
ORDERS=(
"SHOP-10021|0|CO|0:1,4:2"
"SHOP-10022|1|CO|2:1"
"SHOP-10023|2|CO|3:1,6:1,9:3"
"SHOP-10024|3|CO|1:1,8:1"
"SHOP-10025|4|PR|5:2,7:1"
"SHOP-10026|5|DR|9:5"
)
ORDER_IDS=()
for o in "${ORDERS[@]}"; do
  IFS='|' read -r ref ci action lines <<<"$o"
  oid="$(odata c_order "AD_Org_ID eq $MORG and POReference eq '$ref'" | first_id)"
  if [ -z "$oid" ]; then
    bp="${CUST_BP[$ci]}"; bpl="${CUST_LOC[$ci]}"
    ol="$(n=0; for pair in ${lines//,/ }; do pi="${pair%%:*}"; q="${pair##*:}"; n=$((n+10)); jq -n --argjson p "${PROD_IDS[$pi]}" --argjson q "$q" --argjson pr "${PROD_GROSS[$pi]}" --argjson ln "$n" \
      --arg d "$TODAY" --arg dp "$TOMORROW" --argjson u "$UOM_ID" --argjson w "$MWH" --argjson cur "$CURRENCY_ID" --argjson bp "$bp" --argjson bpl "$bpl" --argjson tax "$TAX_ID" \
      '{line:$ln, isActive:true, dateOrdered:$d, datePromised:$dp, qtyOrdered:$q, qtyEntered:$q, qtyReserved:0, qtyDelivered:0, qtyInvoiced:0, qtyLostSales:0,
        priceActual:$pr, priceEntered:$pr, priceList:$pr, priceLimit:$pr, discount:0, lineNetAmt:($pr*$q),
        M_Product_ID:{id:$p}, C_UOM_ID:{id:$u}, C_Tax_ID:{id:$tax}, M_Warehouse_ID:{id:$w}, C_Currency_ID:{id:$cur}, C_BPartner_ID:{id:$bp}, C_BPartner_Location_ID:{id:$bpl}}'; done | jq -s .)"
    res="$(api POST /api/orders/store "$(jq -n --argjson o "$MORG" --argjson dt "$DOCTYPE_SO" --arg d "$TODAY" --arg dp "$TOMORROW" --argjson bp "$bp" --argjson bpl "$bpl" \
      --argjson w "$MWH" --argjson pl "$PRICE_LIST_ID" --argjson cur "$CURRENCY_ID" --argjson sh "$SHIPPER_ID" --arg ref "$ref" --argjson ol "$ol" --argjson pt "$PAYMENT_TERM_ID" \
      '{organizationId:$o, isActive:true, isSOTrx:true, docTypeTargetId:$dt, docStatusId:"DR", dateOrdered:$d, datePromised:$dp, dateAcct:$d,
        partnerId:$bp, partnerLocationId:$bpl, billPartnerId:$bp, billPartnerLocationId:$bpl, warehouseId:$w, priceListId:$pl, currencyId:$cur, paymentTermId:$pt,
        deliveryRuleId:"A", deliveryViaRuleId:"D", invoiceRuleId:"D", priorityRuleId:"5", paymentRuleId:"P", shipperId:$sh, poReference:$ref,
        description:"Demo-Bestellung Onlineshop", orderLines:$ol}')")"
    ok "$res"; oid="$(echo "$res" | jq -r .id)"; echo "  created $ref ($oid)"
  fi
  # apply the target document action when the order is still a draft (re-runs continue here)
  st="$(odata c_order "C_Order_ID eq $oid" | jq -r '.records[0].DocStatus.id // "DR"')"
  if [ "$action" != DR ] && [ "$st" = DR ]; then
    da="$(api POST "/api/orders/$oid/document-action" "$(jq -n --arg a "$action" '{documentAction:$a}')")"; ok "$da"
    echo "  $ref -> $action: $(echo "$da" | jq -r '.DocStatus.identifier // .DocStatus.id // "?"')"
  fi
  ORDER_IDS+=("$oid")
done

# --- 7. shipments for the first three completed orders ---------------------------------------------------
say "shipments"
for ci in 0 1 2; do
  bp="${CUST_BP[$ci]}"
  if [ -n "$(odata m_inout "C_BPartner_ID eq $bp and IsSOTrx eq true" | first_id)" ]; then echo "  exists for customer $bp"; continue; fi
  res="$(api POST /api/processes/inouts/generate "$(jq -n --argjson o "$MORG" --argjson w "$MWH" --argjson bp "$bp" '{organizationId:$o, warehouseId:$w, docStatusId:"CO", partnerId:$bp}')")"
  ok "$res"; echo "  generated for customer $bp: $(echo "$res" | jq -r '.summary // .logs[0].log // "ok"' | cut -c1-120)"
done

# --- 8. one invoice (first shipment) ---------------------------------------------------------------------
say "invoice"
if [ -n "$(odata c_invoice "AD_Org_ID eq $MORG and IsSOTrx eq true" | first_id)" ]; then echo "  exists"; else
  io="$(odata m_inout "C_BPartner_ID eq ${CUST_BP[0]} and IsSOTrx eq true" | first_id)"
  res="$(api POST /api/processes/c_invoice_generate "$(jq -n --argjson o "$MORG" --argjson io "$io" --arg d "$TODAY" '{organizationId:$o, inOutId:$io, docAction:"CO", dateInvoiced:$d, consolidateDocument:false}')")"
  ok "$res"; echo "  $(echo "$res" | jq -r '.summary // .logs[0].log // "ok"' | cut -c1-160)"
fi

say "done — org $MORG / warehouse $MWH / products ${#PROD_IDS[@]} / orders ${#ORDER_IDS[@]}"
