from pathlib import Path
import json,re,shutil,runpy
from bs4 import BeautifulSoup,Comment
mod=runpy.run_path(str(Path(__file__).with_name('translate-fa.py')));prose=mod['prose'];ROOT=mod['ROOT']
def fragment(s):
 # A JavaScript template literal can split an HTML attribute around an interpolation.
 # Keep incomplete attribute/tag fragments verbatim and translate only visible text.
 prefix='';suffix=''
 if re.search(r'\b[\w:-]+=[\"\']',s) and ('<' not in s or s.index('<')>s.index('=')):
  if '>' not in s:return s
  i=s.index('>')+1;prefix=s[:i];s=s[i:]
 if re.match(r'^[\"\')]',s) and '>' in s and ('<' not in s or s.index('>')<s.index('<')):
  i=s.index('>')+1;prefix=s[:i];s=s[i:]
 dangling=re.search(r'<[a-zA-Z/][^>]*$',s)
 if dangling:suffix=s[dangling.start():];s=s[:dangling.start()]
 return prefix+prose(s)+suffix
raw=(ROOT/'vendor/cardiomechanics/index.html').read_text()
for t in sorted(json.loads((ROOT/'scripts/cardio-tokens.json').read_text()),key=lambda t:t['start'],reverse=True):
 s=fragment(t['text'])
 if t['kind']=='literal':
  q=t['quote'];replacement=q+s.replace('\\','\\\\').replace(q,'\\'+q).replace('\n','\\n').replace('\r','')+q
 else:
  s=s.replace('\\','\\\\').replace('`','\\`')
  replacement=('`' if t['kind']=='head' else '}')+s+('${' if t['kind'] in ['head','middle'] else '`')
 raw=raw[:t['start']]+replacement+raw[t['end']:]
# Translate HTML only outside script/style; never reserialize the scripts.
def html_text(match):
 frag=match.group(0)
 if frag.startswith(('<script','<style','<!--')):return frag
 if frag.startswith('<'):
  return re.sub(r'((?:aria-label|title|placeholder)=)(["\'])(.*?)\2',lambda m:m[1]+m[2]+prose(m[3])+m[2],frag)
 return prose(frag)
raw=re.sub(r'<script\b[^>]*>[\s\S]*?</script>|<style\b[^>]*>[\s\S]*?</style>|<!--[\s\S]*?-->|<[^>]*>|[^<]+',html_text,raw)
# Child must not register an origin-wide service worker: parent atlas owns offline caches.
raw=re.sub(r'<script>if\(\x27serviceWorker\x27in navigator\)[\s\S]*?</script>','',raw)
raw=re.sub(r'<link[^>]+(?:fonts.googleapis.com|fonts.gstatic.com)[^>]*>','',raw)
raw=re.sub(r'<link[^>]+rel="manifest"[^>]*>','',raw)
raw=raw.replace('<html lang="en"','<html lang="fa" dir="rtl"')
css='''<style>@font-face{font-family:Vazirmatn;src:url(../../fonts/vazirmatn.woff2) format("woff2");font-display:swap}body,button,input,select,.brand-name,h1,h2,h3,h4,p,.nav{font-family:Vazirmatn,sans-serif!important}body{line-height:1.85}.brand-name{letter-spacing:0}.sym,.metric-val,.slider-val,.formula,svg,input[type=range]{direction:ltr;unicode-bidi:isolate}svg text{font-family:Vazirmatn,sans-serif}.nav{flex-wrap:wrap}.field-lbl{line-height:1.8}.col-left,.col-right,.sim{min-width:0}.readout{direction:rtl}.cm-credit{padding:1rem;text-align:center;font-size:12px}@media(max-width:600px){.topbar-inner{flex-wrap:wrap}.nav{width:100%}.brand-tag{font-size:10px}.wrap{padding-inline:10px}.sim{grid-template-columns:minmax(0,1fr)}.panel{min-width:0}.plot-wrap{overflow:hidden}}</style>'''
raw=raw.replace('</head>',css+'</head>')
# Validated deep link for the shared scenario cards.
raw=raw.replace("loadCondition('normal');","loadCondition('normal');const requested=new URLSearchParams(location.search).get('condition');if(requested&&Object.hasOwn(PATHS,requested))loadCondition(requested);")
raw=raw.replace('</body>','<p class="cm-credit">بر پایهٔ <a href="https://github.com/torsades-de-pointes/cardiomechanics" target="_blank" rel="noopener">CardioMechanics</a> · محتوای آموزشی CC BY 4.0 · <a href="../../fa/credits/" target="_top">منابع و سازندگان تپش</a></p></body>')
# Display-only translation of event labels; core labels remain unchanged for calcMetrics.
raw=raw.replace('</body>', '''<script>new MutationObserver(records=>{for(const r of records){const node=r.target.nodeType===3?r.target.parentElement:r.target;if(!node)continue;for(const el of [node,...node.querySelectorAll('text')]){if(el.tagName?.toLowerCase()!=='text'||el.children.length)continue;const old=el.textContent;const dict={'MV closes':'بسته شدن میترال','AV opens':'باز شدن آئورت','AV closes':'بسته شدن آئورت','MV opens':'باز شدن میترال'};for(const [en,fa] of Object.entries(dict))if(old.includes(en)){el.textContent=old.replace(en,fa);break;}}}}).observe(document.body,{childList:true,characterData:true,subtree:true});</script></body>''')
for lang,data in [('fa',raw),('en',(ROOT/'vendor/cardiomechanics/index.html').read_text())]:
 data=re.sub(r'<script>if\(\x27serviceWorker\x27in navigator\)[\s\S]*?</script>','',data)
 data=re.sub(r'<link[^>]+rel="manifest"[^>]*>','',data)
 data=re.sub(r'<link[^>]+(?:fonts.googleapis.com|fonts.gstatic.com)[^>]*>','',data)
 if lang=='en':data=data.replace("loadCondition('normal');","loadCondition('normal');const requested=new URLSearchParams(location.search).get('condition');if(requested&&Object.hasOwn(PATHS,requested))loadCondition(requested);")
 out=ROOT/'apps/web/public/lab'/lang;out.mkdir(parents=True,exist_ok=True);(out/'index.html').write_text(data);shutil.copytree(ROOT/'vendor/cardiomechanics/icons',out/'icons',dirs_exist_ok=True)
fonts=ROOT/'apps/web/public/fonts';fonts.mkdir(exist_ok=True)
p=next((ROOT/'node_modules/.pnpm').glob('@fontsource-variable+vazirmatn@*/node_modules/@fontsource-variable/vazirmatn/files/vazirmatn-arabic-wght-normal.woff2'))
shutil.copyfile(p,fonts/'vazirmatn.woff2');license=p.parent.parent/'LICENSE';shutil.copyfile(license,ROOT/'apps/web/public/licenses/vazirmatn-OFL.txt')
print('Rendered standalone laboratory, child service worker disabled')
