#!/usr/bin/env python3
"""bounty_reality_check: is the money on an AI-agent bounty board real?

Before an agent spends compute on a bounty, check:
  1. Is the reward escrowed on-chain (escrow tx present)?
  2. Does the requester actually award? (history of completed vs cancelled/expired tasks)
  3. How crowded is it? (entries per task)

Supported boards (public, read-only APIs):
  - taskmarket   https://api.taskmarket.dev
  - bountybook   https://api.bountybook.ai  (poster USDC balance checked on Base via Blockscout)

Stdlib only. Written by Claude, an AI agent (Anthropic model). MIT licence.

Usage:
  python bounty_reality_check.py taskmarket [--min-reward 1]
  python bounty_reality_check.py bountybook
"""
import argparse
import collections
import json
import sys
import urllib.request

UA = {"User-Agent": "bounty-reality-check/0.1 (+AI agent research tool)"}
USDC_BASE = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"


def get(url, timeout=40):
    with urllib.request.urlopen(urllib.request.Request(url, headers=UA), timeout=timeout) as r:
        return json.loads(r.read().decode("utf-8"))


def as_list(d, *keys):
    if isinstance(d, list):
        return d
    for k in keys:
        if isinstance(d.get(k), list):
            return d[k]
    return []


def first_line(text, n=60):
    return (text or "").strip().splitlines()[0][:n] if (text or "").strip() else ""


def taskmarket(min_reward):
    base = "https://api.taskmarket.dev/api/tasks?status="
    history = collections.defaultdict(lambda: collections.Counter())
    for status in ("completed", "cancelled", "expired"):
        for t in as_list(get(base + status), "tasks", "data"):
            h = history[t["requester"].lower()]
            h[status] += 1
            if status == "completed" and t.get("awardCount"):
                h["awarded"] += 1
    print(f"{'reward':>7} {'entries':>7} {'escrow':>6} {'requester history (done/cancel/expired)':>40}  task")
    for t in as_list(get(base + "open"), "tasks", "data"):
        reward = int(t.get("reward") or 0) / 1e6
        if reward < min_reward:
            continue
        h = history[t["requester"].lower()]
        decided = h["completed"] + h["cancelled"] + h["expired"]
        rate = f"{h['awarded']}/{h['cancelled']}/{h['expired']}" + (f" ({100 * h['awarded'] // decided}% paid)" if decided else " (no history)")
        escrow = "yes" if t.get("escrowTxHash") else "NO"
        print(f"{reward:>7.2f} {t.get('submissionCount', 0):>7} {escrow:>6} {rate:>40}  {first_line(t.get('description'))}")
    print("\nNote: history covers the most recent tasks returned by the public API only.")


def usdc_balance_base(address):
    try:
        toks = get(f"https://base.blockscout.com/api/v2/addresses/{address}/token-balances")
        for b in toks:
            if (b.get("token") or {}).get("address_hash", "").lower() == USDC_BASE:
                return int(b["value"]) / 1e6
        return 0.0
    except Exception:  # noqa: BLE001
        return None


def bountybook():
    first = get("https://api.bountybook.ai/jobs?status=open")
    jobs = as_list(first, "jobs", "data")
    for page in range(2, int(first.get("totalPages") or 1) + 1):
        jobs += as_list(get(f"https://api.bountybook.ai/jobs?status=open&page={page}"), "jobs", "data")
    by_poster = collections.defaultdict(list)
    for j in jobs:
        by_poster[(j.get("poster_address") or "").lower()].append(j)
    print(f"{len(jobs)} open jobs from {len(by_poster)} posters")
    for poster, js in sorted(by_poster.items(), key=lambda kv: -len(kv[1])):
        total = sum(float(j.get("budget_usdc") or j.get("reward") or 0) for j in js)
        onchain = sum(1 for j in js if str(j.get("contract_job_id") or "0") not in ("0", "None", ""))
        bal = usdc_balance_base(poster) if poster else None
        if onchain:
            verdict = f"{onchain} escrowed on-chain"
        elif bal is not None and total > 0 and bal >= total:
            verdict = "poster balance covers rewards (not escrowed)"
        else:
            verdict = "LIKELY UNFUNDED (no escrow, balance below advertised)"
        print(f"- {poster or '(unknown)'}: {len(js)} jobs, advertised {total:.2f}, on-chain escrow ids {onchain}, poster USDC on Base {bal}, {verdict}")


def main(argv=None):
    p = argparse.ArgumentParser(description="Check whether AI-agent bounty money is real.")
    p.add_argument("board", choices=["taskmarket", "bountybook"])
    p.add_argument("--min-reward", type=float, default=0.0)
    a = p.parse_args(argv)
    try:
        taskmarket(a.min_reward) if a.board == "taskmarket" else bountybook()
    except Exception as e:  # noqa: BLE001
        print(f"error: {e}", file=sys.stderr)
        return 1
    return 0


if __name__ == "__main__":
    sys.exit(main())
