#!/usr/bin/env bash
# Timeline of all payments against KissMetrics invoices, sorted by date.
#
# Usage:
#   km-payment-timeline.sh [--year YYYY] [--since YYYY-MM-DD] [--until YYYY-MM-DD]
#
# Shows, for each KM payment: date, invoice ref, amount, type, bank account,
# and a running cumulative balance. Useful for the deferred-cycle tracking
# (compare actual cash receipts against the contractual schedule).

set -euo pipefail

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

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

WORK="$(mktemp -d -t kmtimeline.XXXXXX)"
trap 'rm -rf "${WORK}"' EXIT

"${DOL_CURL}" '/invoices?limit=100&sortfield=t.datef&sortorder=DESC' > "${WORK}/inv.json"
"${DOL_CURL}" '/bankaccounts'                                       > "${WORK}/banks.json"

# Pull per-invoice payments only for KM invoices
KM_IDS=$(python3 -c "
import json, sys
d = json.load(open(sys.argv[1]))
print(' '.join(str(r['id']) for r in d if str(r.get('socid'))==sys.argv[2]))
" "${WORK}/inv.json" "${KM_SOCID}")

mkdir -p "${WORK}/pay"
for id in ${KM_IDS}; do
  "${DOL_CURL}" "/invoices/${id}/payments" > "${WORK}/pay/${id}.json"
done

python3 - "${WORK}" "${SINCE}" "${UNTIL}" "${KM_SOCID}" <<'PY'
import json, sys, os, datetime
work, since, until, km_socid = sys.argv[1:5]

invoices = {str(r["id"]): r for r in json.load(open(os.path.join(work,"inv.json")))}
banks    = {str(b["id"]): b for b in json.load(open(os.path.join(work,"banks.json")))}

def parse_iso(s):
    return datetime.datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
def in_window(dt):
    if since and dt.date() < datetime.date.fromisoformat(since): return False
    if until and dt.date() > datetime.date.fromisoformat(until): return False
    return True

rows = []
for fn in os.listdir(os.path.join(work,"pay")):
    iid = fn[:-len(".json")]
    inv = invoices.get(iid)
    if not inv or str(inv.get("socid")) != km_socid: continue
    try: pays = json.load(open(os.path.join(work,"pay",fn)))
    except json.JSONDecodeError: continue
    for p in pays:
        dt = parse_iso(p["date"])
        if not in_window(dt): continue
        rows.append((dt, inv["ref"], float(p.get("amount") or 0), p.get("type",""), p.get("ref",""), p.get("fk_bank_line","")))

rows.sort(key=lambda r: r[0])

# We don't have a direct fk_bank_line → fk_account mapping from /bankaccounts
# alone (would need /bankaccounts/{id}/lines). Show fk_bank_line as-is and
# annotate with the IBAN-bearing account when only one account holds receipts.
# Receivable accounts in this instance: QON1, WIS2, CCA1.

print(f"{'date':<10}  {'invoice':<22}  {'amount':>10}  {'type':<6}  {'ref':<20}  {'bank_line':<10}  running")
print("-" * 110)
running = 0.0
for dt, ref, amt, typ, pref, fbl in rows:
    running += amt
    print(f"{dt.date()}  {ref:<22}  {amt:>10.2f}  {typ:<6}  {pref:<20}  bl={fbl:<8}  {running:>10.2f}")
print("-" * 110)
print(f"# {len(rows)} payment(s), net cash receipts: {running:.2f}")
print()
print("# Bank accounts known to this Dolibarr (label / IBAN-leading):")
for bid, b in sorted(banks.items(), key=lambda kv: int(kv[0])):
    iban = b.get("iban") or "-"
    label = b.get("label") or b.get("ref") or "-"
    print(f"#   id={bid} ref={b.get('ref','-'):<6} label={label:<32} country={b.get('country_code','-'):<3} iban={iban[:18]+'...' if len(iban)>18 else iban}")
PY
