#!/usr/bin/env python3
from __future__ import annotations
import asyncio, base64, importlib.util, json, mimetypes, re
from pathlib import Path
ROOT=Path(__file__).resolve().parents[1]
OUT=ROOT/'qa_screenshots'; OUT.mkdir(exist_ok=True)
spec=importlib.util.spec_from_file_location('rc18qa', str(ROOT/'tests/rc18_pose_motion_runtime_safari_scale_real_dom_qa.py'))
rc18qa=importlib.util.module_from_spec(spec)
spec.loader.exec_module(rc18qa)

def data_uri(src: str):
    if not src or src.startswith(('data:','http')): return None
    clean=src.split('?',1)[0].split('#',1)[0]
    if clean.startswith('file://'):
        p=Path(clean[7:])
    else:
        p=ROOT/clean.lstrip('/')
    if not p.exists() or not p.is_file(): return None
    mime=mimetypes.guess_type(p.name)[0] or 'application/octet-stream'
    return clean, 'data:%s;base64,%s'%(mime,base64.b64encode(p.read_bytes()).decode('ascii'))

async def inline_all_images(page):
    for _ in range(3):
        imgs=await page.evaluate("""() => Array.from(document.images).map((img,i)=>({i,src:img.getAttribute('src')||''}))""")
        changed=0
        for im in imgs:
            d=data_uri(im['src'])
            if not d: continue
            clean,uri=d
            await page.evaluate("""({i,clean,uri})=>{const img=document.images[i]; if(img && !img.dataset.qaInlined){img.dataset.qaOriginalSrc=clean; img.dataset.qaInlined='1'; img.src=uri;}}""", {'i':im['i'],'clean':clean,'uri':uri})
            changed+=1
        await page.wait_for_timeout(120)
        if changed==0: break

async def boot(page,width,height):
    await page.set_viewport_size({'width':width,'height':height})
    await page.set_content(rc18qa.inline_preview(), wait_until='domcontentloaded')
    await page.wait_for_function("() => window.showProgram && window.chooseProgram && window.NEXTActions && window.showImageAdmin && window.showQa", timeout=10000)
    await page.evaluate("() => { try{localStorage.clear();sessionStorage.clear();}catch(e){} } ")
    await page.wait_for_timeout(200)

async def verify_program_click(page,width,height,name):
    await boot(page,width,height)
    # Open normal Program screen through the same public action path used by the top navigation.
    await page.evaluate("() => window.NEXTActions.run('showProgram')")
    await page.wait_for_timeout(250)
    before=await page.evaluate("""() => ({
      active:document.querySelector('.screen.active')?.id||'',
      cardCount:document.querySelectorAll('#programCatalog .next-program-card').length,
      chooseButtons:Array.from(document.querySelectorAll('#programCatalog [data-next-action="choose-program"]')).map(b=>b.dataset.programId),
      inlineChooseButtons:document.querySelectorAll('#programCatalog button[onclick*=chooseProgram]').length,
      activeProgram:window.activeProgramId?.()||''
    })""")
    if before['active']!='programScreen': raise AssertionError('Program screen did not open')
    if before['cardCount']<3: raise AssertionError('Program cards did not render')
    if 'program-stage-2' not in before['chooseButtons']: raise AssertionError('Stage 2 delegated choose button missing')
    if before['inlineChooseButtons']!=0: raise AssertionError('Program choose buttons still depend on inline onclick')
    btn=page.locator('#programCatalog [data-next-action="choose-program"][data-program-id="program-stage-2"]').first
    await btn.scroll_into_view_if_needed(timeout=5000)
    await btn.click(timeout=5000, force=True)
    await page.wait_for_timeout(350)
    after=await page.evaluate("""() => ({
      active:document.querySelector('.screen.active')?.id||'',
      activeProgram:window.activeProgramId?.()||'',
      selected:Array.from(document.querySelectorAll('#programCatalog .next-program-card.selected h2,#programCatalog .next-program-card.selected h3')).map(x=>x.textContent.trim()),
      profileStrip:document.querySelector('.next-profile-strip')?.textContent.trim()||''
    })""")
    if after['activeProgram']!='program-stage-2': raise AssertionError(f"Delegated click did not select Stage 2: {after}")
    if not any('9–12' in x or 'Utveckling' in x for x in after['selected']): raise AssertionError('Stage 2 card is not marked selected')
    await inline_all_images(page)
    await page.evaluate("() => window.scrollTo(0,0)")
    await page.wait_for_timeout(180)
    await page.screenshot(path=str(OUT/f'rc22_program_click_{name}_{width}x{height}.png'), full_page=False)
    return {'before':before,'after':after}

async def verify_assets(page):
    await boot(page,1440,1024)
    await page.evaluate("() => window.showQa()")
    await page.wait_for_timeout(600)
    await page.evaluate("""() => {
      const rows=Array.from(document.querySelectorAll('.next-library-row'));
      const idx=rows.findIndex(r=>/armhävning|push/i.test(r.textContent));
      window.showExerciseDetail(idx>=0?idx:0);
    }""")
    await page.wait_for_timeout(300)
    await inline_all_images(page)
    library=await page.evaluate("""() => ({
      active:document.querySelector('.screen.active')?.id||'',
      rows:document.querySelectorAll('.next-library-row').length,
      visualCount:document.querySelectorAll('.next-exercise-visual').length,
      figures:Array.from(document.querySelectorAll('.next-library-detail .next-exercise-visual-figure')).map(img=>({src:img.dataset.qaOriginalSrc||img.getAttribute('src')||'', w:img.naturalWidth, h:img.naturalHeight, fallback:img.dataset.figureFallback||''}))
    })""")
    if library['rows']<100: raise AssertionError('Exercise library rows missing')
    if not library['figures']: raise AssertionError('Exercise library detail figure missing')
    if not any(f['w']>0 and f['h']>0 for f in library['figures']): raise AssertionError('Exercise library figure did not load in DOM QA')

    await page.evaluate("() => window.showImageAdmin()")
    await page.wait_for_timeout(800)
    await inline_all_images(page)
    admin=await page.evaluate("""() => ({
      active:document.querySelector('.screen.active')?.id||'',
      rowCount:document.querySelectorAll('.next-image-admin-row').length,
      activeKey:document.querySelector('.next-image-admin-row.is-active')?.dataset.exerciseKey||'',
      previewSrc:document.querySelector('#imageAdminDetail .next-exercise-visual-figure')?.dataset.qaOriginalSrc || document.querySelector('#imageAdminDetail .next-exercise-visual-figure')?.getAttribute('src') || '',
      previewW:document.querySelector('#imageAdminDetail .next-exercise-visual-figure')?.naturalWidth || 0,
      poseCards:document.querySelectorAll('.next-pose-bank-card').length,
      poseLoaded:Array.from(document.querySelectorAll('.next-pose-bank-card img')).filter(img=>img.naturalWidth>0&&img.naturalHeight>0).length
    })""")
    if admin['active']!='imageAdminScreen': raise AssertionError('Image admin did not open')
    if admin['rowCount']<100: raise AssertionError('Image admin rows missing')
    if admin['previewW']<=0: raise AssertionError('Image admin preview figure did not load')
    if admin['poseCards']<70 or admin['poseLoaded']<70: raise AssertionError(f"Pose bank missing/unloaded: {admin}")
    await page.screenshot(path=str(OUT/'rc22_image_admin_assets_desktop_1440x1024.png'), full_page=False)
    return {'library':library,'admin':admin}

async def main():
    from playwright.async_api import async_playwright
    async with async_playwright() as p:
        browser=await p.chromium.launch(executable_path='/usr/bin/chromium', headless=True, args=['--no-sandbox','--disable-dev-shm-usage','--disable-gpu','--disable-gpu-sandbox','--use-gl=swiftshader','--enable-unsafe-swiftshader'])
        results={}
        for width,height,name in [(390,844,'mobile'),(820,1180,'ipad'),(1440,1024,'desktop')]:
            page=await browser.new_page()
            results[f'program_{name}']=await verify_program_click(page,width,height,name)
            await page.close()
        page=await browser.new_page()
        results['assets']=await verify_assets(page)
        await page.close()
        await browser.close()
    (OUT/'rc22_program_click_and_assets_metrics.json').write_text(json.dumps(results,indent=2,ensure_ascii=False),encoding='utf-8')
    print('RC22 delegated program click + asset QA OK')

if __name__=='__main__':
    asyncio.run(main())
