"""Compile one authoritative manuscript to the static web edition and formal PDF source."""
from pathlib import Path
import json,re,subprocess,html,hashlib,shutil
from lxml import html as LH, etree
ROOT=Path(__file__).resolve().parents[1]; DIST=ROOT/'dist'; OUT=ROOT/'build'
OUT.mkdir(exist_ok=True);ORIGIN=(ROOT/'site-origin.txt').read_text().strip()
FILES=sorted((ROOT/'content').glob('*.md'));refs=json.loads((ROOT/'content/references.json').read_text());byref={r['id']:r for r in refs}
chapters=[];labs={}
for p in FILES:
 s=p.read_text();m=re.match(r'# (.*?) \{#([^ }]+).*?\}',s)
 assert m,p
 ch={'file':p,'title':m[1],'id':m[2],'number':int(p.name[:2]),'source':s};ch['url']='/chapters/'+ch['id']+'/'
 chapters.append(ch)
 for lm in re.finditer(r'::: \{\.lab #lab-([\w-]+)\}\s*\*\*Laboratory (L\d+) — (.*?)\.\*\*',s):
  key,number,title=lm.groups();labs[number]={'key':key,'number':number,'title':title,'chapter':ch['id'],'chapter_number':ch['number'],'url':ch['url']+'#lab-'+key}
assert len(labs)==26,(len(labs),labs.keys())
numberch={c['number']:c for c in chapters};bylab={l['key']:l for l in labs.values()}

def reference_md(web=False):
 lines=['# References {#references .unnumbered}','Sources were checked against the research boundary of 5 September 2026. Publication status and access limitations remain attached to each entry.']
 for r in refs:
  url=r['url'];title=r['title'].replace('[','\\[').replace(']','\\]')
  linked=f'[{title}]({url})' if url else f'*{title}*'
  lines.append(f"::: {{#ref-{r['id']} .reference}}\n**[{r['number']}]** {r['authors']} ({r['year']}). {linked}. {r['venue']}. **Status:** {r['status']}.\n:::")
 return '\n\n'.join(lines)

def prep(s,web=False):
 def cite(m):
  ids=re.findall(r'@([\w-]+)',m[0]);parts=[]
  for id in ids:
   r=byref[id];target=('/references/' if web else '')+'#ref-'+id
   parts.append(f"[{r['number']}]({target})")
  return '['+', '.join(parts)+']'
 s=re.sub(r'\[@[\w-]+(?:;\s*@[\w-]+)*\]',cite,s)
 s=re.sub(r'\bChapter (\d+)\b',lambda m:f"[Chapter {m[1]}]({numberch[int(m[1])]['url'] if web else '#'+numberch[int(m[1])]['id']})",s)
 s=re.sub(r'\bL\d{2}\b',lambda m:f"[{m[0]}]({ORIGIN+labs[m[0]]['url']})" if m[0] in labs else m[0],s)
 s=re.sub(r'\]\(figures/([^)]*)\.svg\)',lambda m:']('+('/figures/'+m[1]+'.svg' if web else str(DIST/'figures'/f'{m[1]}.pdf'))+')',s)
 return s

def pandoc(s,fmt,args=[]):
 p=subprocess.run(['pandoc','-f','markdown+fenced_divs+tex_math_dollars','-t',fmt,*args],input=s,text=True,capture_output=True,cwd=ROOT)
 if p.stderr:print(p.stderr)
 if "Could not convert TeX math" in p.stderr:raise RuntimeError("Unrendered mathematics")
 if p.returncode:raise RuntimeError(p.stderr)
 return p.stdout

def fragment(s):
 out=pandoc(s,'html5',['--mathml','--highlight-style=pygments'])
 root=LH.fragment_fromstring(out,create_parent='article')
 for div in root.xpath('.//div[contains(concat(" ",normalize-space(@class)," ")," solution ")]'):
  div.tag='details';su=etree.Element('summary');su.text='Show solution / guidance';div.insert(0,su)
 for div in root.xpath('.//div[contains(concat(" ",normalize-space(@class)," ")," lab ")]'):
  key=div.attrib['id'][4:];mount=etree.SubElement(div,'div',{'class':'lab-workspace','data-lab':key});mount.text=''
  no=etree.SubElement(div,'noscript');no.text='Enable JavaScript to use this laboratory. The explanation and all static solutions remain readable.'
 for table in root.xpath('.//table'):
  parent=table.getparent();i=parent.index(table);wrap=etree.Element('div',{'class':'table-scroll'});parent.remove(table);wrap.append(table);parent.insert(i,wrap)
 for img in root.xpath('.//img'):img.set('loading','lazy');img.set('decoding','async')
 return root

PARTS={1:'I · Foundations',9:'II · Algorithms',16:'III · Reliable systems',21:'IV · Platforms and practice',26:'Appendices'}
def sidebar(current):
 nav=[]
 for c in chapters:
  if c['number'] in PARTS:nav.append('<p class="part-label">'+PARTS[c['number']]+'</p>')
  n=c['number'];badge=f'{n:02}' if 1<=n<=25 else 'A' if n==26 else 'B' if n==27 else '↗'
  nav.append('<a class="chapter-link'+(' active' if c['id']==current else '')+'" href="'+c['url']+'" data-chapter="'+c['id']+'"'+(' aria-current="page"' if c['id']==current else '')+'><span>'+badge+'</span><span>'+html.escape(c['title'].replace('Appendix A — ','').replace('Appendix B — ',''))+'</span></a>')
 return ''.join(nav)

def page(title,body,current='',localtoc='',nextnav='',eyebrow='THE INTERACTIVE TEXTBOOK'):
 return '''<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="theme-color" content="#17324f"><meta name="description" content="A complete introductory quantum computing course with mathematical derivations, exercises, and 26 working browser laboratories. Research cutoff: 5 September 2026."><title>'''+html.escape(title)+''' · Quantum Computing 2026</title><link rel="icon" href="/favicon.svg" type="image/svg+xml"><link rel="stylesheet" href="/style.css"><script type="module" src="/app.js"></script></head><body data-page="'''+current+'''"><a class="skip" href="#main">Skip to content</a><header class="topbar"><button id="menu-toggle" aria-expanded="false" aria-controls="sidebar" aria-label="Open chapter navigation">☰</button><a class="brand" href="/"><span class="brand-mark">ψ</span><span>Quantum Computing <b>2026</b></span></a><nav aria-label="Resources"><a href="/labs/">Laboratories</a><a href="/references/">References</a><a class="pdf-link" href="/downloads/Introduction-to-Quantum-Computing-2026.pdf">Download PDF <span aria-hidden="true">↗</span></a></nav></header><aside class="sidebar" id="sidebar"><div class="search"><label for="book-search">Search the textbook</label><input id="book-search" type="search" placeholder="Try relative phase" autocomplete="off"><div id="search-results" hidden aria-live="polite"></div></div><nav aria-label="Chapters">'''+sidebar(current)+'''<a class="chapter-link" href="/references/"><span>↗</span><span>Academic references</span></a></nav><div class="progress-box"><label for="reading-progress">Your reading progress</label><progress id="reading-progress" value="0" max="25"></progress><span id="progress-label">0 of 25 chapters</span><button id="reset-progress" class="text-button">Reset progress</button><p>Saved on this device.</p></div></aside><main id="main"><div class="reading-layout"><article class="book-page"><p class="eyebrow">'''+eyebrow+'''</p>'''+body+nextnav+'''</article>'''+localtoc+'''</div><footer class="site-footer"><span>Research cutoff · 5 September 2026</span><span>One manuscript · Two editions · 26 laboratories</span><a href="/chapters/reading/#provenance-and-independent-authorship">About this work</a><a href="/downloads/">Files and reproducibility</a></footer></main></body></html>'''

def write(route,text):
 p=DIST/route/'index.html';p.parent.mkdir(parents=True,exist_ok=True);p.write_text(text)
search=[];manifest=[]
for i,c in enumerate(chapters):
 tree=fragment(prep(c['source'],True))
 if c['number']>0:
  prefix=str(c['number']) if c['number']<=25 else 'A' if c['number']==26 else 'B';section=0;subsection=0
  for h in tree.xpath('.//h2 | .//h3'):
   if h.tag=='h2':section+=1;subsection=0;label=f'{prefix}.{section}'
   else:subsection+=1;label=f'{prefix}.{section}.{subsection}'
   h.text=label+'  '+(h.text or '')
  for j,cap in enumerate(tree.xpath('.//figcaption'),1):cap.text=f'Figure {prefix}.{j}. '+(cap.text or '')
 serialized=''.join(LH.tostring(x,encoding='unicode') for x in tree)
 h2=tree.xpath('.//h2');toc='<aside class="on-this-page"><p>IN THIS CHAPTER</p><nav aria-label="Chapter sections">'+''.join('<a href="#'+x.attrib['id']+'">'+html.escape(x.text_content())+'</a>' for x in h2)+'</nav></aside>'
 nextnav='<nav class="chapter-pager" aria-label="Adjacent chapters">'
 for j,label in [(i-1,'Previous'),(i+1,'Next')]:
  if 0<=j<len(chapters):d=chapters[j];nextnav+='<a href="'+d['url']+'"><small>'+label+'</small><span>'+html.escape(d['title'])+'</span></a>'
 nextnav+='</nav>'
 if 1<=c['number']<=25:nextnav='<button class="complete-chapter" data-complete="'+c['id']+'" aria-pressed="false">Mark chapter complete</button>'+nextnav
 eyebrow='CHAPTER '+f"{c['number']:02}" if 1<=c['number']<=25 else 'READING GUIDE' if c['number']==0 else 'REFERENCE TOOLKIT'
 write('chapters/'+c['id'],page(c['title'],serialized,c['id'],toc,nextnav,eyebrow))
 active={'title':c['title'],'chapter':c['title'],'url':c['url'],'text':''}
 for el in tree:
  if el.tag=='h2':
   if active['text']:search.append(active)
   active={'title':el.text_content(),'chapter':c['title'],'url':c['url']+'#'+el.get('id'),'text':''}
  if el.tag not in ('h1','h2'):active['text']+=' '+el.text_content()
 if active['text']:search.append(active)
 manifest.append({'id':c['id'],'number':c['number'],'title':c['title'],'url':c['url'],'sha256':hashlib.sha256(c['source'].encode()).hexdigest(),'exercises':len(tree.xpath('.//div[contains(concat(" ",@class," ")," exercise ")]')),'solutions':len(tree.xpath('.//details'))})

body='''<h1 class="home-title">Introduction to<br>Quantum Computing <span>2026</span></h1><p class="home-deck">From the first complex amplitude to reliable quantum computation.</p><p class="home-intro">A mathematical course you can read, question, and experiment with. Begin with ordinary algebra; build toward quantum information, algorithms, noise, and fault tolerance.</p><div class="home-actions"><a class="primary-link" href="/chapters/information/">Begin Chapter 1 →</a><a class="secondary-link" href="/chapters/reading/">How to use this book</a><a id="resume-reading" hidden>Resume reading →</a></div><div class="course-facts"><div><strong>25</strong><span>Chapters</span></div><div><strong>26</strong><span>Working laboratories</span></div><div><strong>120</strong><span>Exercises with solutions</span></div><div><strong>6</strong><span>Integrated projects</span></div></div><div class="featured-experiment"><div><p class="eyebrow">START WITH A QUESTION</p><h2>One state. Three measurement bases.</h2><p>For (2|0⟩ + 3i|1⟩)/√13, the Z, X, and Y bases reveal different probabilities. Change the amplitudes, sample measurements, and unfold every inner product.</p><a href="/chapters/measurement/#lab-basis">Open the projection workbench →</a></div><img src="/figures/bloch.svg" alt="Bloch vector for the recurring example, with coordinates 0, 12/13, minus 5/13."></div><h2>A continuous course</h2><p>Each chapter has the same explanations, examples, notation, and exercises in the PDF and on this site. Laboratory links take you directly from the page to the experiment.</p><div class="curriculum">'''
for lo,hi,title,desc in [(1,8,'Build the foundations','Probability, amplitudes, measurement, circuits, entanglement, and open systems.'),(9,15,'Understand the algorithms','Oracle interference, period finding, Fourier methods, Hamiltonian simulation, and variational techniques.'),(16,20,'Make computation reliable','Classical simulation, stabilizer codes, decoding, logical operations, and resource budgets.'),(21,25,'Connect theory to practice','Physical platforms, quantum networks, reproducible software, 2026 evidence, and integrated projects.')]:
 body+='<section><p class="eyebrow">CHAPTERS '+str(lo)+'–'+str(hi)+'</p><h3>'+title+'</h3><p>'+desc+'</p><ol start="'+str(lo)+'">'+''.join('<li><a href="'+c['url']+'">'+html.escape(c['title'])+'</a></li>' for c in chapters if lo<=c['number']<=hi)+'</ol></section>'
body+='</div><div class="evidence-note"><h2>A dated view of a changing field</h2><p>Research cutoff: <strong>5 September 2026</strong>. Experiments, theory, preprints, and company claims are identified separately. Hardware counts and resource estimates keep their assumptions and scope.</p><a href="/chapters/evidence/">Read the scientific snapshot →</a></div>'
write('',page('Introduction to Quantum Computing',body))
refhtml=''.join(LH.tostring(x,encoding='unicode') for x in fragment(reference_md(True)))
write('references',page('References',refhtml,'references'))
labbody='<h1>The quantum laboratory</h1><p>Choose an experiment or open it in its chapter. Every panel calculates from your inputs. These are classical teaching simulations with their model limits stated beside the controls.</p><div class="lab-directory">'
for l in labs.values():labbody+='<a href="'+l['url']+'"><span class="lab-number">'+l['number']+'</span><div><h2>'+html.escape(l['title'])+'</h2><p>Chapter '+str(l['chapter_number'])+' · Open experiment →</p></div></a>'
labbody+='</div>';write('labs',page('Laboratory directory',labbody,'labs'))
write('downloads',page('Files and reproducibility','''<h1>Files and reproducibility</h1><p>The formal PDF and interactive chapters are compiled from one manuscript. These files let you study offline and reproduce the calculations.</p><ul class="download-list"><li><a href="/downloads/Introduction-to-Quantum-Computing-2026.pdf">Complete textbook PDF</a><p>All chapters, diagrams, exercises, solutions, references, and links to the laboratories.</p></li><li><a href="/downloads/qiskit_examples.py" download>Executable Qiskit examples</a><p>Verified with Qiskit 2.5.2; includes basis measurements, wire ordering, primitives, and a Kraus channel.</p></li><li><a href="/downloads/requirements.txt" download>Pinned Python requirements</a><p>Install in a separate Python environment as described in Chapter 23.</p></li><li><a href="/downloads/quantum-computing-2026-source.zip">Book and laboratory source archive</a><p>Manuscript, reference data, original figures, browser models, validation checks, and build instructions.</p></li><li><a href="/downloads/validation-report.json">Validation record</a><p>Reproducible checks, environment versions, and explicit limits of the verification.</p></li></ul>''','downloads'))
write('404',page('Page not found','<h1>That page is not in this edition.</h1><p>Use the chapter navigation or <a href="/">return to the textbook contents</a>.</p>'))
(DIST/'404.html').write_text((DIST/'404/index.html').read_text())
(DIST/'search-index.json').write_text(json.dumps(search,ensure_ascii=False))
(DIST/'book-manifest.json').write_text(json.dumps({'cutoff':'2026-09-05','edition':'1.0','chapters':manifest,'labs':list(labs.values()),'references':len(refs)},indent=2))

pdf=[]
for c in chapters:
 if c['number']==0:pdf.append('```{=latex}\n\\setcounter{secnumdepth}{-1}\n```')
 if c['number']==1:pdf.append('```{=latex}\n\\mainmatter\n\\setcounter{secnumdepth}{2}\n```')
 if c['number']==26:pdf.append('```{=latex}\n\\appendix\n```')
 s=c['source']
 if c['number']>=26:s=re.sub(r'^# Appendix [AB] — (.*?) \{#([^ }]+) \.unnumbered\}',r'# \1 {#\2}',s)
 pdf.append(prep(s))
pdf.append(reference_md());manuscript='\n\n'.join(pdf);(OUT/'book.md').write_text(manuscript)
args=['--standalone','--top-level-division=chapter','--number-sections','--toc','--toc-depth=1','--lua-filter='+str(ROOT/'scripts/pdf.lua'),'-V','documentclass=book','-V','classoption=oneside,openany','-V','fontsize=11pt','-V','mainfont=DejaVu Serif','-V','sansfont=DejaVu Sans','-V','monofont=DejaVu Sans Mono','-V','mathfont=Latin Modern Math','-V','geometry:paperwidth=7.5in,paperheight=10in,inner=.78in,outer=.67in,top=.78in,bottom=.72in','-V','colorlinks=true','-V','linkcolor=bookblue','-V','urlcolor=bookblue','--include-in-header='+str(ROOT/'scripts/book-header.tex'),'--include-before-body='+str(ROOT/'scripts/book-cover.tex')]
tex=pandoc(manuscript,'latex',args).replace('\\mainmatter\n','',1).replace('\\hypersetup{linkcolor=}','\\hypersetup{linkcolor=bookblue}')
(OUT/'book.tex').write_text(tex)
print(json.dumps({'chapters':len(chapters),'exercises':sum(x['exercises'] for x in manifest),'solutions':sum(x['solutions'] for x in manifest),'labs':len(labs),'references':len(refs),'words':sum(len(c['source'].split()) for c in chapters)},indent=2))
