"""Reproduce known 2012 book cipher; audit bounded direct-transfer candidates.

No full poem text is bundled. Fetch the pinned source with --fetch if absent.
Uses Python standard library only. Coordinates are from the archived Cicada
message; expected output is the historical archive's answer, not a new solution.
"""
from pathlib import Path
import hashlib, html, json, re, sys, urllib.request

ROOT = Path(__file__).resolve().parent
URL = 'https://web.archive.org/web/20111229194935id_/http://www.williamgibsonbooks.com/source/agrippa.asp'
CODES = [(1,5),(152,24),6,(14,13),(7,36),(12,10),(7,16),(24,3),(271,22),
         (10,7),(13,28),(12,7),(86,17),(93,14),6,(16,7),(96,4),(19,13),
         (47,2),(71,22),(75,9),(77,4)]
EXPECTED = 'sq6wmgv2zcsrix6t.onion'


def extract_lines(source):
    body = source[source.index('I hesitated<br>'):]
    body = body[:body.index('in the mechanism.') + len('in the mechanism.')]
    # HTML BR is a verse line break; source-code newlines are not extra lines.
    return [html.unescape(re.sub('<[^>]+>', '', part)).strip()
            for part in re.split(r'<br\s*/?>', body, flags=re.I)]


def lookup(lines, row, col):
    return lines[row-1][col-1] if 1 <= row <= len(lines) and 1 <= col <= len(lines[row-1]) else None


def decode(lines):
    return ''.join('6' if code == 6 else lookup(lines, *code) or '?' for code in CODES)


def main():
    config = json.loads((ROOT/'agrippa_book_sources.json').read_text())
    path = ROOT/'agrippa_author_archive.html'
    if '--fetch' in sys.argv:
        data = urllib.request.urlopen(URL, timeout=45).read()
        assert hashlib.sha256(data).hexdigest() == config['author_html_sha256'], 'Archive bytes changed'
        path.write_bytes(data)
    data = path.read_bytes()
    assert hashlib.sha256(data).hexdigest() == config['author_html_sha256']
    lines = extract_lines(data.decode())
    actual = decode(lines)
    assert actual == EXPECTED
    controls = []
    for keep_blank in (True, False):
        for keep_space in (True, False):
            variant = [line for line in lines if keep_blank or line]
            if not keep_space:
                variant = [re.sub(r'\s', '', line) for line in variant]
            controls.append({'keep_blank_lines':keep_blank, 'keep_internal_spaces':keep_space,
                             'output':decode(variant), 'exact_match':decode(variant)==EXPECTED})
    raw = (ROOT/'liber-primus__transcription--master.txt').read_text()
    pages = raw[raw.index('ᚱ-'):].split('%')[:73][15:]
    unresolved = '%'.join(pages[:56])
    coordinate_matches = re.findall(r'(?<!\d)\d+:\d+(?!\d)', unresolved)
    # Two explicitly speculative transfers, fixed before inspecting output.
    # Do not wrap out-of-range coordinates or silently skip failed lookups.
    cuneiform = [(17,13),(55,1)]
    blob = (ROOT/'base60_verified.bin').read_bytes()
    pairs = list(zip(blob[::2], blob[1::2]))
    byte_validity = [lookup(lines,*pair) is not None for pair in pairs]
    result = {
        'source':URL, 'source_sha256':hashlib.sha256(data).hexdigest(),
        'poem_lines':len(lines), 'line_origin':'first verse line, excluding title/byline',
        'rule':'One-based line:character; preserve blank lines, internal spaces and punctuation; arithmetic clues emit 6',
        'known_solution':actual, 'exact_recovery':True, 'format_controls':controls,
        'mapping':[{'coordinate':list(c),'character':lookup(lines,*c)} if c!=6 else {'arithmetic':'2*3','character':'6'} for c in CODES],
        'unresolved_transcription':{'lp2_pages':'0 through 55','literal_decimal_colon_coordinate_count':len(coordinate_matches)},
        'speculative_cuneiform_coordinates':{'pairs':cuneiform,'output':''.join(lookup(lines,*p) or '?' for p in cuneiform),'interpretation':'Only two characters; no validation or decoding claim'},
        'speculative_adjacent_byte_coordinates':{'pair_count':len(pairs),'valid_pairs':sum(byte_validity),'invalid_pairs':len(pairs)-sum(byte_validity),'interpretation':'Direct complete-message interpretation fails if any coordinate is invalid'},
        'limits':['Historical reproduction, not a new solution or proof of source uniqueness.',
                  'No independent PGP verification performed.',
                  'Literal-coordinate audit covers transcription, not every illustration or hidden layer.',
                  'Speculative transfers are not instructed by a discovered clue; no alternate pairing, offset, modulo, or wraparound tested.']}
    (ROOT/'agrippa_book_results.json').write_text(json.dumps(result,indent=2)+'\n')
    print(json.dumps({k:v for k,v in result.items() if k!='mapping'},indent=2))


if __name__ == '__main__':
    main()
