"""Crawl of the public META 2027 site, from its local mirror plus live status checks.

Link graph and anchors are resolved against the local files (byte-identical to production —
verified by checksum earlier). Liveness, redirect chains and loops are then checked over HTTP
with HEAD requests, which is also the polite way to probe this host.
"""

import glob
import re
import subprocess

PAGES = sorted(set(glob.glob('*.html')) - {'meta26_program_inject.html','compare-cards.html','test-cards.html'})
ids = {f: set(re.findall(r'id="([^"]+)"', open(f, encoding='utf-8').read())) for f in PAGES}

internal, external, files = set(), set(), set()
problems = []

for f in PAGES:
    s = open(f, encoding='utf-8').read()
    for href in re.findall(r'href="([^"]+)"', s) + re.findall(r'src="([^"]+)"', s):
        if href.startswith(('mailto:', 'tel:', 'javascript:', 'data:')):
            continue
        if href.startswith('#'):
            if href[1:] and href[1:] not in ids[f]:
                problems.append('%s → %s (ancre locale absente)' % (f, href))
            continue
        if href.startswith('http'):
            external.add((f, href))
            continue
        if ' ' in href or href != href.strip():
            problems.append('%s → %s (URL mal formée)' % (f, href))
        target, _, frag = href.partition('#')
        target = target.split('?')[0]
        if target.endswith('.html'):
            if target not in ids:
                problems.append('%s → %s (page absente)' % (f, href))
            elif frag and frag not in ids[target]:
                problems.append('%s → %s (ancre absente de la cible)' % (f, href))
            internal.add(target)
        elif target and target not in ('./',):
            files.add((f, target))

print('pages: %d · liens internes distincts: %d · assets référencés: %d · externes: %d'
      % (len(PAGES), len(internal), len({t for _, t in files}), len({h for _, h in external})))

unreachable = sorted(set(PAGES) - internal - {'home.html'})
print('pages jamais liées :', unreachable or 'aucune')

print('\nanomalies du graphe local : %d' % len(problems))
for p in problems:
    print('  ✗', p)

# assets existence is checked by the caller on the server; print the list
print('\nASSETS=%s' % ' '.join(sorted({t for _, t in files})))

# external links to check (HEAD) — the site's own domains only get a redirect-chain analysis
print('EXTERNES=%s' % ' '.join(sorted({h for _, h in external})))
