#!/usr/bin/env python3
from __future__ import annotations
import asyncio, importlib.util, json
from pathlib import Path
ROOT=Path(__file__).resolve().parents[1]
OUT=ROOT/'qa_screenshots'; OUT.mkdir(exist_ok=True)
# Reuse RC18 inline fixture so local assets/JSON are deterministic.
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)

async def boot(page, width=390, height=844):
    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.showImageAdmin && window.NEXTPoseMotionService", timeout=7000)
    await page.evaluate("() => { try{localStorage.clear();sessionStorage.clear();}catch(e){} }")
    await page.wait_for_timeout(250)

async def test_mobile_menu_program(page):
    await boot(page,390,844)
    await page.locator('#appNavToggle').click()
    await page.wait_for_timeout(150)
    before=await page.evaluate("""() => ({navOpen:document.querySelector('.app-topbar')?.classList.contains('nav-open'), hit:document.elementFromPoint(195,258)?.closest?.('.app-tab')?.dataset?.target||''})""")
    await page.locator('button.app-tab[data-target="programScreen"]').click()
    await page.wait_for_timeout(400)
    after=await page.evaluate("""() => ({
      active:document.querySelector('.screen.active')?.id||'',
      navOpen:document.querySelector('.app-topbar')?.classList.contains('nav-open')||false,
      cards:document.querySelectorAll('#programCatalog .next-program-card').length,
      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())
    })""")
    if after['active']!='programScreen': raise AssertionError('Program tab did not open programScreen')
    if after['navOpen']: raise AssertionError('Mobile app nav remained open over program screen')
    if after['cards']<3: raise AssertionError('Program catalog did not render normal programs')
    # Click actual Välj program button for Stage 2, verify temporary profile keeps the selection.
    await page.locator("button[onclick*=\"chooseProgram('program-stage-2')\"]").last.click()
    await page.wait_for_timeout(500)
    choice=await page.evaluate("""() => ({
      active:document.querySelector('.screen.active')?.id||'',
      navOpen:document.querySelector('.app-topbar')?.classList.contains('nav-open')||false,
      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())
    })""")
    if choice['activeProgram']!='program-stage-2': raise AssertionError('Choosing program-stage-2 did not persist in temporary profile')
    if not any('9–12' in x or 'Stage 2' in x for x in choice['selected']): raise AssertionError('Choosing program-stage-2 did not select the Stage 2 card')
    await page.screenshot(path=str(OUT/'rc21_program_normal_flow_mobile_390x844.png'), full_page=False)
    return {'before':before,'after':after,'choice':choice}

async def test_image_admin_defaults(page):
    await boot(page,1440,1024)
    await page.evaluate("() => window.showImageAdmin()")
    await page.wait_for_function("() => document.querySelectorAll('.next-image-admin-row').length > 10", timeout=7000)
    await page.wait_for_timeout(600)
    await rc18qa.inline_images(page)
    metrics=await page.evaluate("""() => {
      const activeRow=document.querySelector('.next-image-admin-row.is-active');
      const fig=document.querySelector('#imageAdminDetail .next-exercise-visual-figure');
      return {
        active:document.querySelector('.screen.active')?.id||'',
        rowCount:document.querySelectorAll('.next-image-admin-row').length,
        activeKey:activeRow?.dataset.exerciseKey||'',
        activeStatus:activeRow?.querySelector('.next-image-status')?.textContent.trim()||'',
        previewFigure:fig?.dataset.qaOriginalSrc || fig?.getAttribute('src') || '',
        poseBankCount:document.querySelectorAll('.next-pose-bank-card').length,
        poseImgCount:document.querySelectorAll('.next-pose-bank-card img').length,
        detailText:document.querySelector('#imageAdminDetail')?.textContent.slice(0,250)||''
      }
    }""")
    if metrics['active']!='imageAdminScreen': raise AssertionError('Bildadmin did not open')
    if metrics['rowCount']<100: raise AssertionError('Bildadmin row list is unexpectedly small')
    if metrics['activeStatus']!='Godkänd': raise AssertionError('Bildadmin did not default to an approved figure')
    if '/figures/' not in metrics['previewFigure'] and 'assets/img/exercises/figures/' not in metrics['previewFigure']:
        raise AssertionError('Bildadmin preview figure path is not an exercise figure')
    if metrics['poseBankCount']<70 or metrics['poseImgCount']<70:
        raise AssertionError('Pose bank figures are missing from image admin')
    await page.screenshot(path=str(OUT/'rc21_image_admin_default_approved_desktop_1440x1024.png'), full_page=False)
    return metrics

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'])
        p1=await browser.new_page(); m1=await test_mobile_menu_program(p1); await p1.close()
        p2=await browser.new_page(); m2=await test_image_admin_defaults(p2); await p2.close()
        await browser.close()
    (OUT/'rc21_normal_flow_dom_metrics.json').write_text(json.dumps({'program':m1,'imageAdmin':m2},indent=2,ensure_ascii=False),encoding='utf-8')
    print('RC21 normal flow real-DOM QA OK')

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