"""Reproduce ORIGINAL article-study arithmetic only. Standard libraries + Node; no SDK/network.
The separate incomplete new cohort is checked for coverage only, never pooled or effect-analyzed.
"""
from pathlib import Path
from decimal import Decimal
import argparse,csv,hashlib,importlib.util,json,math,shutil,statistics,subprocess,sys
HERE=Path(__file__).resolve().parent;BASE=HERE.parent
def rows(name):
 with (BASE/'data'/name).open(encoding='utf-8-sig',newline='') as f:return list(csv.DictReader(f))
def close(a,b):return math.isclose(float(a),float(b),abs_tol=1e-8,rel_tol=1e-12)
def dump_csv(p,data):
 with p.open('w',encoding='utf-8',newline='') as f:w=csv.DictWriter(f,fieldnames=data[0]);w.writeheader();w.writerows(data)
def main():
 parser=argparse.ArgumentParser();parser.add_argument('--check',action='store_true',help='Validate every payoff, published summary and public-message hash');parser.parse_args()
 node=shutil.which('node')
 if not node:raise SystemExit('Node18+ must be installed and on PATH; no automatic installation/network access is attempted.')
 c3=rows('c3-outcomes.csv');assert len(c3)==12 and len({x['run_id'] for x in c3})==12
 p=subprocess.run([node,str(HERE/'derive_c3.mjs')],input=json.dumps(c3),capture_output=True,text=True,encoding='utf-8',timeout=30)
 if p.returncode:raise SystemExit(p.stderr)
 scored=json.loads(p.stdout);cs=[]
 for pairing in ['HH','HO','OO']:
  g=[x for x in scored if x['pairing']==pairing];assert len(g)==4
  cs.append({'pairing':pairing,'assigned':4,'known_agreements':4,'technical_unknowns':0,'mean_net_value_creation_points':statistics.mean(x['joint_gain'] for x in g),'sample_sd_points':statistics.stdev(x['joint_gain'] for x in g),'mean_buyer_gain_points':statistics.mean(x['buyer_gain'] for x in g),'mean_seller_gain_points':statistics.mean(x['seller_gain'] for x in g),'mean_efficiency_fraction':statistics.mean(x['efficiency'] for x in g)})
 corepath=HERE/'environment-n320/source/env_core.py';s=importlib.util.spec_from_file_location('original_env_core',corepath);E=importlib.util.module_from_spec(s);s.loader.exec_module(E)
 er=rows('environment-outcomes.csv');assert len(er)==320 and len({x['run_id'] for x in er})==320
 for r in er:
  assert r['known']=='1' and r['status']=='DEAL'
  veh,sch=E.PLAN_BY_ID[r['plan']];price=int(Decimal(r['price_usd_season'])*100);g=E.outcome_of_deal(veh,sch,price)
  for key,col in [('buyer_cents','buyer_money_usd'),('seller_cents','seller_money_usd'),('joint_cents','joint_money_usd')]:assert Decimal(r[col])*100==g[key],r['run_id']+':'+col
  assert int(r['emissions_kg_co2e_season'])==g['emissions_kg']
  assert int(r['lowest_emissions_package'])==int(g['emissions_kg']==min(E.EMISSIONS_KG.values()))
  assert Decimal(r['buyer_gain_vs_fallback_usd'])*100==g['buyer_cents']-E.FALLBACK['buyer']
  assert Decimal(r['seller_gain_vs_fallback_usd'])*100==g['seller_cents']-E.FALLBACK['seller']
 es=[]
 for arm in ['disclosed','undisclosed']:
  g=[x for x in er if x['arm']==arm];assert len(g)==160
  em=[float(x['emissions_kg_co2e_season']) for x in g];money=[float(x['joint_money_usd']) for x in g]
  es.append({'arm':arm,'assigned':160,'known_agreements':160,'technical_unknowns':0,'lowest_emissions_count':sum(int(x['lowest_emissions_package']) for x in g),'mean_emissions_kg':statistics.mean(em),'sample_sd_emissions_kg':statistics.stdev(em),'mean_joint_money_usd':statistics.mean(money),'sample_sd_joint_money_usd':statistics.stdev(money),'buyer_below_fallback_count':sum(int(x['buyer_below_fallback']) for x in g),'seller_below_fallback_count':sum(int(x['seller_below_fallback']) for x in g)})
 for actual,fn,key in [(cs,'c3-summary.csv','pairing'),(es,'environment-summary.csv','arm')]:
  old={x[key]:x for x in rows(fn)}
  for r in actual:
   for k,v in r.items():assert (old[r[key]][k]==v if k==key else close(old[r[key]][k],v)),fn+':'+k
 hashes=0;corpus_counts={};timestamps={};model_ids=set();terminal_correction_counts={}
 for name in ['c3','environment']:
  corpus=[json.loads(l) for l in (BASE/f'data/{name}-public-transcripts.jsonl').read_text(encoding='utf-8').splitlines()]
  corpus_counts[name]=len(corpus);times=[]
  for run in corpus:
   for turn in run['public_turns']:
    assert hashlib.sha256(turn['message'].encode()).hexdigest()==turn['message_sha256'];hashes+=1
    model_ids.update([turn['requested_model'],turn['returned_model']]);times.append(turn['at_utc'])
  timestamps[name]={'first_public_turn_utc':min(times),'last_public_turn_utc':max(times)}
 for arm in ['disclosed','undisclosed']:terminal_correction_counts[arm]=sum(int(x['service_terminal_used_for_known_deal']) for x in er if x['arm']==arm)
 status=json.loads((HERE/'new-c3-incomplete/collection-status.json').read_text());assert (status['known_agreements'],status['incomplete'],status['unstarted'])==(20,2,18)
 out=HERE/'derived';out.mkdir(exist_ok=True);dump_csv(out/'c3-summary-recomputed.csv',cs);dump_csv(out/'environment-summary-recomputed.csv',es)
 result={'result':'PASS','original_c3_rows_checked':12,'original_environment_rows_checked':320,'public_messages_hashed':hashes,'corpora':corpus_counts,'model_ids':sorted(model_ids),'observed_public_turn_windows':timestamps,'environment_service_terminal_reconstruction_count':terminal_correction_counts,'original_summary_tables_match':True,'new_cohort_status_only_checked':status,'new_cohort_payoff_analysis_performed':False,'network_or_inference_calls':0}
 (out/'REPRODUCTION_CHECK.json').write_text(json.dumps(result,indent=2),encoding='utf-8');print(json.dumps(result,indent=2))
if __name__=='__main__':main()
