#!/usr/bin/env python3
"""Actual steps write/read/replace, with WeChat boundary stubbed only."""
import hashlib
import json
from pathlib import Path
import integrate_20261010 as tool
from integrate_20261011_batches46_48 import item
from integrate_20261012_batch119 import shape
HERE=Path(__file__).resolve().parent
P=Path('/Users/wiscourper/Documents/workspace/local/dm8-batch116-20261012.S1TtvG')
BASE='work_ai_api/2026-10-12-dm8-hk-course-batch/'
REPORT=BASE+'DEVICE_STEPS_ROOT_REVIEW.md'

def verify():
    m=json.loads((tool.ROOT/BASE/'DEVICE_STEPS_MANIFEST.json').read_text())
    bodies=[]; shapes=[]
    for lane in ['B','C']:
        spec=m['lane_'+lane]
        files={key:Path(spec[key]) if lane=='B' else P/Path(spec[key]).name for key in ['receipt','raw']}
        for key,p in files.items():assert hashlib.sha256(p.read_bytes()).hexdigest()==spec[key+'_sha256']
        before,missing,normal,reads,duplicate,final=json.loads(files['receipt'].read_text())['events']
        assert before['motion_rows']==missing['motion_rows']==[] and before['private_token_present']
        assert missing['http']==200 and missing['business_status']==500 and missing['pass']
        assert normal['http']==normal['business_status']==200 and normal['pass']
        assert [(r['date'],r['steps']) for r in normal['motion_rows']]==[(4073731200,1000),(4073817600,2000)]
        assert reads['day_http']==reads['week_http']==reads['day_business_status']==reads['week_business_status']==200
        assert reads['pass'] and reads['day_steps']==1000 and reads['week_total_steps']==3000 and reads['week_detail_count']==2
        assert duplicate['http']==duplicate['business_status']==200
        assert sorted((r['date'],r['steps']) for r in duplicate['motion_rows'])==[(4073731200,500),(4073731200,700),(4073817600,2000)]
        ids={r['id'] for r in duplicate['motion_rows']}
        assert normal['motion_rows'][0]['id'] not in ids and normal['motion_rows'][1]['id'] in ids
        assert final['token_absent'] and final['own_2099_rows_after']==[] and final['cleanup_status']=='all_own_rows_removed'
        assert {r['id'] for r in final['removed_exact_ids']}==ids and all(r['affected']==1 for r in final['removed_exact_ids'])
        wires=[json.loads(line) for line in files['raw'].read_text().splitlines() if line]
        assert len(wires)==5
        hashes=[missing['raw_response_sha256'],normal['raw_response_sha256'],reads['day_raw_sha256'],reads['week_raw_sha256'],duplicate['raw_response_sha256']]
        results=[]
        for n,(w,pin) in enumerate(zip(wires,hashes)):
            assert w['http']==200 and w['method']=='POST'
            assert hashlib.sha256(w['response_raw'].encode()).hexdigest()==pin
            body=json.loads(w['response_raw']);assert body['status']==(500 if n==0 else 200)
            body.pop('trace_id',None);results.append(body)
        bodies.append(results);shapes.append(shape(results))
    assert bodies[0]==bodies[1] and shapes[0]==shapes[1]

if __name__=='__main__':
    audit=HERE/'integration-20261012-batch121.json';assert not audit.exists()
    targets=[item('ai_api','POST','/api/UserMotion/movementMpWxSteps',BASE,evidence=REPORT,
        success_case='partial-evidence-real-controller-transaction-two-future-days-synthetic-WeChat-session-decrypt-only',
        readback='tested-pass-owned-motion-rows-day-week-readback-replacement-and-final-zero',
        missing_required='tested-pass-missing-code-rejected-no-motion-rows',
        duplicate_retry='partial-evidence-same-day-two-new-rows-replace-old-one-other-day-preserved-duplicate-after-aggregate-untested',
        boundary_empty_min_max='partial-evidence-2099-unsigned-timestamp-not-arbitrary-date-range',
        parity='partial-evidence-five-complete-HTTP-responses-values-and-recursive-types-equal-except-trace-id',
        cleanup='exact-owned-IDs-deleted-own-date-range-zero-and-private-token-absent')]
    for path in ['/api/UserMotion/userDaySteps','/api/UserMotion/userWeekSteps']:
        targets.append(item('ai_api','POST',path,BASE,evidence=REPORT,evidence_only=True))
    tool.main(targets,audit,verify)
