#!/usr/bin/env bash
# Query the Bundesagentur für Arbeit Jobsuche-API — a structured, FRESH, high-recall
# primary source for the it-stellensuche skills that complements free-text WebSearch.
# Results come back already shaped for the skill's firma_slug-based Dedup, and a
# posting's full detail (Volltext, Bewerbungs-URL, Zeitarbeit/Vermittler-Flags,
# Adresse) can be pulled with one call — no WebFetch needed for the basics.
#
# Public, read-only API. The API key is the well-known public value
# "jobboerse-jobsuche" (same key the arbeitsagentur.de site itself uses).
#
# Usage:
#   arbeitsagentur.sh search <was> <wo> [umkreis_km] [tage] [size] [arbeitszeit]
#       Lists postings for role <was> around location <wo>. One API call.
#       umkreis_km default 15, tage (veröffentlicht seit) default 30, size default 50.
#       <wo> leer lassen ("") = deutschlandweit. arbeitszeit optional, u. a. "ho"
#       (Homeoffice) für die Remote-Suche, sonst weglassen.
#       Output: one posting per line, tab-separated:
#         firma_slug <TAB> firma <TAB> titel <TAB> ort <TAB> datum <TAB> refnr <TAB> angebotsart
#       firma_slug is identical to applied-set.sh / firmaSlug() in lib/blacklist.js,
#       so a hit can be dedup'd against the applied-set/blacklist immediately.
#
#   arbeitsagentur.sh detail <refnr>
#       Full posting for a refnr (from a search row). Prints a readable field block
#       plus the complete Beschreibung — treat it like a WebFetch of the ad. Carries
#       istArbeitnehmerUeberlassung / istPrivateArbeitsvermittlung → drop Zeitarbeit/
#       Vermittler here WITHOUT a fetch; externeURL → the employer's original link
#       (bevorzugt als quelle_url, sofern es zum echten Arbeitgeber führt).
#
# Existence check: refnr is stable; a detail call that returns no Titel/Beschreibung
# (HTTP 404 / error body) means the ad is gone → verwerfen (bzw. JobOffer löschen).
set -euo pipefail

API='https://rest.arbeitsagentur.de/jobboerse/jobsuche-service/pc/v4'
KEY='jobboerse-jobsuche'

usage() { sed -n '2,25p' "$0" >&2; exit 2; }

TMP="$(mktemp)"
trap 'rm -f "$TMP"' EXIT

cmd="${1:-}"; [[ -n "$cmd" ]] && shift || usage

if [[ "$cmd" == "search" ]]; then
  was="${1:-}"; wo="${2:-}"; umkreis="${3:-15}"; tage="${4:-30}"; size="${5:-50}"; arbeitszeit="${6:-}"
  [[ -n "$was" ]] || { echo "search: <was> fehlt" >&2; usage; }
  # curl-Argumente als Array — sauberes, optionales wo=/arbeitszeit=.
  args=(-sS -m 30 -G "$API/jobs" -H "X-API-Key: $KEY"
        --data-urlencode "was=$was"
        --data-urlencode "umkreis=$umkreis"
        --data-urlencode "veroeffentlichtseit=$tage"
        --data-urlencode "size=$size")
  [[ -n "$wo" ]] && args+=(--data-urlencode "wo=$wo")
  [[ -n "$arbeitszeit" ]] && args+=(--data-urlencode "arbeitszeit=$arbeitszeit")
  curl "${args[@]}" -o "$TMP" || { echo "# Arbeitsagentur-API nicht erreichbar" >&2; exit 0; }

  RESP_FILE="$TMP" python3 <<'PY'
import os, sys, json, html, re, unicodedata

# firma_slug — MUSS mit firmaSlug() (lib/blacklist.js) & applied-set.sh übereinstimmen.
_GENDER = re.compile(r'\((?:[mwdfax](?:\s*/\s*[mwdfax])*)\)', re.I)
_LEGAL = {'gmbh','ggmbh','mbh','ug','haftungsbeschraenkt','ag','kg','kgaa','ohg','gbr',
          'se','ek','eg','ev','partg','partmbb','co','cie','inc','incorporated','llc',
          'ltd','limited','plc','corp','corporation','company','sa','sarl','sas','bv',
          'nv','oy','ab','as','aps','srl','spa'}
def firma_slug(s):
    t = html.unescape(str(s or '')).lower()
    t = t.replace('ä','ae').replace('ö','oe').replace('ü','ue').replace('ß','ss')
    t = unicodedata.normalize('NFKD', t)
    t = ''.join(c for c in t if not unicodedata.combining(c))
    t = _GENDER.sub(' ', t)
    t = re.sub(r'[^a-z0-9]+',' ',t)
    t = re.sub(r'\s+',' ',t).strip()
    if not t: return ''
    w = t.split(' '); kept = w[:]
    while len(kept) > 1 and kept[-1] in _LEGAL: kept.pop()
    return '-'.join(kept if kept else w)

def clean(v):
    return html.unescape(str(v if v is not None else '')).replace('\t',' ').replace('\n',' ').strip()

try:
    with open(os.environ['RESP_FILE'], encoding='utf-8') as fh:
        d = json.load(fh)
except Exception as e:
    print(f'# Arbeitsagentur-API: keine/ungueltige Antwort ({e})', file=sys.stderr); sys.exit(0)

jobs = d.get('stellenangebote') or []
n = 0
for j in jobs:
    slug = firma_slug(j.get('arbeitgeber'))
    if not slug:
        continue
    ort = (j.get('arbeitsort') or {}).get('ort')
    print('\t'.join([slug, clean(j.get('arbeitgeber')), clean(j.get('titel')), clean(ort),
                     clean(j.get('aktuelleVeroeffentlichungsdatum')),
                     clean(j.get('refnr')), clean(j.get('angebotsart') or 'ARBEIT')]))
    n += 1
print(f'# {n} Treffer (Arbeitsagentur, {d.get("maxErgebnisse","?")} gesamt)', file=sys.stderr)
PY

elif [[ "$cmd" == "detail" ]]; then
  ref="${1:-}"; [[ -n "$ref" ]] || { echo "detail: <refnr> fehlt" >&2; usage; }
  seg="$(REF="$ref" python3 -c 'import base64,urllib.parse,os; print(urllib.parse.quote(base64.b64encode(os.environ["REF"].encode()).decode(), safe=""))')"
  curl -sS -m 30 "$API/jobdetails/$seg" -H "X-API-Key: $KEY" -o "$TMP" \
    || { echo "# Arbeitsagentur-API nicht erreichbar" >&2; exit 0; }

  REF="$ref" RESP_FILE="$TMP" python3 <<'PY'
import os, sys, json, html
try:
    with open(os.environ['RESP_FILE'], encoding='utf-8') as fh:
        d = json.load(fh)
except Exception as e:
    print(f'# detail: ungueltige Antwort ({e})', file=sys.stderr); sys.exit(0)

titel = d.get('stellenangebotsTitel') or d.get('titel')
if not titel and not d.get('stellenangebotsBeschreibung'):
    print(f'# refnr {os.environ.get("REF","")}: keine aktive Anzeige (abgelaufen/entfernt)')
    sys.exit(0)

def u(v): return html.unescape(v) if isinstance(v, str) else v
lok = (d.get('stellenlokationen') or [{}])[0]
adr = ', '.join(x for x in [lok.get('strasse'),
      ' '.join(y for y in [lok.get('plz'), lok.get('ort')] if y)] if x)

print('FIRMA:', u(d.get('firma')))
print('TITEL:', u(titel))
print('ART:', d.get('stellenangebotsart'))
print('ZEITARBEIT_AUE:', d.get('istArbeitnehmerUeberlassung'))
print('PRIVATE_ARBEITSVERMITTLUNG:', d.get('istPrivateArbeitsvermittlung'))
print('HOMEOFFICE_MOEGLICH:', d.get('homeofficemoeglich'))
print('VERGUETUNG:', d.get('verguetungsangabe'))
print('ADRESSE:', adr or '(keine)')
print('EXTERNE_URL:', u(d.get('externeURL')) or '(keine — dann Firmen-Karriereseite suchen)')
print('REFERENZNUMMER:', d.get('referenznummer') or os.environ.get('REF',''))
print('VEROEFFENTLICHT:', d.get('datumErsteVeroeffentlichung') or d.get('aktuelleVeroeffentlichungsdatum'))
print('AENDERUNG:', d.get('aenderungsdatum'))
print('--- BESCHREIBUNG ---')
print(u(d.get('stellenangebotsBeschreibung')) or '(keine)')
PY

else
  usage
fi
