"""Synthetic Base64/UTF-8 boundary experiment; Python standard library only."""
from pathlib import Path
import base64, codecs, csv, hashlib, io, json, platform

HERE=Path(__file__).resolve().parent
OUT=HERE/'generated'

def error_record(exc,phase):
    return dict(type=type(exc).__name__,message=str(exc),phase=phase,
                start=getattr(exc,'start',None),end=getattr(exc,'end',None),
                reason=getattr(exc,'reason',None))

def decode_chunks(chunks,incremental=True):
    carry=''; recovered=bytearray(); text=[]; events=[]
    decoder=codecs.getincrementaldecoder('utf-8')('strict')
    phase='base64'; seen_padding=False
    try:
        for index,chunk in enumerate(chunks):
            if seen_padding and chunk: raise ValueError('Data after terminal padding')
            carry+=chunk
            take=len(carry)//4*4
            if not take:continue
            block,carry=carry[:take],carry[take:]
            phase='base64'
            if '=' in block:
                if carry or block.rstrip('=').find('=')>=0:raise ValueError('Invalid terminal padding')
                seen_padding=True
            raw=base64.b64decode(block,validate=True)
            recovered.extend(raw);phase='utf8-feed'
            emitted=decoder.decode(raw,final=False) if incremental else raw.decode('utf-8','strict')
            text.append(emitted)
            events.append(dict(transport_index=index,base64_block=block,base64_carry=carry,
                decoded_hex=raw.hex(),decoded_bytes=len(raw),emitted=emitted,
                utf8_pending_hex=decoder.getstate()[0].hex() if incremental else ''))
        phase='base64-final'
        if carry:raise ValueError('Incomplete Base64 quartet at EOF: '+str(len(carry))+' characters')
        phase='utf8-final'
        pending=decoder.getstate()[0].hex() if incremental else ''
        if incremental:text.append(decoder.decode(b'',final=True))
        return dict(ok=True,text=''.join(text),recovered_hex=recovered.hex(),
                    decoded_bytes=len(recovered),events=events,pending_before_final_hex=pending)
    except (UnicodeError,ValueError) as exc:
        return dict(ok=False,text_prefix=''.join(text),recovered_hex=recovered.hex(),
                    decoded_bytes=len(recovered),events=events,
                    pending_before_error_hex=decoder.getstate()[0].hex(),error=error_record(exc,phase))

def partitions(value):
    for mask in range(1<<(len(value)-1)):
        cuts=[i for i in range(1,len(value)) if mask&(1<<(i-1))]
        edges=[0]+cuts+[len(value)]
        yield mask,[value[a:b] for a,b in zip(edges,edges[1:])]

def main():
    OUT.mkdir(exist_ok=True)
    fixtures={'unicode':bytes.fromhex('41e282acf09f9a805a'),
              'ascii':b'ABCDEFGHI',
              'truncated-utf8':bytes.fromhex('41e282acf09f9a'),
              'invalid-utf8':bytes.fromhex('41ff5a')}
    report=dict(python=platform.python_version(),fixtures={},chunk_matrix=[],partition_summary={})
    for name,raw in fixtures.items():
        encoded=base64.b64encode(raw).decode('ascii')
        (OUT/(name+'.bin')).write_bytes(raw)
        (OUT/(name+'.b64.txt')).write_text(encoded,encoding='ascii')
        assert base64.b64decode(encoded,validate=True)==raw
        report['fixtures'][name]=dict(bytes=len(raw),hex=raw.hex(),base64=encoded,
            base64_characters=len(encoded),sha256=hashlib.sha256(raw).hexdigest(),base64_roundtrip=True)
    value=report['fixtures']['unicode']['base64']
    for size in [1,2,3,4,5,7,12]:
        chunks=[value[i:i+size] for i in range(0,len(value),size)]
        good=decode_chunks(chunks);bad=decode_chunks(chunks,False)
        assert good['ok'] and good['text']=='A\u20ac\U0001f680Z'
        assert good['recovered_hex']==fixtures['unicode'].hex()
        report['chunk_matrix'].append(dict(chunk_characters=size,chunks=chunks,incremental=good,stateless=bad))
    csv_rows=[]
    for name,fixture in report['fixtures'].items():
        value=fixture['base64']; counts=dict(partitions=0,incremental_success=0,stateless_success=0)
        for mask,chunks in partitions(value):
            good=decode_chunks(chunks);bad=decode_chunks(chunks,False)
            counts['partitions']+=1; counts['incremental_success']+=int(good['ok']);counts['stateless_success']+=int(bad['ok'])
            if name in ['unicode','ascii']:
                assert good['ok'] and good['recovered_hex']==fixtures[name].hex()
                assert good['text']==fixtures[name].decode('utf-8')
            else: assert not good['ok']
            csv_rows.append([name,mask,'|'.join(chunks),int(good['ok']),int(bad['ok']),
                good.get('error',{}).get('phase',''),bad.get('error',{}).get('phase','')])
        report['partition_summary'][name]=counts
    report['controls']={name:decode_chunks([fixture['base64'][i:i+4] for i in range(0,len(fixture['base64']),4)]) for name,fixture in report['fixtures'].items() if name not in ['unicode','ascii']}
    report['controls']['truncated-base64']=decode_chunks([report['fixtures']['unicode']['base64'][:-1]])
    assert not report['controls']['truncated-base64']['ok']
    stream=io.StringIO(newline='');writer=csv.writer(stream,lineterminator='\n')
    writer.writerow(['fixture','cut_mask','transport_chunks','incremental_ok','stateless_ok','incremental_error_phase','stateless_error_phase']);writer.writerows(csv_rows)
    (OUT/'partitions.csv').write_text(stream.getvalue(),encoding='utf-8')
    (OUT/'measurements.json').write_text(json.dumps(report,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
    print(json.dumps(report,ensure_ascii=False,indent=2))

if __name__=='__main__':main()
