#!/usr/bin/env python3
"""Nine empty/default API reads and literal no-DB response, never nonempty pass."""
import hashlib
import json
from pathlib import Path
import stat
import subprocess
import sys
import integrate_20261010 as tool
from integrate_20261011_batches46_48 import item

BASE=tool.AI+'parallel-ai-api-customer-read12-20261011/'
SEALS=[('', '0b3b521b2ce8cee62304ab85d402f2f6fa41c4fd4a82310fa14dc0fb243dd089',191,
        '/tmp/cdx-ai-api-customer-read12-20261011',17712),
       ('failed-weight-retest/','d05e1a1e7f7129f1cf0e34c0d69082b7bbfd4ea1d9fc43f4ac085fbcdb0bffdc',70,
        '/tmp/cdx-ai-customer-weight-retest-20261011',12737),
       ('final-weight-read/','d3882bdf5b9c40b531f39044ea6d4f3a6e5e639fb55dcf57d9fa4a0aa87b8844',89,
        '/tmp/cdx-ai-final-weight-read-20261011',13141)]

def seal(relative,pin,count,private_root):
    root=tool.ROOT/relative
    manifest=root/'manifest.json'
    assert hashlib.sha256(manifest.read_bytes()).hexdigest()==pin
    m=json.loads(manifest.read_text());groups=[(root,m['public_files'])]
    for lane,group in m['canonical_private_groups'].items():
        expected=Path(private_root)/f'private-{lane}-final'
        assert lane in ['B','C'] and Path(group['root'])==expected
        assert not expected.is_symlink() and stat.S_IMODE(expected.stat().st_mode)==0o700
        groups.append((expected,group['files']))
    total=0
    for parent,records in groups:
        for r in records:
            rel=Path(r['path'])
            assert not rel.is_absolute() and '..' not in rel.parts
            f=parent/rel
            assert not f.is_symlink() and f.is_file()
            b=f.read_bytes()
            assert len(b)==r['bytes'] and hashlib.sha256(b).hexdigest()==r['sha256']
            assert oct(stat.S_IMODE(f.stat().st_mode))==r['mode']
            total+=1
    assert total==count

def verify():
    for sub,pin,count,private_root,checks in SEALS:
        root=tool.ROOT/BASE/sub
        seal(BASE+sub,pin,count,private_root)
        result=subprocess.run([sys.executable,'-B',str(root/'verify.py')],capture_output=True,text=True)
        assert result.returncode==0,result.stderr
        report=json.loads(result.stdout)
        assert report['checks']==checks
        for lane in ['B','C']:
            assert report['lanes'][lane]['DML']==report['lanes'][lane]['DDL']==0
    main=tool.load(BASE+'verification.json')
    for lane in ['B','C']:
        out=main['lanes'][lane]['outcomes']
        assert sum(v.get('business_DB_pass') is True for v in out.values())==8
        assert out['INDEX']['native_SQL']==0 and out['INDEX']['result']=='literal404-no-businessDB'
    # Eight source-default fields, despite the original report's prose typo "9".
    for lane in ['B','C']:
        p=Path(SEALS[0][3])/f'private-{lane}-final'/'expected.json'
        value=json.loads(p.read_text())['SIGN']['data']['signInfo']
        assert list(value)==['id','user_id','action_id','status','award_time','receive_time','address_id','address']
    pins=tool.load(BASE+'source-frozen-sha.json')['files']
    assert len(pins)==6168
    for rel,digest in pins.items():
        assert hashlib.sha256((tool.ROOT.parent/'zhctproject/ai_api'/rel).read_bytes()).hexdigest()==digest

def targets():
    paths=['/api/points.Log/list','/api/Refund/list','/api/Sign/list',
           '/api/user.UserWeight/byDate','/api/WellandScale/latest','/api/WellandScale/dates',
           '/api/YunkangbaoScale/latest','/api/YunkangbaoScale/dates','/api/YunkangbaoScale/byDate']
    result=[]
    for path in paths:
        base=BASE+'final-weight-read/' if path.endswith('/byDate') and 'UserWeight' in path else BASE
        result.append(item('ai_api','GET',path,base,
            risk='readonly-original-actor-empty-or-source-default-DTO-no-business-write',
            mysql_state='partial-evidence-empty-or-source-default-contract-only',
            dm_state='partial-evidence-empty-or-source-default-contract-only',
            success_case='partial-evidence-empty-only-not-nonempty-business-data',
            parity='partial-exact-complete-empty-default-DTO-types-order-values-no-nonempty-claim',
            boundary_empty_min_max='partial-empty-source-or-current-day-fallback-or-eight-sign-default-fields',
            cleanup='original-actors-all-rows-and-financial-ledgers-conserved-own-tokens-cache-workers-ports-cleaned'))
    result.append(item('ai_api','GET','/api/Index/index',BASE,scope='no-db',
        risk='literal-intentional-default-404-with-zero-business-native-SQL',
        success_case='partial-evidence-literal-source-404-no-business-database',
        parity='partial-literal-404-complete-source-response-not-database-compatibility-pass',
        readback='not-applicable-zero-business-native-SQL',
        mysql_readback='not-applicable-no-business-database',dm_readback='not-applicable-no-business-database'))
    return result

if __name__=='__main__':
    audit=Path(__file__).with_name('integration-20261011-batch82.json')
    assert not audit.exists(),'Preserve original transition audit'
    tool.main(targets(),audit,verify)
