#!/bin/sh
# taifoon avenue — the Taifoon workflows for ALSO earning elsewhere from a Grid box.
#
#   curl -fsSL https://www.taifoon.io/grid/avenue.sh | sh                  # list
#   curl -fsSL https://www.taifoon.io/grid/avenue.sh | sh -s plan storj    # what it takes, what is missing
#   curl -fsSL https://www.taifoon.io/grid/avenue.sh | sh -s check all     # what is running on this box
#   curl -fsSL https://www.taifoon.io/grid/avenue.sh | TAIFOON_AVENUE_RUN=yes sh -s run storj
#
# One catalog (www.taifoon.io/grid/avenues.json) drives this script, the node's
# TAIFOON_AVENUES switch, the Skills Bank WORKFLOW family and the MCP tool.
# Every command is verbatim from the provider's docs, with the link.
#
# Guards, in code:
#   · nothing runs without TAIFOON_AVENUE_RUN=yes
#   · a step that signs a transaction is PRINTED for you to run with your key, never run
#   · class "staking" needs TAIFOON_HOST_ALLOWS_STAKING=yes — your host's terms, your call
#   · an avenue with human steps needs TAIFOON_HUMAN_STEPS_DONE=yes; a HUMAN-only avenue never runs
#   · a missing variable stops the run before the first command
#   · mining is not in the catalog and is refused
# Nothing is sent anywhere: the only network call is fetching the catalog.
set -eu
GW="${TAIFOON_GATEWAY:-https://www.taifoon.io}"
command -v python3 >/dev/null 2>&1 || { echo "python3 is needed (apt-get install -y python3)"; exit 1; }
CAT="${TAIFOON_AVENUES_JSON:-}"
if [ -z "$CAT" ]; then
  CAT=$(curl -fsS --max-time 30 "$GW/grid/avenues.json") || { echo "could not fetch $GW/grid/avenues.json"; exit 1; }
elif [ -f "$CAT" ]; then
  CAT=$(cat "$CAT")
fi
CAT="$CAT" exec python3 - "$@" <<'PY'
import json, os, shutil, subprocess, sys

cat = json.loads(os.environ['CAT'])
A = {a['id']: a for a in cat['avenues']}
args = sys.argv[1:] or ['list']
verb, target = args[0], (args[1] if len(args) > 1 else None)
yes = lambda k: os.environ.get(k, '').lower() == 'yes'

def missing_env(a):
    return [k for k in a.get('env', []) if not os.environ.get(k)]

def pick(t):
    if t in A: return A[t]
    sys.exit(f"unknown avenue '{t}' — one of: {', '.join(A)}")

def cmd_list():
    print(f"{'id':<16}{'level':<10}{'class':<9}{'verdict':<8}{'resource':<9}name")
    for a in cat['avenues']:
        print(f"{a['id']:<16}{a['level']:<10}{a['class']:<9}{a['verdict']:<8}{a['resource']:<9}{a['name']}")
    print('\nrefused: ' + '; '.join(f"{r['what']} ({r['why']})" for r in cat.get('refused', [])))
    print('next: sh -s plan <id>   ·   sh -s check all')

def cmd_plan(a):
    print(f"{a['name']} — {a['level']} · {a['class']} · verdict {a['verdict']}")
    print(f"pays: {a['pays']}")
    for p in a.get('prereqs', []): print(f"  needs  {p}")
    for h in a.get('human', []): print(f"  HUMAN  {h}")
    for n in a.get('notes', []): print(f"  note   {n}")
    m = missing_env(a)
    if a.get('env'): print(f"  env    {' '.join(a['env'])}" + (f"   (missing: {' '.join(m)})" if m else '   (all set)'))
    print('steps:')
    for i, s in enumerate(a['steps'], 1):
        print(f"  {i}. {s['what']}{'   [SIGNS — you run it]' if s.get('signs') else ''}")
        print(f"     $ {s['run']}")
        print(f"     doc: {s.get('doc') or 'not captured'}")
    for u in a.get('unverified', []): print(f"  unverified: {u}")
    if a['class'] == 'staking': print('  staking: confirm your host allows it, then TAIFOON_HOST_ALLOWS_STAKING=yes')

def check(a):
    c = a['check']
    k = c['kind']
    try:
        if k == 'container-image':
            if not shutil.which('docker'): return 'UNKNOWN', 'docker not found'
            out = subprocess.run(['docker', 'ps', '--format', '{{.Image}}'], capture_output=True, text=True, timeout=20).stdout
            return ('RUNNING', c['match']) if c['match'] in out else ('NOT RUNNING', f"no container from {c['match']}")
        if k == 'process':
            r = subprocess.run(['pgrep', '-f', c['match']], capture_output=True, text=True, timeout=10)
            return ('RUNNING', c['match']) if r.returncode == 0 else ('NOT RUNNING', f"no process '{c['match']}'")
        if k == 'command':
            exe = c['run'].split()[0]
            if not shutil.which(exe): return 'NOT INSTALLED', exe
            r = subprocess.run(c['run'], shell=True, capture_output=True, text=True, timeout=60)
            return ('RUNNING', c['run']) if r.returncode == 0 else ('FAILING', (r.stderr or r.stdout).strip()[:160])
    except Exception as e:
        return 'UNKNOWN', str(e)[:160]
    return 'MANUAL', c.get('how', 'check by hand')

def cmd_check(t):
    rows = [check(a) + (a['id'],) for a in (cat['avenues'] if t in (None, 'all') else [pick(t)])]
    if os.environ.get('TAIFOON_AVENUE_JSON') == '1':
        print(json.dumps([{'id': i, 'state': s, 'detail': d} for s, d, i in rows])); return
    for s, d, i in rows: print(f"{i:<16}{s:<15}{d}")

def cmd_run(a):
    stop = []
    if not yes('TAIFOON_AVENUE_RUN'): stop.append('set TAIFOON_AVENUE_RUN=yes to run (nothing ran)')
    if a['level'] == 'HUMAN': stop.append('this avenue is application/UI only — follow the plan; `check` verifies it')
    if a['class'] == 'staking' and not yes('TAIFOON_HOST_ALLOWS_STAKING'): stop.append("staking: confirm your host's terms allow it, then TAIFOON_HOST_ALLOWS_STAKING=yes")
    if a.get('human') and not yes('TAIFOON_HUMAN_STEPS_DONE'): stop.append('do the HUMAN steps first, then TAIFOON_HUMAN_STEPS_DONE=yes')
    m = missing_env(a)
    if m: stop.append('missing: ' + ' '.join(m))
    if stop:
        cmd_plan(a); print(); [print('STOP  ' + s) for s in stop]; sys.exit(2)
    for i, s in enumerate(a['steps'], 1):
        if s.get('signs'):
            print(f"{i}. {s['what']}\n   SIGNS — run it yourself with your key:\n   $ {s['run']}"); continue
        print(f"{i}. {s['what']}\n   $ {s['run']}")
        r = subprocess.run(['bash', '-c', s['run']])
        if r.returncode != 0: sys.exit(f"step {i} failed ({r.returncode}) — stopped; doc: {s.get('doc')}")
    print('\n' + ' '.join(check(a)))

if verb == 'list': cmd_list()
elif verb == 'plan': cmd_plan(pick(target))
elif verb == 'check': cmd_check(target)
elif verb == 'run': cmd_run(pick(target))
else: sys.exit('usage: list | plan <id> | check [<id>|all] | run <id>')
PY
