#!/usr/bin/env bash
# Update an EXISTING thirdparty's DOSSIER fields in the SANDBOX — allowlisted.
#
# The ledger grammar's "thirdparty complete": a fiche is completed with identity /
# address / typology / notes / national ids — never with ledger-side state. This
# script therefore REFUSES any field outside the allowlist below (code_client,
# client/fournisseur roles, remises, payment conditions, bank details… are
# creation- or ledger-side concerns, not dossier updates). Refusal happens BEFORE
# any request is sent, exits non-zero, and names the offending key(s).
#
# Usage:
#   thirdparty-update.sh <socid> <json-file-or-inline-json>
#   thirdparty-update.sh <socid> -             # fields JSON on stdin
#   thirdparty-update.sh                       # promote form: {"socid":N,"fields":{…}} on stdin
#
# Read-back contract (anti-silent-partial-apply):
#   GET before → PUT → GET after. stdout is a JSON diff of the REQUESTED fields
#   that actually changed: {"id":N,"changed":{field:{"before":…,"after":…}}}.
#   A requested field whose read-back differs from the requested value makes the
#   script exit non-zero — a write that "didn't take" is an error, never a silent
#   partial apply. Re-running the same update yields "changed": {} (idempotent).
#
# All requests go through dol-write.sh (or $DOL_WRITE), which refuses any host
# that is not the sandbox.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
W="${DOL_WRITE:-${SCRIPT_DIR}/dol-write.sh}"

SOCID_ARG="${1:-}"
SRC="${2:-}"
if [[ -n "${SOCID_ARG}" ]]; then
  if [[ -z "${SRC}" || "${SRC}" == "-" ]]; then INPUT="$(cat)"
  elif [[ -f "${SRC}" ]]; then INPUT="$(cat "${SRC}")"
  else INPUT="${SRC}"; fi          # inline JSON
else
  INPUT="$(cat)"                   # promote form: {"socid":N,"fields":{…}}
fi

TMPD="$(mktemp -d -t tpupd.XXXXXX)"; trap 'rm -rf "${TMPD}"' EXIT

# --- 1. Validate against the dossier allowlist (before ANY request) ------------
cat > "${TMPD}/validate.py" <<'PY'
import json, sys
# The dossier allowlist — identity / address / typology / notes / national ids.
# Everything else is refused: ledger-side or creation-side fields have their own
# ops, and silently forwarding them would let a typo mutate the ledger.
ALLOWED = ["name", "name_alias", "address", "zip", "town", "state_id",
           "region_id", "country_id", "country_code", "email", "url", "phone",
           "typent_id", "effectif_id", "note_public", "note_private",
           "idprof1", "idprof2", "idprof3", "idprof4", "idprof5", "idprof6",
           "tva_intra"]
socid_arg = sys.argv[1]
try:
    d = json.loads(sys.stdin.read() or "null")
except Exception as e:
    sys.exit("thirdparty-update.sh: input is not valid JSON: %s" % e)
if socid_arg:
    socid, fields = socid_arg, d
else:
    if not isinstance(d, dict) or "socid" not in d or "fields" not in d:
        sys.exit("thirdparty-update.sh: stdin form must be {\"socid\":N,\"fields\":{...}}")
    socid, fields = d["socid"], d["fields"]
socid = str(socid)
if not socid.isdigit():
    sys.exit("thirdparty-update.sh: socid must be numeric, got %r" % socid)
if not isinstance(fields, dict) or not fields:
    sys.exit("thirdparty-update.sh: no fields to update (fields must be a non-empty JSON object)")
bad = sorted(k for k in fields if k not in ALLOWED)
if bad:
    sys.exit("thirdparty-update.sh: REFUSED — field(s) outside the dossier allowlist: %s\n"
             "  allowed: %s" % (", ".join(bad), ", ".join(ALLOWED)))
print(socid)
print(json.dumps(fields, ensure_ascii=False))
PY
MAPPED="$(printf '%s' "${INPUT}" | python3 "${TMPD}/validate.py" "${SOCID_ARG}")"
SOCID="$(sed -n 1p <<<"${MAPPED}")"
BODY="$(sed -n 2p <<<"${MAPPED}")"
printf '%s' "${BODY}" > "${TMPD}/requested.json"

# --- 2. GET before → PUT → GET after -------------------------------------------
"${W}" GET "/thirdparties/${SOCID}" > "${TMPD}/before.json"
"${W}" PUT "/thirdparties/${SOCID}" "${BODY}" > /dev/null
"${W}" GET "/thirdparties/${SOCID}" > "${TMPD}/after.json"

# --- 3. Per-field diff of the requested fields; fail if a field didn't take ----
cat > "${TMPD}/diff.py" <<'PY'
import json, sys
before = json.load(open(sys.argv[1]))
after  = json.load(open(sys.argv[2]))
req    = json.load(open(sys.argv[3]))
socid  = int(sys.argv[4])
def s(v):  # Dolibarr returns most scalars as strings; None ≡ ""
    return "" if v is None else str(v)
changed, failed = {}, []
for k, want in req.items():
    b, a = before.get(k), after.get(k)
    if s(a) != s(want):
        failed.append((k, want, a))
    if s(b) != s(a):
        changed[k] = {"before": b, "after": a}
print(json.dumps({"id": socid, "changed": changed}, ensure_ascii=False))
if failed:
    for k, want, got in failed:
        sys.stderr.write("thirdparty-update.sh: field %r did NOT take — requested %r, read back %r\n"
                         % (k, want, got))
    sys.exit(1)
PY
python3 "${TMPD}/diff.py" "${TMPD}/before.json" "${TMPD}/after.json" "${TMPD}/requested.json" "${SOCID}"
