"""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


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')
