#!/usr/bin/env bash
# Per-invoice payment state for KissMetrics.
#
# For each KM invoice (socid=1): total HT, sum of payments, balance, status.
# Surfaces partials (paye=0 but payments > 0) and unpaid (no payments).
#
# Usage:
#   km-payment-state.sh [--since YYYY-MM-DD]
#
# Exit 0 if every KM invoice in the date window is fully reconciled
# (balance == 0). Exit 1 if there's any partial / unpaid.

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DOL_CURL="${SCRIPT_DIR}/../../dolibarr/scripts/dol-curl.sh"

KM_SOCID=1
SINCE=""
while [[ $# -gt 0 ]]; do
  case "$1" in
    --since) SINCE="$2"; shift 2 ;;
    -h|--help) sed -n '2,12p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
    *) echo "km-payment-state.sh: unknown arg: $1" >&2; exit 2 ;;
  esac
done

SINCE_EPOCH=0
if [[ -n "${SINCE}" ]]; then
  if SINCE_EPOCH=$(date -j -f "%Y-%m-%d" "${SINCE}" "+%s" 2>/dev/null); then :
  else SINCE_EPOCH=$(date -d "${SINCE}" "+%s"); fi
fi

# 1. Pull KM invoices
INV_TMP="$(mktemp -t kmpay.inv.XXXXXX.json)"
trap 'rm -f "${INV_TMP}"' EXIT
"${DOL_CURL}" '/invoices?limit=100&sortfield=t.datef&sortorder=DESC' > "${INV_TMP}"

# 2. For each KM invoice in the window, pull its payments
KM_IDS=$(python3 -c "
import json,sys
rows = json.load(open(sys.argv[1]))
since = int(sys.argv[2])
km_socid = sys.argv[3]
ids = [str(r['id']) for r in rows if str(r.get('socid'))==km_socid and (since==0 or int(r.get('date') or 0)>=since)]
print(' '.join(ids))
" "${INV_TMP}" "${SINCE_EPOCH}" "${KM_SOCID}")

PAY_DIR="$(mktemp -d -t kmpay.XXXXXX)"
trap 'rm -rf "${INV_TMP}" "${PAY_DIR}"' EXIT
for id in ${KM_IDS}; do
  "${DOL_CURL}" "/invoices/${id}/payments" > "${PAY_DIR}/${id}.json"
done

# 3. Reconcile in python
python3 - "${INV_TMP}" "${PAY_DIR}" "${SINCE_EPOCH}" "${KM_SOCID}" <<'PY'
import json, sys, os, datetime
inv_path, pay_dir, since, km_socid = sys.argv[1], sys.argv[2], int(sys.argv[3]), sys.argv[4]
invoices = json.load(open(inv_path))

km = [r for r in invoices if str(r.get("socid"))==km_socid]
km = [r for r in km if since==0 or int(r.get("date") or 0)>=since]
km.sort(key=lambda r: int(r.get("date") or 0), reverse=True)

print(f"{'id':>4}  {'ref':<24} {'date':<10}  {'HT':>10}  {'paid':>10}  {'balance':>10}  {'state':<10}  payments")
print("-" * 130)
fails = 0
sum_ht = sum_paid = sum_bal = 0.0
for r in km:
    iid = r["id"]
    pays = []
    p = os.path.join(pay_dir, f"{iid}.json")
    if os.path.exists(p):
        try: pays = json.load(open(p))
        except json.JSONDecodeError: pays = []
    paid = sum(float(x.get("amount") or 0) for x in pays)
    ht   = float(r.get("total_ht") or 0)
    ttc  = float(r.get("total_ttc") or 0)
    # Reconcile against TTC (the contractual payable), not HT.
    balance = ttc - paid
    if abs(balance) < 0.005:
        state = "OK"
    elif paid == 0:
        state = "UNPAID"
        fails += 1
    elif abs(paid) < abs(ttc):
        state = "PARTIAL"
        fails += 1
    else:
        state = "OVERPAID"
        fails += 1
    ts = int(r.get("date") or 0)
    dt = datetime.datetime.fromtimestamp(ts).strftime("%Y-%m-%d") if ts else "-"
    pay_summary = "; ".join(
        f"{x['amount']}@{x['date'].split(' ')[0]}({x.get('type','?')})"
        for x in pays
    ) or "(none)"
    print(f"{iid:>4}  {r['ref']:<24} {dt:<10}  {ht:>10.2f}  {paid:>10.2f}  {balance:>10.2f}  {state:<10}  {pay_summary}")
    sum_ht += ht
    sum_paid += paid
    sum_bal  += balance
print("-" * 130)
print(f"{'TOTAL':>40}  {sum_ht:>10.2f}  {sum_paid:>10.2f}  {sum_bal:>10.2f}")
print(f"# {len(km)} invoice(s), {fails} not fully reconciled")
sys.exit(0 if fails == 0 else 1)
PY
