#!/usr/bin/env python3
"""Scores an ad-library cohort run into src/data/ads-bench/<date>.json and publishes per-call records
plus CSV tables under public/evidence/ads-runs/<date>/. Deterministic.
Usage: scripts/score-ads-cohort.py evidence/fixtures/ads-cohort-v1.json evidence/ads-runs/YYYY-MM-DD"""
import json, sys, os, glob, math, statistics, csv
cohort = json.load(open(sys.argv[1])); d = sys.argv[2]; date = os.path.basename(d.rstrip('/'))
ADV = [a['name'] for a in cohort['advertisers']]
LIST = {('meta','sc_company'): 0.00188, ('meta','sc_search'): 0.00188, ('meta','apify'): 0.005, ('google','sc_advertisers'): 0.00188, ('google','sc_ads'): 0.00188,
        ('google','serpapi'): 0.015, ('tiktok','sc'): 0.00188, ('tiktok','apify'): 0.003, ('tiktok','tikhub'): 0.001, ('linkedin','sc'): 0.00188}
PER_RESULT = {('meta','apify'), ('tiktok','apify')}
recs = [json.load(open(f)) for f in sorted(glob.glob(os.path.join(d, '*.json')))]
def nz(v): return v not in (None, '', 0, '0', [], {})
def ads(r):
    """Return (list_of_ads, fieldmap) where fieldmap maps canonical field -> extractor."""
    p, prov, b = r['_run']['platform'], r['_run']['provider'], r['response']
    if not isinstance(b, (dict, list)): return [], {}
    if isinstance(b, dict) and (b.get('detail') or b.get('error') or b.get('_nonjson')): return None, {}
    try:
        if p == 'meta' and prov == 'sc_company': L = b['results']; F = dict(creative=lambda a: (a.get('snapshot') or {}).get('images') or (a.get('snapshot') or {}).get('videos') or (a.get('snapshot') or {}).get('cards'), start=lambda a: a.get('start_date'), end=lambda a: a.get('end_date'), platforms=lambda a: a.get('publisher_platform'), status=lambda a: a.get('is_active'), spend=lambda a: a.get('spend'), reach=lambda a: a.get('reach_estimate'), landing=lambda a: (a.get('snapshot') or {}).get('link_url'), text=lambda a: (a.get('snapshot') or {}).get('body'))
        elif p == 'meta' and prov == 'sc_search': L = b['searchResults']; F = dict(creative=lambda a: (a.get('snapshot') or {}).get('images') or (a.get('snapshot') or {}).get('videos') or (a.get('snapshot') or {}).get('cards'), start=lambda a: a.get('start_date'), end=lambda a: a.get('end_date'), platforms=lambda a: a.get('publisher_platform'), status=lambda a: a.get('is_active'), spend=lambda a: a.get('spend'), reach=lambda a: a.get('reach_estimate'), landing=lambda a: (a.get('snapshot') or {}).get('link_url'), text=lambda a: (a.get('snapshot') or {}).get('body'))
        elif p == 'meta' and prov == 'apify': L = b if isinstance(b, list) else []; F = dict(creative=lambda a: (a.get('snapshot') or {}).get('images') or (a.get('snapshot') or {}).get('videos') or (a.get('snapshot') or {}).get('cards'), start=lambda a: a.get('startDate'), end=lambda a: a.get('endDate'), platforms=lambda a: a.get('publisherPlatform'), status=lambda a: a.get('isActive'), spend=lambda a: a.get('spend'), reach=lambda a: a.get('reachEstimate') or a.get('impressionsWithIndex'), landing=lambda a: (a.get('snapshot') or {}).get('link_url') or (a.get('snapshot') or {}).get('linkUrl'), text=lambda a: (a.get('snapshot') or {}).get('body'))
        elif p == 'google' and prov == 'sc_advertisers': L = b['advertisers']; F = dict(creative=lambda a: None, start=lambda a: None, end=lambda a: None, platforms=lambda a: None, status=lambda a: None, spend=lambda a: None, reach=lambda a: a.get('number_of_ads_estimate'), landing=lambda a: None, text=lambda a: a.get('name'))
        elif p == 'google' and prov == 'sc_ads': L = b['ads']; F = dict(creative=lambda a: a.get('imageUrl') or a.get('adUrl'), start=lambda a: a.get('firstShown'), end=lambda a: a.get('lastShown'), platforms=lambda a: a.get('format'), status=lambda a: None, spend=lambda a: None, reach=lambda a: None, landing=lambda a: a.get('domain'), text=lambda a: None)
        elif p == 'google' and prov == 'serpapi': L = b['ad_creatives']; F = dict(creative=lambda a: a.get('image') or a.get('details_link'), start=lambda a: a.get('first_shown'), end=lambda a: a.get('last_shown'), platforms=lambda a: a.get('format'), status=lambda a: None, spend=lambda a: None, reach=lambda a: None, landing=lambda a: a.get('target_domain'), text=lambda a: None)
        elif p == 'tiktok' and prov == 'sc': L = b['ads']; F = dict(creative=lambda a: a.get('videos') or a.get('image_urls'), start=lambda a: a.get('first_shown_date'), end=lambda a: a.get('last_shown_date'), platforms=lambda a: 'tiktok', status=lambda a: a.get('audit_status'), spend=lambda a: a.get('spent'), reach=lambda a: a.get('impression') or a.get('estimated_audience'), landing=lambda a: a.get('url'), text=lambda a: a.get('title'))
        elif p == 'tiktok' and prov == 'apify': L = b if isinstance(b, list) else []; F = dict(creative=lambda a: a.get('videos') or a.get('video') or a.get('image_urls') or a.get('images'), start=lambda a: a.get('first_shown_date') or a.get('firstShownDate') or a.get('start_date'), end=lambda a: a.get('last_shown_date') or a.get('lastShownDate') or a.get('end_date'), platforms=lambda a: 'tiktok', status=lambda a: a.get('audit_status') or a.get('status'), spend=lambda a: a.get('spent') or a.get('spend'), reach=lambda a: a.get('impression') or a.get('impressions') or a.get('estimated_audience'), landing=lambda a: a.get('url') or a.get('landing_page'), text=lambda a: a.get('title') or a.get('name'))
        elif p == 'tiktok' and prov == 'tikhub': dd = b.get('data') or {}; L = dd.get('materials') or dd.get('list') or dd.get('ads') or (dd if isinstance(dd, list) else []); F = dict(creative=lambda a: a.get('video_info') or a.get('cover') or a.get('video'), start=lambda a: None, end=lambda a: None, platforms=lambda a: 'tiktok', status=lambda a: None, spend=lambda a: None, reach=lambda a: a.get('like') or a.get('ctr') or a.get('vtr'), landing=lambda a: a.get('landing_page'), text=lambda a: a.get('ad_title') or a.get('brand_name'))
        elif p == 'linkedin' and prov == 'sc': L = b['ads']; F = dict(creative=lambda a: a.get('image') or a.get('video') or a.get('carouselImages'), start=lambda a: a.get('startDate'), end=lambda a: a.get('endDate'), platforms=lambda a: 'linkedin', status=lambda a: None, spend=lambda a: None, reach=lambda a: a.get('totalImpressions') or a.get('impressionsByCountry'), landing=lambda a: a.get('destinationUrl') or a.get('landingPage'), text=lambda a: a.get('headline') or a.get('description'))
        else: return [], {}
    except Exception: return None, {}
    return (L if isinstance(L, list) else []), F
FIELDS = ['creative', 'start', 'end', 'platforms', 'status', 'spend', 'reach', 'landing', 'text']
routes = {}
for r in recs:
    key = (r['_run']['platform'], r['_run']['provider']); routes.setdefault(key, []).append(r)
rows = []; fields_rows = []; detail = {a: {} for a in ADV}
for (p, prov), rs in sorted(routes.items()):
    lat = sorted(r['_run']['latency_ms'] for r in rs)
    oks = []; counts = []; present = {f: 0 for f in FIELDS}; total_ads = 0
    for r in rs:
        L, F = ads(r)
        if L is None or r['_run']['exit'] != 0: detail[r['_run']['adv']][f'{p}/{prov}'] = 'error'; continue
        oks.append(r); counts.append(len(L)); total_ads += len(L); detail[r['_run']['adv']][f'{p}/{prov}'] = len(L)
        for a in L:
            if not isinstance(a, dict): continue
            for f in FIELDS:
                try:
                    if nz(F[f](a)): present[f] += 1
                except Exception: pass
    per_call = LIST[(p, prov)] * (statistics.median(counts) if (p, prov) in PER_RESULT and counts else 1)
    rows.append({'platform': p, 'provider': prov, 'route': f'{p}/{prov}', 'calls': len(rs), 'ok': len(oks), 'errors': len(rs) - len(oks), 'zero_results': sum(1 for c in counts if c == 0),
                 'median_ads': statistics.median(counts) if counts else None, 'total_ads': total_ads, 'p50_ms': lat[len(lat)//2] if lat else None, 'p95_ms': lat[max(0, math.ceil(0.95*len(lat))-1)] if lat else None,
                 'cost_usd_per_call': round(per_call, 4), 'billing': 'per result' if (p, prov) in PER_RESULT else 'per call'})
    fr = {'route': f'{p}/{prov}', 'ads_inspected': total_ads}
    for f in FIELDS: fr[f] = (round(present[f] / total_ads, 2) if total_ads else None)
    fields_rows.append(fr)
adv_rows = [{'advertiser': a, **{k: v for k, v in detail[a].items()}} for a in ADV]
summary = {'date': date, 'cohort': cohort['version'], 'advertisers': len(ADV), 'calls': len(recs), 'ok': sum(r['ok'] for r in rows),
           'est_cost_usd': round(sum((LIST[(r['_run']['platform'], r['_run']['provider'])] * (len(ads(r)[0] or []) if (r['_run']['platform'], r['_run']['provider']) in PER_RESULT else 1)) for r in recs), 3),
           'routes': rows, 'fields': fields_rows, 'advertisers_detail': adv_rows}
os.makedirs('src/data/ads-bench', exist_ok=True)
pub = f'public/evidence/ads-runs/{date}'; os.makedirs(os.path.join(pub, 'tables'), exist_ok=True)
for r in recs: json.dump(r, open(os.path.join(pub, r['_run']['label'] + '.json'), 'w'))
for name in ('routes', 'fields', 'advertisers_detail'):
    rr = summary[name]; keys = list(rr[0].keys()) if rr else []
    for x in rr:
        for k in x:
            if k not in keys: keys.append(k)
    with open(os.path.join(pub, 'tables', f'{name}.csv'), 'w', newline='') as fh:
        w = csv.DictWriter(fh, fieldnames=keys, extrasaction='ignore'); w.writeheader(); w.writerows(rr)
summary['files'] = {'tables': sorted(os.listdir(os.path.join(pub, 'tables'))), 'raw_count': len(recs)}
json.dump(summary, open(f'src/data/ads-bench/{date}.json', 'w'), indent=1)
print(json.dumps({k: summary[k] for k in ('calls', 'ok', 'est_cost_usd')}))
for r in rows: print(r)
for r in fields_rows: print(r)
