"""Execute unchanged Cake Wallet constructor/fromUri function in Dart.
Isolated parsing boundary only. Unsupported dependency paths throw; no GUI,
address validation, amount validation, signing, camera or network is tested.
Usage: python run-cake-study.py payment_request.dart dart.exe corpus.json outdir
"""
from pathlib import Path
import json,sys,hashlib,subprocess,datetime
def extract(text,signature):
 start=text.index(signature);a=text.index('{',start);depth=1;i=a+1
 while depth:
  if text[i]=='{':depth+=1
  elif text[i]=='}':depth-=1
  i+=1
 return text[start:i]
src,dart,corpus,out=map(Path,sys.argv[1:]);out.mkdir(exist_ok=True);text=src.read_text()
constructor=text[text.index('  PaymentRequest('):text.index('  factory PaymentRequest.fromString')]
factory=extract(text,'  factory PaymentRequest.fromUri(Uri? uri)')
fields='final String address,amount,note,scheme; final String? pjUri,callbackUrl,callbackMessage,contractAddress,rawTokenAmount; final int? chainId;'
deps='''
// Guarded harness adapters: no accepted Litecoin fixture enters these paths.
// A Lightning-shaped input would abort this experiment, not return a result.
dynamic getBolt11Amount(String s){if(s.toLowerCase().startsWith('ln'))throw UnsupportedError('outside corpus');throw FormatException('not a Lightning invoice');}
class Money{static dynamic zero(dynamic x)=>throw UnsupportedError('outside corpus');}
class CryptoCurrency{static dynamic btcln;}
dynamic nano=null,nanoUtil=null;
class ERC681URI{static dynamic fromUri(Uri uri)=>throw UnsupportedError('outside corpus');}
'''
body="import 'dart:convert';import 'dart:io';\n"+deps+'\nclass PaymentRequest{'+constructor+factory+fields+"static bool _isAlreadyUsableAmount(String s)=>throw UnsupportedError('Nano outside corpus');}\n"
body+='''void main(List<String> args){final cases=jsonDecode(File(args[0]).readAsStringSync()) as List;final results=[];for(final c in cases){try{final uri=Uri.parse(c['uri']);if(['ethereum','tron','solana','nano','lightning'].contains(uri.scheme)||uri.path.toLowerCase().startsWith('ln'))throw UnsupportedError('outside corpus');final r=PaymentRequest.fromUri(uri);results.add({'id':c['id'],'parsed':{'address':r.address,'amount_text':r.amount,'note':r.note,'scheme':r.scheme},'parsed_without_error':true});}catch(e){if(e is UnsupportedError)rethrow;results.add({'id':c['id'],'parsed_without_error':false,'error':e.toString()});}}print(jsonEncode(results));}
'''
(out/'cake-harness.dart').write_text(body)
p=subprocess.run([str(dart),'run',str(out/'cake-harness.dart'),str(corpus)],capture_output=True,text=True,timeout=60)
if p.returncode:raise RuntimeError(p.stderr+p.stdout)
cases=json.loads(p.stdout);by={x['id']:x for x in cases}
assert by['eight-decimals']['parsed']['amount_text']=='0.12345678'
assert by['bad-checksum']['parsed_without_error'] and by['negative-amount']['parsed']['amount_text']=='-1'
r={'schema':1,'release':'2026-09-30.1','wallet_version':'Cake Wallet v6.4.5','source_commit':'9fe23970c7a6e7f88e2bab88abe4e6346c732316','tested_at':datetime.datetime.now(datetime.timezone.utc).isoformat(),'dart_version':subprocess.check_output([str(dart),'--version'],text=True).strip(),'source_sha256':hashlib.sha256(src.read_bytes()).hexdigest(),'harness_sha256':hashlib.sha256((out/'cake-harness.dart').read_bytes()).hexdigest(),'scope':'Unchanged constructor and PaymentRequest.fromUri factory, compiled and executed with Dart. Litecoin/Bitcoin/plain-address corpus only. Guarded throwing adapters for unrelated Lightning/ERC681/Nano dependencies. No full app, QR camera, address/amount validation, signing, sending or mobile GUI. Parsed without error does NOT mean payable or safe.','cases':cases}
(out/'cake-results.json').write_text(json.dumps(r,indent=2)+'\n');print(json.dumps({'cases':len(cases),'duplicate_amount':by['duplicate-amount']['parsed'],'unknown_required':by['unknown-required']['parsed'],'label':by['encoded-label']['parsed']}))
