#!/usr/bin/env python3
"""Read-only verification of personal data and Store consumer, then one-time ledger update."""
import hashlib
import json
from pathlib import Path
import integrate_20261010 as tool
from integrate_20261011_batches46_48 import item

HERE = Path(__file__).resolve().parent
B = Path('/private/tmp/cdx-ai-hk-course-b.Kmdirn/runtime')
P = Path('/Users/wiscourper/Documents/workspace/local/dm8-batch116-20261012.S1TtvG')
BASE = 'work_ai_api/2026-10-12-dm8-hk-course-batch/'
REPORT = BASE + 'ROOT_VERIFIED_PERSONAL_SYNC_20261012.md'
FILES = [
    (B, 'personal-full-staff-CDXHK7BD1E2CA66A7.json', '1695f0a98da4ac7afe8140f6f6dc01d00a8647ffd536938b7b9c7e58eb0cce8b'),
    (P, 'personal-full-staff-CDXHKFC5ADAC26D95.json', '6dc42a3c3dee013793f57ce346f7daa81f89175360a8e0d11bd14f906a328467'),
    (B, 'personal-full-staff-raw-CDXHK7BD1E2CA66A7.jsonl', 'fda9938788b4821f8fc63a88a5dd1c8e1151d7f19d963a180b88b8951bd8c877'),
    (P, 'personal-full-staff-raw-CDXHKFC5ADAC26D95.jsonl', '7e095a80ba33727ac11ff6537e3baee8cdb671f0fab00f8c09e8459743e25bc5'),
    (B, 'personal-info-deptfix-CDXHK7BD1E2CA66A7.json', 'e95a3afa3c3b63585af6e737d15d88e6b17eccf0bde0340d73a0b97c9c00ec18'),
    (P, 'personal-info-deptfix-CDXHKFC5ADAC26D95.json', '334b41d1cfbbaa4e8829c5ae84d51c9709a07e9de26757d6c9054ddbe132b545'),
    (B, 'personal-fourbyte-CDX4B08F287A6ADF4.json', '1818f60a4a426f7640d6bc4cb3f4f410c82c3ccb8e89dcb49725289a71c0f5be'),
    (P, 'personal-fourbyte-CDX4BB18B04AD7BAF.json', '5f8d92430bf510f98323ee01306fc9575000b01ab9f1eadfb47effb5aad3eb66'),
    (B, 'personal-fourbyte-raw-CDX4B08F287A6ADF4.jsonl', '9465904ac34d114d0d2a57666bf3fbc3eb5c18c799c5bbf09e44a9f174eb47fe'),
    (P, 'personal-fourbyte-raw-CDX4BB18B04AD7BAF.jsonl', 'cb87cdeae4541432d5da25f45b74c1336b9b955b775cc367871b4b981f8da4ca'),
    (P, 'store-sync-consumer-CDXSYNCCFFF63B206.json', '1b674ad1c5b87569ac4936863ae6341af5c90e951794cc18f6e8ed26d3a6826b'),
    (P, 'store-sync-consumer-CDXSYNC841370FDCE.json', '5fbbe3956eed437aca37c78d09396d0a461a6ea532e49df19a0c6c7e9ba74fd9'),
    (P, 'store-sync-consumer-raw-CDXSYNCCFFF63B206.jsonl', 'e06007d55ff6b717e9eba180a491cfc838458dc481d55a9b51bfd4f4853df636'),
    (P, 'store-sync-consumer-raw-CDXSYNC841370FDCE.jsonl', '49d2c31d14849af7f294a6988cff8f3f3f485aee11d32f80b846491b475c7417'),
]

def sha(raw):
    return hashlib.sha256(raw).hexdigest()

def verify():
    data = []
    for root, name, pin in FILES:
        raw = (root / name).read_bytes()
        assert sha(raw) == pin, name
        data.append(json.loads(raw))
    for repo, path, pin in [
        ('ai_api', 'app/common/model/zhct/Department.php', '68d758050b2ef624be9f2a02b8991a3c79cfb20707e8935171563c93032e37f1'),
        ('store', 'application/api/service/Department.php', '89592e7212c301f46f913a66bc612681bc303a7d7ff4b334077c5c630a99aeb8'),
    ]:
        assert sha((tool.source_root(repo) / path).read_bytes()) == pin
    types, staff_bodies = [], []
    for receipt, wire in zip(data[:2], data[2:4]):
        before, after, checks, final = receipt['events']
        assert before['phase'] == 'before_http' and after['phase'] == 'after_http'
        assert before['at_epoch'] <= after['at_epoch'] <= final['at_epoch']
        assert before['private_auth_ready'] and after['http'] == after['business_status'] == 200
        for table, count in [('user', 1), ('staff', 1), ('weight', 1), ('score_log', 1), ('score', 1), ('health_metric', 2), ('bind', 1)]:
            old, new = before['original_rows']['ai'][table], after['original_rows']['ai'][table]
            assert new['count'] == count
            if table in ('user', 'staff'):
                assert old['count'] == 1 and old['rows'][0]['row_sha256'] != new['rows'][0]['row_sha256']
            else:
                assert old['count'] == 0
        assert before['original_rows']['store'] == after['original_rows']['store']
        assert checks['basic_fields_match_input'] and checks['weight_score_metric_bind_sidecars_present']
        assert checks['store_rows_unchanged_due_to_explicit_UserApi_stub'] and final['token_absent']
        assert 'user_sync' in checks['external_stub_events']
        assert wire['http'] == 200 and wire['path'] == '/api/User/personal'
        assert sha(wire['response_raw'].encode()) == after['raw_response_sha256']
        body = json.loads(wire['response_raw'])
        assert type(body['status']) is int and body['status'] == 200 and body['data'] == []
        body.pop('trace_id', None)
        staff_bodies.append(body)
        types.append(checks['selected_field_json_types'])
    # These misleadingly named receipt fields are PDO readback types, not HTTP fields.
    assert types[0]['height'] == types[0]['weight'] == 'string'
    assert types[1]['height'] == types[1]['weight'] == 'double'
    assert staff_bodies[0] == staff_bodies[1]
    for receipt in data[4:6]:
        before, after, final = receipt['events']
        assert after['http'] == after['business_status'] == 200 and after['pass']
        assert after['original_rows_unchanged'] and after['response_weight_matches_synthetic_input']
        for key in ['user_row_sha256', 'staff_row_sha256']:
            assert before[key] == after[key]
        assert after['response_weight_type'] == 'string' and after['response_height_type'] == 'integer'
        assert final['token_absent']
    bodies = []
    for receipt, wire in zip(data[6:8], data[8:10]):
        before, after, final = receipt['events']
        assert before['staff_rows'] == after['staff_rows'] == 0 and before['auth_token_present']
        assert after['http'] == after['business_status'] == 200 and after['exact_utf8_bytes_match']
        assert before['expected_utf8_hex'] == after['db_after_utf8_hex'] != before['db_before_utf8_hex']
        assert before['expected_sha256'] == after['db_after_sha256'] == sha(bytes.fromhex(after['db_after_utf8_hex']))
        assert sha(wire['response_raw'].encode()) == after['raw_response_sha256'] and final['token_absent']
        body = json.loads(wire['response_raw']); body.pop('trace_id', None); bodies.append(body)
    assert bodies[0] == bodies[1]
    shapes = []
    for receipt, wire in zip(data[10:12], data[12:14]):
        before, after = receipt['events']
        assert before['external_stubs'] and before['redis_isolated']
        assert before['synthetic_department_rows_before'] == 0 and after['synthetic_department_rows_after'] == 1
        assert after['http'] == 200 and after['business_code'] == 0 and after['pass']
        for key in ['department_charge_by_empty', 'department_leader_empty', 'shared_department_10000_unchanged',
                    'own_staff_and_user_name_match_synthetic', 'own_staff_weight_matches_synthetic',
                    'own_staff_mobile_matches_input', 'own_user_mobile_matches_input']:
            assert after[key], key
        assert before['shared_department_10000_sha256'] == after['shared_department_10000_sha256']
        assert before['own_staff_sha256'] != after['own_staff_sha256']
        assert before['own_user_sha256'] != after['own_user_sha256']
        assert sha(wire['response_raw'].encode()) == after['raw_response_sha256']
        assert wire['path'] == '/api/user/sync' and json.loads(wire['response_raw'])['code'] == 0
        shapes.append((after['response_field_types'], after['response_user_id_type']))
    assert shapes[0] == shapes[1]

def targets():
    return [
        item('ai_api', 'POST', '/api/User/personal', BASE, evidence=REPORT,
             scope='AI-seven-owned-tables-full-staff-and-no-staff-fourbyte-not-unmodified-Store-producer-end-to-end',
             success_case='partial-evidence-full-staff-seven-table-write-and-no-staff-fourbyte-local-utf8mb4',
             readback='tested-pass-own-seven-table-counts-hashes-input-values-and-exact-fourbyte-bytes',
             parity='partial-evidence-full-staff-and-fourbyte-HTTP-response-equal-except-trace-PDO-types-differ-not-response-fields',
             boundary_empty_min_max='partial-evidence-fourbyte-nickname-fixed-local-utf8mb4-old-utf8-failure-preserved'),
        item('ai_api', 'GET', '/api/User/info', BASE, evidence=REPORT, evidence_only=True),
        item('store', 'POST', '/api/user/sync', BASE, evidence=REPORT,
             from_repo='unreviewed', from_source='apifox-2026-05-08',
             scope='actual-Store-consumer-own-user-staff-department-not-producer-fixed-shared-department',
             readback='tested-pass-three-owned-tables-input-values-shared-department-10000-unchanged',
             parity='partial-evidence-response-types-equal-own-IDs-differ-source-default-empty-string-verified'),
    ]

if __name__ == '__main__':
    audit = HERE / 'integration-20261012-batch116.json'
    assert not audit.exists()
    tool.main(targets(), audit, verify)
