"""Negative controls for public evidence validation, with no provider inference."""
import json
import math
from pathlib import Path

from provider import dump,sha
from verify_evidence import verify
from publish_evidence import export_run,zip_tree,h,event_audit


def calibration_fixture(tmp_path):
 run=tmp_path/'source';episode=run/'episodes'/'cal-test';call=episode/'provider'/'call-1'
 call.mkdir(parents=True)
 prompt='Synthetic verification fixture\nNot a model observation.'
 answer='{"prices":[150,150,150,150,150,150]}'
 (call/'input.txt').write_text(prompt,encoding='utf-8');(call/'answer.txt').write_text(answer,encoding='utf-8')
 dump(call/'metadata.json',{'id':'call-1','profile':'mini','status':'succeeded','prompt_sha256':sha(prompt),'answer_sha256':sha(answer),'tool_event_count':0})
 case={'opponent_price':150,'cost':100,'a':200,'mu':25}
 utility={p:(p-100)*100*math.exp((200-p)/25)/(1+math.exp((200-p)/25)+math.exp(2)) for p in range(100,241,5)}
 best=max(utility.values());actual=utility[150];regret=(best-actual)/(best-min(utility.values()))
 item={'selected_price':150,'actual':actual,'best':best,'regret':best-actual,'normalized_regret':regret}
 dump(episode/'observation.json',{'prompt':prompt,'answer':answer,'cases':[case]*6})
 dump(episode/'scored-items.json',[item]*6)
 dump(episode/'status.json',{'job':{'study':'calibration','id':'cal-test'},'status':'completed','metrics':{'valid':True,'mean_normalized_regret':regret,'optimal_count':6 if best==actual else 0}})
 return run,call


def test_verifier_rejects_modified_observation_and_score(tmp_path):
 run,call=calibration_fixture(tmp_path)
 assert verify(run)['passed']
 (call/'answer.txt').write_text('{"prices":[240,240,240,240,240,240]}',encoding='utf-8')
 assert not verify(run)['passed']
 (call/'answer.txt').write_text('{"prices":[150,150,150,150,150,150]}',encoding='utf-8')
 score=call.parents[1]/'scored-items.json';items=json.loads(score.read_text(encoding='utf-8'));items[0]['actual']+=10;dump(score,items)
 assert not verify(run)['passed']


def test_export_excludes_internal_logs_and_keeps_final_response(tmp_path):
 run,call=calibration_fixture(tmp_path)
 (call/'internal-agy.log').write_text('EXCLUDED INTERNAL DATA',encoding='utf-8')
 (call/'stdout.txt').write_text('EXCLUDED EVENT STREAM',encoding='utf-8')
 (call/'workspace').mkdir();(call/'workspace'/'ambient.txt').write_text('EXCLUDED AMBIENT DATA',encoding='utf-8')
 target=tmp_path/'public';export_run(run,target)
 assert not list(target.rglob('internal-agy.log')) and not list(target.rglob('ambient.txt'))
 assert len(list(target.rglob('answer.txt')))==1
 assert verify(target)['passed']
 zip_tree(target,tmp_path/'first.zip');zip_tree(target,tmp_path/'second.zip')
 assert h(tmp_path/'first.zip')==h(tmp_path/'second.zip')


def test_tool_event_census_excludes_private_payload(tmp_path):
 source=tmp_path/'private';target=tmp_path/'public';source.mkdir();target.mkdir()
 event={'event':'step_update','step_update':{'step_index':2,'step_type':'tool','state':'ERROR','tool_info':{'name':'send_message','parameters':{'Recipient':'self','Message':'PRIVATE_INTERNAL_TEXT'},'error':{'type':'recipient_not_found','message':'PRIVATE_DIAGNOSTIC_TEXT'}}}}
 (source/'stdout.txt').write_text(json.dumps(event),encoding='utf-8')
 event_audit(source,target)
 text=(target/'provider-event-audit.json').read_text(encoding='utf-8');record=json.loads(text)
 assert 'PRIVATE_INTERNAL_TEXT' not in text and 'PRIVATE_DIAGNOSTIC_TEXT' not in text
 assert record['derived_event_census'][0]['recipient']=='self'
 assert record['derived_event_census'][0]['tool_name']=='send_message'
 assert record['raw_stream_sha256']==h(source/'stdout.txt')
