#!/usr/bin/env python3
from __future__ import annotations

import argparse
import base64
import json
import os
from pathlib import Path
import subprocess
import time
import urllib.request


def main() -> int:
    ap = argparse.ArgumentParser(description='Firefox no-JS/semantic smoke for canonical pdf2html output.')
    ap.add_argument('url')
    ap.add_argument('output', type=Path)
    ap.add_argument('--geckodriver', default=os.environ.get('GECKODRIVER', '/tmp/geckodriver'))
    ap.add_argument('--port', type=int, default=4457)
    args = ap.parse_args()
    process = subprocess.Popen([args.geckodriver, '--port', str(args.port), '--log', 'error'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

    def request(method: str, path: str, payload: dict | None = None, timeout: float = 60) -> dict:
        data = None if payload is None else json.dumps(payload).encode()
        req = urllib.request.Request(
            f'http://127.0.0.1:{args.port}{path}',
            data=data,
            method=method,
            headers={'Content-Type': 'application/json'},
        )
        with urllib.request.urlopen(req, timeout=timeout) as response:
            return json.load(response)

    metadata_url = args.url.rsplit('/', 1)[0] + '/metadata.json'
    with urllib.request.urlopen(metadata_url, timeout=60) as response:
        canonical_metadata = json.load(response)
    expected_figures = sum(len(page.get('figures', [])) for page in canonical_metadata['pages'])
    session_id = None
    try:
        for _ in range(60):
            try:
                request('GET', '/status', timeout=1)
                break
            except Exception:
                time.sleep(0.1)
        created = request('POST', '/session', {
            'capabilities': {'alwaysMatch': {
                'browserName': 'firefox',
                'moz:firefoxOptions': {'args': ['-headless'], 'prefs': {
                    'browser.cache.disk.enable': False,
                    'browser.cache.memory.enable': False,
                    'browser.shell.checkDefaultBrowser': False,
                }},
            }},
        })['value']
        session_id = created['sessionId']
        request('POST', f'/session/{session_id}/window/rect', {'width': 1400, 'height': 1000})
        request('POST', f'/session/{session_id}/url', {'url': args.url}, timeout=120)
        script = r'''return (async()=>{
          await document.fonts.ready;
          const pages=[...document.querySelectorAll('.pf')],first=pages[0],last=pages[pages.length-1];
          first.scrollIntoView();await new Promise(resolve=>setTimeout(resolve,200));
          last.scrollIntoView();await new Promise(resolve=>setTimeout(resolve,600));
          const p4=document.querySelector('[data-page-no="4"]');
          const paragraph=[...p4.querySelectorAll('p')].find(node=>node.textContent.includes('We take pride in our role as a center of excellence'));
          const second=[...p4.querySelectorAll('p')].find(node=>node.textContent.includes('Danziger Kenya is known for its high-quality URC'));
          const list=[...p4.querySelectorAll('ul')].find(node=>node.textContent.includes('End-to-end production supply chain'));
          const ids=[...document.querySelectorAll('[id]')].map(node=>node.id),duplicates=[...new Set(ids.filter((id,index,all)=>all.indexOf(id)!==index))];
          const lastRect=last.getBoundingClientRect();
          const html=document.documentElement.innerHTML;
          return {
            pages:pages.length,visualLines:document.querySelectorAll('.vl').length,rendererTextDivs:document.querySelectorAll('div.t').length,
            paragraphs:document.querySelectorAll('p').length,headings:document.querySelectorAll('h1,h2,h3,h4,h5,h6').length,
            lists:document.querySelectorAll('ul,ol').length,listItems:document.querySelectorAll('li').length,tables:document.querySelectorAll('table').length,
            rows:document.querySelectorAll('tr').length,cells:document.querySelectorAll('th,td').length,figures:document.querySelectorAll('.semantic-figure').length,
            residuals:document.querySelectorAll('.residual-artwork').length,scripts:document.scripts.length,title:document.title,lang:document.documentElement.lang,
            description:document.querySelector('meta[name="description"]')?.content||'',duplicateIds:duplicates,
            p4Lines:paragraph?.querySelectorAll('.vl').length||0,p4SecondLines:second?.querySelectorAll('.vl').length||0,p4ListItems:list?.querySelectorAll('li').length||0,
            p4Order:{paragraph:html.indexOf('We take pride in our role as a center of excellence'),second:html.indexOf('Danziger Kenya is known for its high-quality URC'),list:html.indexOf('End-to-end production supply chain')},
            phrase:window.find('flowers. Our State-of-the-Art facilities use cutting-'),
            lastRect:{w:lastRect.width,h:lastRect.height},contentVisibility:getComputedStyle(first).contentVisibility,
            remoteResources:performance.getEntriesByType('resource').map(row=>row.name).filter(name=>!name.startsWith(location.origin+'/')),
            residualPointer:[...document.querySelectorAll('.residual-artwork')].filter(node=>getComputedStyle(node).pointerEvents!=='none').length,
            loadedLast:[...last.querySelectorAll('img')].every(img=>img.complete&&img.naturalWidth>0)
          };
        })()'''
        value = request('POST', f'/session/{session_id}/execute/sync', {'script': script, 'args': []}, timeout=90)['value']
        page4 = request('POST', f'/session/{session_id}/element', {'using': 'css selector', 'value': '[data-page-no="4"]'})['value']
        element_id = page4['element-6066-11e4-a52e-4f735466cecf']
        png = base64.b64decode(request('GET', f'/session/{session_id}/element/{element_id}/screenshot', timeout=60)['value'])
        screenshot_path = args.output.with_suffix('.page4.png')
        screenshot_path.write_bytes(png)
        value['page4Screenshot'] = str(screenshot_path)
        value['page4ScreenshotBytes'] = len(png)
        printed = base64.b64decode(request('POST', f'/session/{session_id}/print', {'background': True, 'pageRanges': ['4'], 'shrinkToFit': True}, timeout=180)['value'])
        print_path = args.output.with_suffix('.page4.pdf')
        print_path.write_bytes(printed)
        pdfinfo = subprocess.run(['pdfinfo', str(print_path)], check=True, capture_output=True, text=True).stdout
        print_pages = next((line.split(':', 1)[1].strip() for line in pdfinfo.splitlines() if line.startswith('Pages:')), '')
        print_size = next((line.split(':', 1)[1].strip() for line in pdfinfo.splitlines() if line.startswith('Page size:')), '')
        value['page4Print'] = str(print_path)
        value['page4PrintBytes'] = len(printed)
        value['page4PrintPages'] = int(print_pages) if print_pages.isdigit() else -1
        value['page4PrintSize'] = print_size
        value['firefox'] = created.get('capabilities', {}).get('browserVersion', '')
        value['platform'] = created.get('capabilities', {}).get('platformName', '')
        args.output.write_text(json.dumps(value, indent=2) + '\n', encoding='utf-8')
        print(json.dumps(value, sort_keys=True))
        expected = 'ANNUALS & PERENNIALS CATALOG EU & ROW — 2026-28'
        ok = (
            value['pages'] == 176 and value['visualLines'] == 7128 and value['rendererTextDivs'] == 0 and
            value['figures'] == expected_figures and value['residuals'] == 176 and value['scripts'] == 0 and
            value['title'] == expected and value['lang'] == 'en' and value['description'].startswith(expected + '. ') and
            not value['duplicateIds'] and value['p4Lines'] == 5 and value['p4SecondLines'] > 0 and value['p4ListItems'] == 7 and
            value['p4Order']['paragraph'] >= 0 and value['p4Order']['second'] > value['p4Order']['paragraph'] and value['p4Order']['list'] > value['p4Order']['second'] and
            value['phrase'] and value['lastRect'] == {'w': 612, 'h': 792} and value['contentVisibility'] == 'auto' and
            not value['remoteResources'] and value['residualPointer'] == 0 and value['loadedLast'] and len(png) > 10000 and value['page4PrintPages'] == 1 and value['page4PrintSize'].startswith('612 x 792 pts') and value['page4PrintBytes'] > 100000
        )
        return 0 if ok else 2
    finally:
        if session_id is not None:
            try:
                request('DELETE', f'/session/{session_id}', {})
            except Exception:
                pass
        process.terminate()
        try:
            process.wait(timeout=5)
        except subprocess.TimeoutExpired:
            process.kill()


if __name__ == '__main__':
    raise SystemExit(main())
