#!/usr/bin/env python3
"""Member read DTO red/green evidence; no HTTP replay or database access."""
import hashlib
import json
import re
import subprocess
from pathlib import Path
import integrate_20261010 as tool
from integrate_20261011_batches46_48 import item
from integrate_20261012_batch148 import shape

HERE = Path(__file__).resolve().parent
PRIVATE = tool.ROOT.parent / 'worktrees/dm8-compat-20261012/private-prepay-harness/batch154-member-plan'
BASE = 'work_store/apifox/2026-09-30-dm8-interface-acceptance/parallel-member-read-contract-20261012/'
SOURCE_SHA = 'c0b61e372e372a353e9cdde1987297abeebdab9a5778d03bc728fc9ccfaff87e'


def verify():
    def pin(path, digest):
        assert hashlib.sha256(path.read_bytes()).hexdigest() == digest, path.name
    for name, digest in [
        ('REPORT.md', 'e5ea123446e206ba71e034cdd4e312f066c34a99dff56cf11aed315edd7f9de7'),
        ('RETEST_REPORT.md', 'aaa21864edc2a64ef85b7fe4cdf5392677d7a0e61678d8e63859a35246f16c55'),
        ('verify_offline.php', 'e264aa9a0257d75a91e34c697561fa9cd42e10e540ed5307865e82c12c05f99e'),
        ('verify_retest_offline.php', 'a12e3d23ba4e96684c7d7be3f88ae2328e6240181748eb41ee1fea43f5e27cab'),
    ]:
        pin(PRIVATE / name, digest)
    for name in ['REPORT.md', 'RETEST_REPORT.md']:
        for file, digest in re.findall(r'\| `([\w.-]+)` \| `([0-9a-f]{64})` \|', (PRIVATE / name).read_text()):
            pin(PRIVATE / file, digest)
    pin(tool.source_root('store') / 'application/p/logic/Member.php', SOURCE_SHA)
    pin(tool.source_root('store') / 'tests/Member/MemberResponseContractTest.php',
        '5fdb2246ea2e781f1f03f6a0eb05a8d84652b538a1da45c8b2350c1deeda50ba')
    root = '/workspace/zhct/worktrees/dm8-compat-20261012/private-prepay-harness/batch154-member-plan/'
    for script, status in [('verify_offline.php', 'verified-parity-fail'),
                           ('verify_retest_offline.php', 'verified-parity-pass')]:
        result = json.loads(subprocess.check_output(['docker', 'exec', 'zhctapp_v2_0',
            '/usr/local/php7.3/bin/php', root + script]))
        assert result['status'] == status
    bodies = {}
    for lane in ['B', 'C']:
        pre = json.loads((PRIVATE / (lane + '-preflight.json')).read_text())
        for table, field, precision in [('user', 'BALANCE', 10), ('user', 'PAY_MONEY', 10), ('staff', 'HEIGHT', 5)]:
            meta = pre['metadata'][table]['required'][field]
            assert meta['data_type'].lower() == 'decimal'
            assert int(meta.get('numeric_precision', meta.get('data_precision'))) == precision
            assert int(meta.get('numeric_scale', meta.get('data_scale'))) == 2
        for action in ['info', 'lists']:
            old = (PRIVATE / (lane + '-member-' + action + '.raw')).read_bytes()
            new = (PRIVATE / (lane + '-member-' + action + '-retest.raw')).read_bytes()
            if lane == 'B':
                assert old == new
            else:
                expected = json.loads(old)
                row = expected['data'] if action == 'info' else expected['data']['rows'][0]
                assert row['balance'] == 0.01 and row['pay_money'] == 0.02
                row['balance'], row['pay_money'] = '0.01', '0.02'
                if action == 'info':
                    assert row['height'] == 172
                    row['height'] = '172.00'
                assert row['create_time'].endswith('.000000')
                row['create_time'] = row['create_time'][:-7]
                assert expected == json.loads(new) and shape(expected) == shape(json.loads(new))
            bodies[lane, action] = json.loads(new)
    for action in ['info', 'lists']:
        assert shape(bodies['B', action]) == shape(bodies['C', action])


if __name__ == '__main__':
    audit = HERE / 'integration-20261012-batch154.json'
    assert not audit.exists()
    specs = [item('store', 'POST', '/p/member/' + action, BASE,
        from_repo='unreviewed', from_source='apifox-2026-05-08',
        risk='readonly-new-disabled-synthetic-member-original-row-hashes-no-native-statement-trace',
        readback='tested-pass-two-owned-original-rows-all-columns-unchanged-nonempty-ID-linkage',
        success_case='partial-evidence-one-disabled-member-nonempty-detail-and-list-total1',
        parity='partial-evidence-MySQL-original-wire-bytes-unchanged-DM-only-seven-known-DTO-paths-corrected',
        cleanup='two-setup-rows-per-engine-retained-disabled-zero-role-ledger-QR-no-new-writes-on-retest')
        for action in ['info', 'lists']]
    tool.main(specs, audit, verify)
