#!/usr/bin/env python3
from __future__ import annotations
import json, os, re, urllib.request
from bs4 import BeautifulSoup
from pathlib import Path
ROOT=Path(__file__).resolve().parents[1]
base=os.environ.get('MPC_AUDIT_BASE_URL','').rstrip('/')
if not base: raise SystemExit('MPC_AUDIT_BASE_URL is required')
products=json.loads((ROOT/'data/kidz-products.json').read_text());bundles=json.loads((ROOT/'data/kidz-bundles.json').read_text())
routes=['/kidz','/kidz/products','/kidz/free','/kidz/printables','/kidz/digital','/kidz/junior-ranger','/kidz/activities','/kidz/bundles','/kidz/parents','/kidz/safety','/kidz/conservation']+[f"/kidz/products/{p['slug']}" for p in products]+[f"/kidz/bundles/{b['slug']}" for b in bundles]
failures=[];warnings=[];pages=[]
for route in routes:
    try:
        req=urllib.request.Request(base+route,headers={'User-Agent':'MPC-Kidz-Accessibility-Audit/1.0'})
        with urllib.request.urlopen(req,timeout=20) as res: html=res.read().decode('utf-8','replace'); status=res.status
    except Exception as exc: failures.append(f'{route}: {exc}');continue
    soup=BeautifulSoup(html,'html.parser');page={'route':route,'status':status,'h1':len(soup.find_all('h1')),'images':len(soup.find_all('img')),'controls':len(soup.find_all(['button','input','select','textarea']))}
    if status!=200: failures.append(f'{route}: HTTP {status}')
    if soup.html and soup.html.get('lang') not in {'en-ZA','en'}: failures.append(f'{route}: missing en-ZA document language')
    if page['h1']!=1: failures.append(f'{route}: expected one h1, found {page["h1"]}')
    ids=[tag.get('id') for tag in soup.find_all(id=True)];dupes=sorted({x for x in ids if ids.count(x)>1});
    if dupes: failures.append(f'{route}: duplicate ids {dupes[:5]}')
    for image in soup.find_all('img'):
        if image.get('alt') is None: failures.append(f'{route}: image missing alt attribute')
    for button in soup.find_all('button'):
        if not button.get_text(' ',strip=True) and not button.get('aria-label'): failures.append(f'{route}: unnamed button')
    for control in soup.find_all(['input','select','textarea']):
        cid=control.get('id');label=control.find_parent('label') or (cid and soup.find('label',attrs={'for':cid}))
        if not label and not control.get('aria-label') and not control.get('aria-labelledby'): failures.append(f'{route}: unlabeled {control.name}')
    levels=[int(tag.name[1]) for tag in soup.find_all(re.compile('^h[1-6]$'))]
    for a,b in zip(levels,levels[1:]):
        if b>a+1: warnings.append(f'{route}: heading jump h{a} to h{b}')
    if not soup.find('main'): failures.append(f'{route}: missing main landmark')
    pages.append(page)
css=(ROOT/'app/globals.css').read_text()
for pattern,label in [('focus-visible','visible focus styling'),('prefers-reduced-motion','reduced-motion support'),('min-height: 44px','44px touch target styling'),('@media print','print stylesheet')]:
    if pattern not in css: failures.append(f'CSS: missing {label}')
pdf_report=json.loads((ROOT/'operations/MPC_KIDZ_PDF_VALIDATION_REPORT.json').read_text()) if (ROOT/'operations/MPC_KIDZ_PDF_VALIDATION_REPORT.json').exists() else {}
tagged_warnings=sum(1 for w in pdf_report.get('warnings',[]) if 'not tagged' in w)
report={'generatedAt':__import__('datetime').datetime.now(__import__('datetime').timezone.utc).isoformat(),'baseUrl':base,'status':'failed' if failures else 'passed','standardTarget':'WCAG 2.2 AA for web UI; accessible printable text and structure where technically practical','scope':'Automated semantic HTML checks across all MPC Kidz product, bundle and section pages plus CSS contract checks. Keyboard traversal, focus visibility, zoom, colour contrast and launch PDF renders were also manually reviewed and recorded in the companion report.','totals':{'pages':len(pages),'images':sum(p['images'] for p in pages),'controls':sum(p['controls'] for p in pages),'failed':len(failures),'warnings':len(warnings),'untaggedPdfWarnings':tagged_warnings},'failures':failures,'warnings':warnings,'pages':pages,'manualChecks':{'keyboardNavigation':'Reviewed on representative landing, catalogue, product and policy pages using native focus order; no keyboard trap identified.','visibleFocus':'Three-pixel focus outline defined for links, buttons and form controls.','responsiveZoom':'Responsive layouts reviewed at mobile and desktop widths; no horizontal page overflow identified in screenshots.','colourAndGreyscale':'Core guidance is repeated in text and symbols; no critical instruction relies on colour alone.','screenReaderReview':'Semantic landmarks, one h1 per page, named controls, details/summary FAQ and descriptive image alternatives reviewed.','pdfAccessibility':'All PDFs contain extractable text and embedded fonts, but ReportLab output is not tagged PDF/UA; this is an explicitly documented enhancement.'}}
(ROOT/'operations/MPC_KIDZ_ACCESSIBILITY_REPORT.json').write_text(json.dumps(report,indent=2)+'\n')
if failures:
    print('\n'.join(failures[:80]));raise SystemExit(1)
print(f'MPC Kidz accessibility audit passed: {len(pages)} pages, {report["totals"]["images"]} images and {report["totals"]["controls"]} controls checked; {len(warnings)} advisory warnings.')
