#!/usr/bin/env python3
"""No coercion and no raw values in durable terminal evidence."""
import argparse
import importlib.util
import json
from pathlib import Path
import re

args = argparse.ArgumentParser()
args.add_argument('b_report')
args.add_argument('b_responses')
args.add_argument('c_report')
args.add_argument('c_responses')
options = args.parse_args()
workspace = Path(__file__).resolve().parents[3]
source = workspace / 'work/2026-09-28-smart-canteen-dm8-poc/http-harness/strict_response_compare.py'
spec = importlib.util.spec_from_file_location('strict', source)
strict = importlib.util.module_from_spec(spec)
spec.loader.exec_module(strict)
b, c = strict.load_json(options.b_responses), strict.load_json(options.c_responses)
br, cr = strict.load_json(options.b_report), strict.load_json(options.c_report)
expected = {'ENUM_1','ENUM_2','ENUM_3','ENUM_17','FOODS_ALL','FOODS_LIST','FOODS_CATE','FOODS_DETAIL',
            'DISHES_ALL','DISHES_LIST','DISHES_CATE','DISHES_DETAIL','DISHES_MORE','NATION','SPORT'}
if set(b) != expected or set(c) != expected:
    raise SystemExit('Required 15 positive scenarios absent')
if br['terminal_source_sha256'] != cr['terminal_source_sha256']:
    raise SystemExit('Terminal business source drift')
rule = {'$["trace_id"]': {'type':'string','pattern':'[0-9a-f]{13}',
    'reason':'Validated unique framework request trace only','require_distinct':True}}
cases = []
def group_differences(differences):
    groups = {}
    for path, left, right, expected, actual in differences:
        key = (re.sub(r'\[\d+\]', '[*]', path), left, right)
        group = groups.setdefault(key, {'path_pattern':key[0],'mysql_type':left,'dm_type':right,'occurrences':0})
        group['occurrences'] += 1
    return list(groups.values())

for name in sorted(expected):
    for body in [b[name], c[name]]:
        if body['http_status'] != 200 or body['body'].get('status') != 200:
            raise SystemExit('Positive HTTP/business response absent: '+name)
        if not re.fullmatch('[0-9a-f]{13}', body['body'].get('trace_id','')):
            raise SystemExit('Trace absent or malformed: '+name)
    diffs = strict.compare(b[name]['body'], c[name]['body'], rules=rule)
    left = next(x for x in br['cases'] if x['name'] == name)
    right = next(x for x in cr['cases'] if x['name'] == name)
    cases.append({'name':name,'method':left['method'],'path':left['path'],
        'strict_equal':not diffs,'differences':len(diffs),
        'type_differences':sum(x[1]!=x[2] for x in diffs),
        'difference_paths_and_types':group_differences(diffs),
        'mysql_rows':left['positive_rows'],'dm_rows':right['positive_rows'],
        'mysql_source_projection_equal':left['source_projection_equal'],
        'dm_source_projection_equal':right['source_projection_equal'],
        'mysql_source_fields_compared':left['source_fields_compared'],
        'dm_source_fields_compared':right['source_fields_compared']})
def runtime_summary(report):
    events = [e for e in report['events'] if e.get('kind') == 'query_observed']
    classification = {key:sum(e.get(key,0) for e in report['events'] if e.get('kind')=='listener_classification')
        for key in ['connection_events','metadata_events','unclassified_events']}
    return {'queries':len(events),'fingerprints':len({e['fingerprint'] for e in events}),
        'listener_classification':classification,
        'business_select_observations':len(events)-classification['metadata_events'],
        'operations':{op:sum(e['operation']==op for e in events) for op in sorted({e['operation'] for e in events})},
        'callsites':sorted({site for e in events for site in e['callsites']})}

summary = {'date':'2026-10-10','scenarios':cases,'unique_get_routes':12,'scenario_pairs':len(cases),
    'strict_equal_scenarios':sum(x['strict_equal'] for x in cases),
    'mysql_source_unchanged':br['source_before_after_unchanged'],
    'dm_source_unchanged':cr['source_before_after_unchanged'],
    'mysql_non_select_observed':br['non_select_observed'],'dm_non_select_observed':cr['non_select_observed'],
    'tokens_cleaned':br['token_cleaned'] and cr['token_cleaned'],
    'terminal_source_sha256':br['terminal_source_sha256'],
    'snapshots':{'B':br['snapshot'],'C':cr['snapshot']},
    'runtime':{'B':runtime_summary(br),'C':runtime_summary(cr)},
    'scope':'Partial read evidence. Value differences from historical snapshots are not automatically compatibility defects.'}
print(json.dumps(summary,indent=2,ensure_ascii=False,default=str))
