"""Local-only content, PDF, navigation and rendering verification."""
import hashlib
from html.parser import HTMLParser
import json
from pathlib import Path
import re
import subprocess
import xml.etree.ElementTree as ET

from PIL import Image, ImageDraw
from content import PAGES, QUICK_IDS, COMMANDS

RUN=Path(__file__).resolve().parent
OUT=Path('/workspace/architecture_docs/topics/contextra/20260930_field_operations_gamebook')
REPO=Path('/workspace/contextra-analytics-runtime-lifecycle-refactor')
FULL='01_contextra_field_gamebook_20260930'
QUICK='02_contextra_field_quick_20260930'

def command(args):return subprocess.check_output(args,text=True,timeout=90)
def squash(text):return re.sub(r'\s+','',text)

class PreText(HTMLParser):
    def __init__(self):super().__init__();self.pre=False;self.blocks=[]
    def handle_starttag(self,tag,attrs):
        if tag=='pre':self.pre=True;self.blocks.append('')
    def handle_endtag(self,tag):
        if tag=='pre':self.pre=False
    def handle_data(self,data):
        if self.pre:self.blocks[-1]+=data

result={'server_access':False,'runtime_changes':False,'new_dependencies':False,'pdfs':{},'commands':len(COMMANDS)}
for name,pages in [(FULL,PAGES),(QUICK,[p for p in PAGES if p['id'] in QUICK_IDS])]:
    path=OUT/(name+'.pdf')
    info=command(['pdfinfo',str(path)])
    assert int(re.search(r'Pages:\s+(\d+)',info)[1])==len(pages)
    assert 'Tagged:          yes' in info and '(A4)' in info and 'JavaScript:      no' in info
    fonts=command(['pdffonts',str(path)]).splitlines()[2:]
    assert fonts and all(re.search(r'yes\s+yes\s+yes\s+\d+\s+\d+\s*$',row) for row in fonts)
    raw=path.read_bytes()
    assert b'/Outlines' in raw and b'/Title' in raw
    assert b'/URI (file:' not in raw
    dests=command(['pdfinfo','-dests',str(path)])
    actual={m[2]:int(m[1]) for m in re.finditer(r'^\s*(\d+)\s+\[.*\]\s+"([^"]+)"',dests,re.M)}
    for i,p in enumerate(pages,1):assert actual.get(p['id'])==i,(name,p['id'],actual.get(p['id']))
    for dest in re.findall(rb'/Dest /([^\s>]+)',raw):assert dest.decode() in actual
    extracted=command(['pdftotext','-layout',str(path),'-'])
    text_pages=extracted.split('\f')
    assert len(text_pages)==len(pages)+1
    assert '\ufffd' not in extracted
    for i,p in enumerate(pages):
        assert squash(p['title']) in squash(text_pages[i]),(name,p['id'],'missing title')
        assert p['id'] in text_pages[i]
        parser=PreText();parser.feed(p['body'])
        for block in parser.blocks:
            assert squash(block) in squash(text_pages[i]),(name,p['id'],'command changed',block)
    assert not re.search(r'contextra\d{2}\b|-----BEGIN [^-]*PRIVATE KEY|eyJ[A-Za-z0-9_-]{20,}',extracted)
    bbox=command(['pdftotext','-bbox-layout',str(path),'-'])
    tree=ET.fromstring(bbox)
    ns={'x':'http://www.w3.org/1999/xhtml'}
    words=tree.findall('.//x:word',ns)
    for w in words:
        assert 0 <= float(w.attrib['xMin']) < float(w.attrib['xMax']) <= 595.5,w.attrib
        assert 0 <= float(w.attrib['yMin']) < float(w.attrib['yMax']) <= 842.5,w.attrib
    (RUN/(name+'.text.txt')).write_text(extracted)
    (RUN/(name+'.bbox.xml')).write_text(bbox)
    (RUN/(name+'.destinations.txt')).write_text(dests)
    prefix='page' if name==FULL else 'quick'
    subprocess.run(['pdftoppm','-scale-to','1400','-png',str(path),str(RUN/prefix)],check=True,timeout=100)
    images=sorted(RUN.glob(prefix+'-*.png'))
    assert len(images)==len(pages)
    for i in range(0,len(images),2):
        frames=[Image.open(q).convert('RGB') for q in images[i:i+2]]
        canvas=Image.new('RGB',(sum(im.width for im in frames)+30,max(im.height for im in frames)+30),'#bcc6ca')
        x=10
        for im in frames:canvas.paste(im,(x,15));x+=im.width+10
        canvas.save(RUN/f'{prefix}-review-{i+1:02d}.jpg',quality=90)
    result['pdfs'][name]={'pages':len(pages),'size_bytes':path.stat().st_size,'embedded_fonts':len(fonts),
        'unicode_mapping':True,'outlines':True,'named_destinations':len(actual),'commands_preserved':True,
        'bounded_pdf_words':len(words),'all_pages_rendered':True,'sha256':hashlib.sha256(raw).hexdigest()}

reference=(REPO/'docs/deployment/CT_FIELD_GAMEBOOK_GPT.ko.md').read_text()
for p in PAGES:assert f'## {p["id"]} {p["title"]}' in reference
assert not re.search(r'\b192\.168\.\d+\.\d+',reference)
assert not re.search(r'contextra\d{2}\b|-----BEGIN [^-]*PRIVATE KEY|eyJ[A-Za-z0-9_-]{20,}',reference)
for match in re.finditer(r'\]\(([^)#]+)(?:#[^)]*)?\)',reference):
    target=match[1]
    if '://' not in target:assert (REPO/'docs/deployment'/target).exists(),target
result['gpt_reference']={'sections':len(PAGES),'site_addresses_redacted':True,'local_source_links_valid':True}
(RUN/'verification.json').write_text(json.dumps(result,ensure_ascii=False,indent=2))
print(json.dumps(result,ensure_ascii=False,indent=2))
