"""Read-only UTF8/HEX baseline, no JSON functions required on MySQL5.6."""
from pathlib import Path
import subprocess,json,hashlib,gzip,sys,re,time
ROOT=Path(__file__).resolve().parents[1]
CMD=['docker','exec','-i','front-mysql56-upgrade-20261023','mysql','--defaults-extra-file=/run/migration.cnf','--default-character-set=utf8mb4','--batch','--raw','--skip-column-names','--quick','--max-allowed-packet=256M']
def q(sql):return subprocess.check_output(CMD,input=("SET NAMES utf8mb4;SET SESSION time_zone='+08:00';"+sql).encode()).decode('utf8').splitlines()
def ident(v):
 assert re.fullmatch(r'[a-zA-Z0-9_]+',v)
 return '`'+v+'`'
def h(v):return None if v=='NULL' else bytes.fromhex(v).decode('utf8')
def baseline(db,label,original=None):
 assert q("SELECT HEX('中文😀é');")==['E4B8ADE69687F09F9880C3A9']
 out=ROOT/'private'/label;out.mkdir(mode=0o700,exist_ok=True)
 meta={};counts={};digests={};maxbytes={};types={};t0=time.time()
 tables=q("SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA='"+db+"' AND TABLE_TYPE='BASE TABLE' ORDER BY TABLE_NAME;")
 original_meta=json.loads(Path(original).read_text())['metadata'] if original else None
 for table in tables:
  cols=[]
  raw=q("SELECT COLUMN_NAME,DATA_TYPE,COLUMN_TYPE,IS_NULLABLE,HEX(COLUMN_DEFAULT),EXTRA,COALESCE(CHARACTER_SET_NAME,''),COALESCE(COLLATION_NAME,''),HEX(COLUMN_COMMENT) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='"+db+"' AND TABLE_NAME='"+table+"' ORDER BY ORDINAL_POSITION;")
  for line in raw:
   a=line.split('\t');assert len(a)==9
   cols.append(dict(zip(['name','data_type','type','nullable','default','extra','charset','collation','comment'],a)))
   cols[-1]['default']=h(a[4]);cols[-1]['comment']=h(a[8]);types[a[1]]=types.get(a[1],0)+1
  meta[table]=cols
  chosen=original_meta.get(table,[]) if original_meta else cols
  if original_meta and not chosen:continue
  expr=','.join("IF("+ident(c['name'])+" IS NULL,'N',CONCAT('H',HEX(CAST("+ident(c['name'])+" AS BINARY))))"for c in chosen)
  sql='SET NAMES utf8mb4;SET SESSION time_zone=\'+08:00\';SELECT '+expr+' FROM '+ident(db)+'.'+ident(table)+';'
  stderr=(out/(table+'.stderr')).open('wb')
  p=subprocess.Popen(CMD,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=stderr);p.stdin.write(sql.encode());p.stdin.close()
  hashes=[];n=0;lengths=[0]*len(chosen)
  with gzip.open(out/(table+'.hex.jsonl.gz'),'wt',encoding='utf8',compresslevel=1)as f:
   for line in p.stdout:
    vals=line.rstrip(b'\n').decode('ascii').split('\t');assert len(vals)==len(chosen)
    row=[]
    for i,v in enumerate(vals):
     if v=='N':row.append(None)
     else:
      assert v.startswith('H');value=v[1:];bytes.fromhex(value);row.append(value);lengths[i]=max(lengths[i],len(value)//2)
    canonical=json.dumps(row,separators=(',',':'));f.write(canonical+'\n');hashes.append(hashlib.sha256(canonical.encode()).digest());n+=1
  rc=p.wait();stderr.close();assert rc==0,table
  counts[table]=n;digests[table]=hashlib.sha256(b''.join(sorted(hashes))).hexdigest();maxbytes[table]=dict(zip([c['name']for c in chosen],lengths))
  ddl=q('SHOW CREATE TABLE '+ident(db)+'.'+ident(table)+';');(out/(table+'.ddl.txt')).write_text('\n'.join(ddl))
 report={'db':db,'tables':len(tables),'rows':sum(counts.values()),'counts':counts,'metadata':meta,'multiset_sha256':digests,'max_bytes':maxbytes,'types':types,'encoding_hex_probe':True,'elapsed_s':round(time.time()-t0,2)}
 (out/'baseline.json').write_text(json.dumps(report,ensure_ascii=False,indent=2)+'\n');print(json.dumps({'db':db,'tables':len(tables),'rows':report['rows'],'types':types,'elapsed_s':report['elapsed_s']}),flush=True)
 return report
if __name__=='__main__':baseline(*sys.argv[1:])
