#!/usr/bin/env bash
# Create a contact (socpeople) on a thirdparty in the SANDBOX — IDEMPOTENT from
# day one (the erp#44 pattern, applied at birth).
#
# Input: a JSON object on stdin (or a file path / inline JSON in $1):
#   socid      (required) the thirdparty the contact belongs to
#   lastname   (required)
#   firstname, poste (job title), email, phone (stored as phone_pro),
#   phone_pro, phone_perso, phone_mobile, address, zip, town, country_id,
#   note_public, note_private                                    (all optional)
# Unknown fields are REFUSED, never silently dropped — notably the WIP operator
# payloads' `soc2` is NOT a Dolibarr field: the job title is `poste`.
#
# Idempotency: BEFORE any POST, list the target's existing contacts
# (GET /contacts?thirdparty_ids=<socid>) and dedupe
#   1. by case-insensitive email, then
#   2. by case-insensitive (lastname, firstname).
# On a match: emit {"id": <existing>, "deduped": true} and exit 0 without
# POSTing. Otherwise POST /contacts and emit {"id": <new>, "deduped": false}.
#
# The Dolibarr API answers HTTP 404 (not []) when a thirdparty has no contacts —
# that failure, and only that one, is treated as "no contacts yet". Any other
# listing failure ABORTS: assuming "empty" on e.g. a 403 would mint duplicates.
#
# 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}"

SRC="${1:-}"
if [[ -z "${SRC}" || "${SRC}" == "-" ]]; then INPUT="$(cat)"
elif [[ -f "${SRC}" ]]; then INPUT="$(cat "${SRC}")"
else INPUT="${SRC}"; fi            # inline JSON

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

# --- 1. Validate + map the POST body (before ANY request) ----------------------
cat > "${TMPD}/validate.py" <<'PY'
import json, sys
ALLOWED = ["socid", "lastname", "firstname", "poste", "email", "phone",
           "phone_pro", "phone_perso", "phone_mobile", "address", "zip", "town",
           "country_id", "note_public", "note_private"]
try:
    d = json.loads(sys.stdin.read() or "null")
except Exception as e:
    sys.exit("contact-create.sh: input is not valid JSON: %s" % e)
if not isinstance(d, dict):
    sys.exit("contact-create.sh: input must be a JSON object")
if "soc2" in d:
    sys.exit("contact-create.sh: REFUSED — 'soc2' is not a Dolibarr contact field "
             "(the API would silently drop it); the job title field is 'poste'")
bad = sorted(k for k in d if k not in ALLOWED)
if bad:
    sys.exit("contact-create.sh: REFUSED — unknown field(s): %s\n  allowed: %s"
             % (", ".join(bad), ", ".join(ALLOWED)))
socid = str(d.get("socid", ""))
if not socid.isdigit():
    sys.exit("contact-create.sh: 'socid' is required (numeric), got %r" % socid)
if not str(d.get("lastname", "")).strip():
    sys.exit("contact-create.sh: 'lastname' is required")
body = {"socid": socid, "lastname": str(d["lastname"]).strip()}
for k in ALLOWED:
    if k in ("socid", "lastname", "phone"):
        continue
    v = d.get(k)
    if v not in (None, ""):
        body[k] = v
# `phone` is the office line — Dolibarr contacts store it as phone_pro.
if d.get("phone") not in (None, "") and "phone_pro" not in body:
    body["phone_pro"] = d["phone"]
print(socid)
print(json.dumps(body, ensure_ascii=False))
PY
MAPPED="$(printf '%s' "${INPUT}" | python3 "${TMPD}/validate.py")"
SOCID="$(sed -n 1p <<<"${MAPPED}")"
BODY="$(sed -n 2p <<<"${MAPPED}")"
printf '%s' "${BODY}" > "${TMPD}/body.json"

# --- 2. Dedupe against the target's existing contacts --------------------------
set +e
"${W}" GET "/contacts?thirdparty_ids=${SOCID}&limit=500" \
  > "${TMPD}/list.json" 2> "${TMPD}/list.err"
rc=$?
set -e
if [[ ${rc} -ne 0 ]]; then
  if grep -q "HTTP 404" "${TMPD}/list.err"; then
    printf '[]' > "${TMPD}/list.json"    # empty contact list answers 404, not []
  else
    cat "${TMPD}/list.err" >&2
    echo "contact-create.sh: could not list contacts for socid ${SOCID} — refusing to POST blind (dedupe impossible)" >&2
    exit 1
  fi
fi

cat > "${TMPD}/match.py" <<'PY'
import json, sys
body = json.load(open(sys.argv[1]))
try:
    rows = json.load(open(sys.argv[2]))
except Exception:
    rows = []
rows = rows if isinstance(rows, list) else []
def norm(v): return str(v or "").strip().lower()
email, ln, fn = norm(body.get("email")), norm(body.get("lastname")), norm(body.get("firstname"))
hit = None
if email:
    for r in rows:
        if norm(r.get("email")) == email:
            hit = r; break
if hit is None:
    for r in rows:
        if norm(r.get("lastname")) == ln and norm(r.get("firstname")) == fn:
            hit = r; break
if hit is not None:
    print(json.dumps({"id": int(hit["id"]), "deduped": True}))
PY
MATCH="$(python3 "${TMPD}/match.py" "${TMPD}/body.json" "${TMPD}/list.json")"
if [[ -n "${MATCH}" ]]; then
  echo "contact-create.sh: contact already exists on socid ${SOCID} — deduped, no POST" >&2
  printf '%s\n' "${MATCH}"
  exit 0
fi

# --- 3. POST the new contact ----------------------------------------------------
NEWID="$("${W}" POST /contacts "${BODY}")"
if [[ ! "${NEWID}" =~ ^[0-9]+$ ]]; then
  echo "contact-create.sh: contact POST did not return an id: ${NEWID}" >&2
  exit 1
fi
printf '{"id": %s, "deduped": false}\n' "${NEWID}"
