"""Run unchanged Electrum-LTC URI functions in a source-isolated harness.
This is NOT a wallet GUI, camera, signing, or full application compatibility test.
Usage: python uri-parser-study.py /path/Electrum-LTC-4.2.2.1 output-directory
Only Python's standard library is required. No network is accessed.
"""
from pathlib import Path
import ast,types,sys,typing,hashlib,json,datetime,decimal,re,urllib.parse,csv
def main():
 src=Path(sys.argv[1])/'electrum_ltc';out=Path(sys.argv[2]);out.mkdir(parents=True,exist_ok=True)
 pkg=types.ModuleType('lw_electrum_source');pkg.__path__=[];sys.modules[pkg.__name__]=pkg
 g={**vars(typing),'Decimal':decimal.Decimal,'re':re,'urllib':urllib,'hashlib':hashlib,'__package__':pkg.__name__}
 chosen={};hashes={}
 def extract(file,names):
  text=(src/file).read_text(encoding='utf-8');hashes[file]=hashlib.sha256((src/file).read_bytes()).hexdigest()
  tree=ast.parse(text);nodes=[n for n in tree.body if isinstance(n,(ast.FunctionDef,ast.ClassDef)) and n.name in names]
  assert set(n.name for n in nodes)==set(names)
  chosen[file]=names;exec(compile(ast.Module(body=nodes,type_ignores=[]),str(src/file),'exec'),g)
 extract('util.py',['BitcoinException','InvalidBitcoinURI','assert_bytes','to_bytes','bh2u','parse_URI'])
 extract('crypto.py',['sha256','sha256d'])
 # Actual source assignments; no address checker is replaced with a permissive stub.
 tree=ast.parse((src/'bitcoin.py').read_text(encoding='utf-8'))
 constant_names={'COIN','TOTAL_COIN_SUPPLY_LIMIT_IN_BTC','__b58chars','__b43chars'}
 nodes=[n for n in tree.body if isinstance(n,ast.Assign) and any(isinstance(t,ast.Name) and t.id in constant_names for t in n.targets)]
 assert len(nodes)==4;exec(compile(ast.Module(body=nodes,type_ignores=[]),str(src/'bitcoin.py'),'exec'),g)
 seg=types.ModuleType('segwit_addr');exec(compile((src/'segwit_addr.py').read_text(),str(src/'segwit_addr.py'),'exec'),seg.__dict__);g['segwit_addr']=seg
 # Mainnet constants are asserted against the upstream declarations below.
 const=(src/'constants.py').read_text()
 for token in ['ADDRTYPE_P2PKH = 48','ADDRTYPE_P2SH = 50','SEGWIT_HRP = "ltc"']:assert token in const
 g['constants']=types.SimpleNamespace(net=types.SimpleNamespace(ADDRTYPE_P2PKH=48,ADDRTYPE_P2SH=50,SEGWIT_HRP='ltc'))
 version=(src/'util.py').read_text();assert "BITCOIN_BIP21_URI_SCHEME = 'litecoin'" in version
 g['BITCOIN_BIP21_URI_SCHEME']='litecoin'
 extract('bitcoin.py',['hash160_to_b58_address','b58_address_to_hash160','BaseDecodeError','InvalidChecksum','base_encode','base_decode','EncodeBase58Check','DecodeBase58Check','is_segwit_address','is_b58_address','is_address'])
 bitname=pkg.__name__+'.bitcoin';bit=types.ModuleType(bitname);bit.__dict__.update(g);bit.__name__=bitname;sys.modules[bitname]=bit;pkg.bitcoin=bit
 for n in ['segwit_addr.py','constants.py','version.py']:hashes[n]=hashlib.sha256((src/n).read_bytes()).hexdigest()
 addr=g['hash160_to_b58_address'](bytes.fromhex('11'*20),48)
 assert g['is_address'](addr)
 bad=addr[:-1]+('1' if addr[-1]!='1' else '2')
 segaddr=seg.encode_segwit_address('ltc',0,bytes.fromhex('22'*20))
 p='litecoin:'+addr
 specs=[
 ('plain-address',addr,'Address only'),
 ('uri-no-amount',p,'URI with no amount'),
 ('eight-decimals',p+'?amount=0.12345678','Exactly eight decimals'),
 ('one-litoshi',p+'?amount=0.00000001','One litoshi'),
 ('nine-decimals',p+'?amount=0.123456789','Nine decimals'),
 ('negative-amount',p+'?amount=-1','Negative amount'),
 ('decimal-comma',p+'?amount=0,125','Decimal comma'),
 ('duplicate-amount',p+'?amount=1&amount=2','Duplicate amount'),
 ('empty-amount',p+'?amount=','Empty amount'),
 ('unknown-optional',p+'?amount=0.125&invoice=17','Unknown optional field'),
 ('unknown-required',p+'?amount=0.125&req-invoice=17','Unknown required field'),
 ('encoded-label',p+'?amount=0.125&label=Caf%C3%A9%20%26%20invoice%2017','UTF-8 and encoded ampersand'),
 ('unescaped-ampersand',p+'?amount=0.125&label=Cafe & invoice 17','Unescaped ampersand'),
 ('bad-checksum','litecoin:'+bad+'?amount=0.125','Wrong address checksum'),
 ('wrong-scheme','bitcoin:'+addr+'?amount=0.125','Wrong currency scheme'),
 ('uppercase-scheme','LITECOIN:'+addr+'?amount=0.125','Uppercase scheme'),
 ('native-segwit','litecoin:'+segaddr+'?amount=0.125','Native SegWit address'),
 ('scientific-notation',p+'?amount=1e-8','Scientific notation'),
 ('amount-over-supply',p+'?amount=84000001','Above total supply'),
 ('expires-field',p+'?amount=0.125&exp=1&time=1','Time and expiry metadata')]
 cases=[]
 for id,uri,label in specs:
  try:parsed=g['parse_URI'](uri);r={'accepted':True,'parsed':parsed}
  except Exception as e:
   # Missing harness dependencies must fail the study, never count as a rejection.
   if isinstance(e,(NameError,ImportError,AttributeError)):raise
   r={'accepted':False,'error_type':type(e).__name__,'error':str(e)}
  cases.append({'id':id,'label':label,'uri':uri,**r})
 by={r['id']:r for r in cases}
 assert by['eight-decimals']['parsed']['amount']==12345678
 assert by['one-litoshi']['parsed']['amount']==1
 assert not by['bad-checksum']['accepted'] and not by['wrong-scheme']['accepted']
 assert not by['duplicate-amount']['accepted'] and by['native-segwit']['accepted']
 result={'release':'2026-09-30.1','tested_at':datetime.datetime.now(datetime.timezone.utc).isoformat(),'version':'Electrum-LTC 4.2.2.1','method':'Unmodified upstream function ASTs executed in a minimal standard-library harness; upstream Base58Check and SegWit validation included. Full package, GUI, camera, signing and transaction construction not run. No payment-request callback supplied; no network requests.','source_sha256':hashes,'extracted_functions':chosen,'fixtures':'Synthetic public address hashes, with no known private keys. Never fund the fixtures. Published scan examples use an invalid address instead.','cases':cases}
 (out/'uri-results.json').write_text(json.dumps(result,indent=2,ensure_ascii=False)+'\n',encoding='utf-8')
 (out/'uri-corpus.json').write_text(json.dumps([{'id':r['id'],'label':r['label'],'uri':r['uri']} for r in cases],indent=2,ensure_ascii=False)+'\n',encoding='utf-8')
 with (out/'uri-results.csv').open('w',newline='',encoding='utf-8') as f:
  w=csv.writer(f);w.writerow(['id','label','parser_accepted','amount_litoshis','parsed_json','error'])
  for r in cases:w.writerow([r['id'],r['label'],r['accepted'],r.get('parsed',{}).get('amount',''),json.dumps(r.get('parsed',{}),ensure_ascii=False),r.get('error','')])
 print(json.dumps({'cases':len(cases),'results':[{k:r[k] for k in ['id','accepted',*(['parsed'] if r['accepted'] else ['error'])]} for r in cases]},ensure_ascii=False))
if __name__=='__main__':main()
