#!/usr/bin/env python3
"""Independent offline fixed-sequence/raw conservation checker and mutations."""
import copy
import hashlib
import importlib.util
import json
import stat
import struct
from pathlib import Path

R=Path(__file__).resolve().parent
BASE=R.parent
P=json.loads((BASE/'c-literal-rint-proposal-20261011/proposal.json').read_bytes())
spec=importlib.util.spec_from_file_location('analysis',R/'analyze-evidence.py')
A=importlib.util.module_from_spec(spec);spec.loader.exec_module(A)


def verify(es,analysis):
    n=0
    def ok(test):
        nonlocal n
        assert test
        n+=1
    ok([e['seq'] for e in es]==list(range(1,len(es)+1)))
    ok(len(es)==208 and all(e['lane']=='C' for e in es))
    starts=[e['data'] for e in es if e['event'] in ['read-start','prepare-only-start']]
    observed=[e['data'] for e in es if e['event']=='read-observed-before-gate']
    successes=[e['data'] for e in es if e['event']=='read-success']
    ok(len(starts)==50 and len(observed)==len(successes)==49)
    ok([s['attempt'] for s in starts]==list(range(1,51)))
    ok(starts[0]['role']=='identity_schema' and starts[1]['role']=='identity_version' and starts[2]['role']=='literal_rint' and starts[2]['parameters']==[])
    ok([s['case_id'] for s in starts[3:]]==[c['case_id'] for c in P['cases']])
    for i,s in enumerate(starts):
        ok(s['sql']==P['templates'][s['role']])
        ok(s['sql_sha256']==hashlib.sha256(s['sql'].encode()).hexdigest())
        expected=[] if i<3 else P['cases'][i-3]['parameters']
        ok(len(s['parameters'])==len(expected))
        for actual,b in zip(s['parameters'],expected):
            ok(actual['original']==b)
            ok(actual['runtime']=={'php_type':b['php_type'],'value':b['runtime_value']})
            ok(actual['pdo_type_code']==b['pdo_type_code'])
    observed_attempts=[1,2]+list(range(4,51))
    ok([x['attempt'] for x in observed]==observed_attempts==[x['attempt'] for x in successes])
    for d in observed:
        ok(d['execute_errorInfo'][0]=='00000' and d['execute_errorInfo'][1]==0)
        ok(d['fetch_errorInfo'][0]=='00000' and d['fetch_errorInfo'][1]==0)
        ok(d['serialized_bytes_before_bytecount_field']<=65536)
        ok(d['column_metadata']['supported'] is True)
        ok(d['warning_status']=='unknown')
        ok(d['row_count']==len(d['rows'])==len(d['typed_rows']))
        for row,ts in zip(d['rows'],d['typed_rows']):
            ok(row.keys()==ts.keys())
            for key,t in ts.items():
                if t['php_type']=='double':
                    ok(type(t['value']) is float)
                    ok(t['native_double_hex']==struct.pack('<d',t['value']).hex())
                    ok(t['native_double_hex']==struct.pack('<d',row[key]).hex())
                    ok(t['byte_order']=='little' and isinstance(t['float17g'],str))
                else:ok(row[key]==t['value'])
        if d['role']=='literal_rint':
            ok(len(d['rows'])==1 and len(d['rows'][0])==13)
            ok(len(d['column_metadata']['columns'])==13)
    ok(any(e['event']=='connection-released' and e['data']['PDO_released'] is True for e in es))
    ok(not any(e['event'] in ['read-or-prepare-failure','stopped'] for e in es))
    complete=es[-1]
    ok(complete['event']=='complete')
    for key,value in {'attempts':50,'SELECT_executions':49,'prepare_only':1,'literal_cases':47,'DDL':0,'DML':0,'natural':0,'B_SQL':0,'HTTP':0,'transaction_calls':0,'new_objects_created_by_runner':0}.items():ok(complete['data'][key]==value)
    ok(analysis==A.analyze())
    def no_float(value):
        ok(type(value) is not float)
        if isinstance(value,dict):
            for x in value.values():no_float(x)
        elif isinstance(value,list):
            for x in value:no_float(x)
    no_float(analysis)
    ok(analysis['summary']['stored_B_bit_matches']==44)
    ok(analysis['summary']['contextual_getter_B_bit_matches']==44)
    ok(analysis['summary']['stage_differences_vs_source_algebra_observed_C_X']=={})
    ok([m['case_id'] for m in analysis['summary']['mismatches']]==['S-L39','S-L40','OS-L03'])
    return n


if __name__=='__main__':
    raw=(R/'private-evidence/C-run.jsonl').read_bytes()
    assert hashlib.sha256(raw).hexdigest()=='7560ebc93bc675dd45fd2b8f293c14f18d1c5e661225aa6668ce9a7bbaea136b'
    assert stat.S_IMODE((R/'private-evidence').stat().st_mode)==0o700 and stat.S_IMODE((R/'private-evidence/C-run.jsonl').stat().st_mode)==0o600
    es=[json.loads(x) for x in raw.splitlines()];analysis=json.loads((R/'observations-bit-strings.json').read_bytes())
    count=verify(es,analysis);rejected=[]
    def mutation(label,fn):
        e=copy.deepcopy(es);a=copy.deepcopy(analysis);fn(e,a)
        try:verify(e,a)
        except (AssertionError,KeyError,ValueError):rejected.append(label)
        else:raise AssertionError('mutation accepted '+label)
    first_start=lambda e:next(x['data'] for x in e if x['event']=='read-start' and x['data']['case_id'])
    first_result=lambda e:next(x['data'] for x in e if x['event']=='read-observed-before-gate' and x['data']['case_id'])
    mutation('changed SQL',lambda e,a:first_start(e).__setitem__('sql','SELECT * FROM NUTRITION'))
    mutation('changed runtime input',lambda e,a:first_start(e)['parameters'][0]['runtime'].__setitem__('value','12.35'))
    mutation('changed PDO type',lambda e,a:first_start(e)['parameters'][0].__setitem__('pdo_type_code',1))
    mutation('missing event',lambda e,a:e.pop(10))
    mutation('budget expanded',lambda e,a:e[-1]['data'].__setitem__('SELECT_executions',50))
    mutation('wrong native HEX',lambda e,a:first_result(e)['typed_rows'][0]['x'].__setitem__('native_double_hex','0000000000000000'))
    mutation('unknown metadata',lambda e,a:first_result(e)['column_metadata'].__setitem__('supported',False))
    mutation('bytes overflow',lambda e,a:first_result(e).__setitem__('serialized_bytes_before_bytecount_field',65537))
    mutation('hide actual mismatch',lambda e,a:a['summary'].__setitem__('stored_B_bit_matches',47))
    mutation('signed-zero stage sign hidden',lambda e,a:next(c for c in a['cases'] if c['original_binding']['original_value']=='-0')['actual_C_stages']['x'].__setitem__('native_double_hex','0000000000000000'))
    unchanged=[]
    for folder,name,key in [(BASE,'execution-seal.json','public_sha256'),(BASE/'primary-source-derivation-20261011','research-manifest.json','files'),(BASE/'half-tie-execution-20261011','seal.json','public_sha256'),(BASE/'c-literal-rint-proposal-20261011','review-seal.json','public_sha256')]:
        m=json.loads((folder/name).read_bytes())
        for path,sha in m[key].items():assert hashlib.sha256((folder/path).read_bytes()).hexdigest()==sha,path
        unchanged.append({'seal':name,'files':len(m[key])})
    print(json.dumps({'status':'PURE_EXECUTION_VERIFICATION_GREEN_ACTUAL_CANDIDATE_PARTIAL_MATCH','assertions':count,'mutation_rejections':rejected,'raw_sha256':hashlib.sha256(raw).hexdigest(),'old_and_review_seals_unchanged':unchanged,'no_derived_floating_JSON_numbers':True},indent=2))
