import sys, json, hashlib, wave
from pathlib import Path
manifest=json.loads(Path(sys.argv[1]).read_text()); root=Path(sys.argv[2]).resolve()
errors=[]; results=[]
program_hashes={e['byteSha256'] for e in manifest['files'] if e['role'] in ['program','program_fixture']}
for entry in manifest['files']:
    p=(root/entry['path']).resolve(); row={'file_id':entry['file_id'],'path':entry['path'],'role':entry['role'],'changedBlocks':[]}
    if not p.is_relative_to(root): raise ValueError('path outside recipient root')
    if not p.is_file():
        errors.append({'file_id':entry['file_id'],'code':'MISSING_FILE','path':entry['path']}); results.append(row); continue
    actual_sha=hashlib.sha256(p.read_bytes()).hexdigest(); row['actualByteSha256']=actual_sha
    if actual_sha!=entry['byteSha256']:
        errors.append({'file_id':entry['file_id'],'code':'BYTE_SHA_MISMATCH'})
        if entry['role'] not in ['program','program_fixture'] and actual_sha in program_hashes:
            errors.append({'file_id':entry['file_id'],'code':'PROGRAM_ALIAS_IN_SOURCE_SLOT'})
    with wave.open(str(p),'rb') as w:
        actual={'sampleRate':w.getframerate(),'sampleWidthBytes':w.getsampwidth(),'channels':w.getnchannels(),'frames':w.getnframes()}
        row['actualWav']=actual; row['actualDurationSeconds']=actual['frames']/actual['sampleRate']
        if any(actual[k]!=entry[k] for k in actual):
            errors.append({'file_id':entry['file_id'],'code':'WAV_HEADER_MISMATCH'})
        else:
            for block in entry['blocks']:
                w.setpos(block['startFrame']); pcm=w.readframes(block['endFrame']-block['startFrame'])
                if hashlib.sha256(pcm).hexdigest()!=block['pcmSha256']:
                    row['changedBlocks'].append({k:block[k] for k in ['startFrame','endFrame','startSeconds','endSeconds']})
            if row['changedBlocks']: errors.append({'file_id':entry['file_id'],'code':'PCM_BLOCK_MISMATCH','blocks':row['changedBlocks']})
    results.append(row)
print(json.dumps({'identity':'actual_local_author_handoff_check_not_official_Adobe_utility','passed':not errors,'files':results,'errors':errors,'limits':'Compares trusted sender baseline only; does not prove speaker identity, Adobe origin, semantic completeness or server recovery.'},ensure_ascii=False,indent=2))
raise SystemExit(1 if errors else 0)
