#!/usr/bin/env python3
"""The fair version of the cross-platform comparison.

The first objection to putting Farcaster reply rates next to Lemmy comment rates
is that the platforms have different norms: Lemmy is a threaded forum where
commenting IS the interaction, while on Farcaster the ordinary response is a like
or a recast. Comparing replies to comments would then measure culture, not
presence, and would flatter the forum.

So this counts ANY engagement from another account -- reply, like, or recast --
for the same Farcaster casts, and puts it beside the reply-only figure. If the
farm channels are merely quiet-but-real, likes are where their engagement will
be hiding. Same maturity gate, same channels, same 150 casts.
"""
import json, time, urllib.parse, urllib.request
from concurrent.futures import ThreadPoolExecutor

H = 'https://snap.farcaster.xyz:3381'
FC_EPOCH = 1609459200
UA = {'user-agent': 'Mozilla/5.0'}
MATURE = 6.0


def get(url):
    return json.load(urllib.request.urlopen(urllib.request.Request(url, headers=UA), timeout=30))


def fetch(url, want=150):
    out, tok = [], None
    while len(out) < want:
        q = f"{H}/v1/castsByParent?url={urllib.parse.quote(url, safe='')}&pageSize=100&reverse=true"
        if tok:
            q += '&pageToken=' + tok
        try:
            d = get(q)
        except Exception:
            break
        ms = d.get('messages', [])
        out += ms
        tok = d.get('nextPageToken')
        if not ms or not tok:
            break
    return out[:want]


def engaged(m):
    """(any_engagement, reply_only) from an account other than the author."""
    fid, h = m['data']['fid'], m['hash']
    rep = lik = False
    try:
        d = get(f"{H}/v1/castsByParent?fid={fid}&hash={h}&pageSize=10")
        rep = any(r['data']['fid'] != fid for r in d.get('messages', []))
    except Exception:
        pass
    for rt in ('Like', 'Recast'):
        if lik:
            break
        try:
            d = get(f"{H}/v1/reactionsByCast?target_fid={fid}&target_hash={h}"
                    f"&reaction_type={rt}&pageSize=10")
            lik = any(r['data']['fid'] != fid for r in d.get('messages', []))
        except Exception:
            pass
    return (rep or lik), rep


CH = [('data', 'https://farcaster.group/data'),
      ('science', 'chain://eip155:8453/erc721:0xd953664a9b9e30fa7b3ccd00a2f9c21c7b75c5f0'),
      ('dev', 'chain://eip155:1/erc721:0x7dd4e31f1530ac682c8ea4d8016e95773e08d8b0'),
      ('founders', 'https://farcaster.group/founders'),
      ('ai', 'chain://eip155:7777777/erc721:0x5747eef366fd36684e8893bf4fe628efc2ac2d10'),
      ('base', 'https://onchainsummer.xyz')]

rows = []
print(f"{'channel':<12}{'n>6h':>6}{'reply%':>8}{'any%':>8}  (any = reply, like or recast from another account)")
print('-' * 72)
for name, url in CH:
    ms = fetch(url)
    now = time.time()
    mature = [m for m in ms if (now - (m['data']['timestamp'] + FC_EPOCH)) / 3600 >= MATURE]
    with ThreadPoolExecutor(max_workers=16) as ex:
        res = list(ex.map(engaged, mature))
    n = len(res) or 1
    any_pct = 100.0 * sum(a for a, _ in res) / n
    rep_pct = 100.0 * sum(r for _, r in res) / n
    rows.append(dict(channel=name, n=len(res), reply=rep_pct, any=any_pct))
    print(f"{name:<12}{len(res):>6}{rep_pct:>8.1f}{any_pct:>8.1f}")
json.dump(rows, open('/home/agent/fcreal/engage_result.json', 'w'), indent=1)
