#!/usr/bin/env python3
"""Readonly receipt checks before one-time integration; never replay score writes."""
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
PRIVATE = Path('/private/tmp/cdx-ai-hk-course-b.Kmdirn')
BASE = 'work_ai_api/2026-09-30-dm8-interface-acceptance/parallel-terminal-report-score-20261012/'
PINS = {
    'numeric': [
        ('terminal-numeric-followup-CDXTERM4A7475D022.json', '89418182d7f6024fd18263c028f714a80adc8b4a713176651f4b571a897bbffb'),
        ('C_terminal_numeric_followup_CD8782F94BAB.json', '7e2a9738b516fc1df79b9e9bed8f5425469ed5b9ccf693e03ec0ac12f1447f11')],
    'single': [
        ('terminal-score-write-CDXTERM23C0A036FD.json', '431b9f18db8e5ff3704479cdc1af6c6be41ab8ab246c83787d01852aab67b23c'),
        ('C_terminal_score_write_CDDEF5C7FEA0.json', '9d894a8fcad5ab1b5dd72a9d3461ced896f8fd9b34ae107df8427c5e17afc47f')],
    'multi': [
        ('terminal-score-multivalue-CDXTERM104EAAC78A.json', 'b311cf71c8d98d50fbe5f950ce6ba7ca1543a4c952709b6c337c82a89e6f27c6'),
        ('C_terminal_score_multivalue_CDDEE9CC8032.json', 'ea3a50d16c956560030888a353bda99ed75393d0c189deafde649ddcc3491a57')],
}


def verify():
    for name, digest in [
        ('run_terminal_numeric_followup.php', '88187280bebf52c2c2b9caa80f36fc25c8bacff9710a95b853bf7651ba120ddf'),
        ('run_terminal_score_write.php', 'abdd02a4440d51ab7465bd32f1fab195016cd0177a0226b900e405421d0dac10'),
        ('run_terminal_score_multivalue.php', 'c2bd1baef89f4c921e3419048e3098a1c4d7c60dcefb03c15c6d3c9c7aea25df'),
    ]:
        assert hashlib.sha256((PRIVATE / name).read_bytes()).hexdigest() == digest
    total = 0
    for kind, pins in PINS.items():
        lanes = []
        for name, digest in pins:
            raw = (PRIVATE / 'runtime' / name).read_bytes()
            assert hashlib.sha256(raw).hexdigest() == digest
            events = json.loads(raw)['events']
            by = {e['phase']: e for e in events}
            start, setup, cleanup = by['before_setup'], by['after_setup'], by['final_cleanup']
            assert start['global_week_ai_meal_rows_before'] == start['global_week_store_menu_rows_before'] == 0
            assert setup['setup_dml'] == {'ai_meal_insert': 1, 'ai_meal_infos_insert': 1, 'other_business_insert': 0}
            assert setup['global_week_ai_meal_rows_after_setup'] == 1
            assert cleanup['token_absent'] and cleanup['global_week_ai_meal_rows_after'] == cleanup['global_week_store_menu_rows_after'] == 0
            assert cleanup['cleanup_status'] == ('exact_two_rows_removed' if kind == 'numeric' else 'exact_own_rows_removed')
            http = [e for e in events if 'path' in e]
            checked = [e for e in events if e['phase'].startswith('verify_')]
            assert len(http) == len(checked) == (4 if kind == 'numeric' else 2)
            total += len(http)
            for e in checked:
                assert e['http'] == 200 and e['pass'] and e['source_rows_unchanged']
                assert e['external_stub_delta_bytes'] == 0 and e['global_week_ai_meal_rows'] == 1
                for col in ['own_meal_row_sha256', 'own_infos_row_sha256']:
                    assert e[col] == setup[col]
                if kind == 'numeric':
                    assert e['numeric_pass']
                    for value in e['whitelisted_numeric_checks'].values():
                        assert value['actual'] == value['expected']
                        if 'actual_type' in value:
                            assert value['actual_type'] == value['expected_type']
                else:
                    assert e['business_code'] == 200 and e['score_fields_match_own_input']
                    assert e['score_row_count'] == (1 if kind == 'single' else 2)
            if kind == 'single':
                assert by['before_score_insert']['score_rows_before'] == 0
                assert checked[0]['own_score_id'] == checked[1]['own_score_id'] > 0
                assert checked[0]['score_row_sha256'] != checked[1]['score_row_sha256']
            if kind == 'multi':
                assert by['before_score_multi_insert']['score_rows_before'] == 0
                before, after = [e['score_types_to_ids'] for e in checked]
                assert set(before) == {'1', '2'} and set(after) == {'2', '3'}
                assert before['2'] == after['2'] and after['3'] not in before.values()
            lanes.append((http, checked))
        for left, right in zip(lanes[0][0], lanes[1][0]):
            assert left['path'].split('?')[0] == right['path'].split('?')[0]
            assert left['method'] == right['method'] and left['response_shape'] == right['response_shape']
        if kind == 'numeric':
            for left, right in zip(lanes[0][1], lanes[1][1]):
                assert left['whitelisted_numeric_checks'] == right['whitelisted_numeric_checks']
    assert total == 16


if __name__ == '__main__':
    audit = HERE / 'integration-20261012-batch128.json'
    assert not audit.exists()
    targets = [item('ai_api', method, path, BASE,
        from_repo='unreviewed', from_source='apifox-2026-05-08',
        parity='partial-evidence-HTTP-JSON-types-and-whitelisted-source-derived-numerics-not-all-nutrition-values',
        readback='tested-pass-owned-one-meal-infos-source-fingerprints-and-whitelisted-numeric-values',
        cleanup='exact-owned-meal-infos-removed-week-empty-token-absent-zero-external')
        for method, path in [
            ('POST', '/terminal/analysis.Analysis/daily'),
            ('POST', '/terminal/analysis.WeekNew/weekNew'),
            ('GET', '/terminal/recommend.Recommend/infos'),
            ('GET', '/terminal/recommend.Recommend/getStaffDishesInfo'),
        ]]
    targets.append(item('ai_api', 'POST', '/terminal/analysis.Analysis/score', BASE,
        from_repo='unreviewed', from_source='apifox-2026-05-08',
        risk='owned-score-row-write',
        success_case='partial-evidence-group1-insert-update-and-group2-multivalue-replace',
        parity='partial-evidence-HTTP-JSON-shape-equal-owned-input-to-original-score-row-fields',
        readback='tested-pass-score0-to1-to1-same-id-and0-to2-to2-type1-delete-type2-retain-type3-new',
        cleanup='exact-owned-score-meal-infos-removed-token-absent-zero-external'))
    tool.main(targets, audit, verify)
