#!/usr/bin/env python3
"""Verify the self-contained Codex report delivery using only the standard library."""

from __future__ import annotations

import re
import struct
import sys
import zipfile
import hashlib
from html.parser import HTMLParser
from pathlib import Path
from urllib.parse import unquote, urlsplit
from xml.etree import ElementTree


CODEX = Path(__file__).resolve().parent
REPO = CODEX.parents[1]
REPOSITORY_CONTEXT = (
    (REPO / "docs/Configuration.txt").is_file()
    and (REPO / "test").is_dir()
)
ERRORS: list[str] = []


def check(ok: bool, message: str) -> None:
    print(f"[{'OK' if ok else 'FAIL'}] {message}")
    if not ok:
        ERRORS.append(message)


def skip(message: str) -> None:
    print(f"[SKIP] {message}")


def local_target(source: Path, raw: str) -> Path | None:
    raw = raw.strip().strip("<>")
    parsed = urlsplit(raw)
    if parsed.scheme or raw.startswith("//"):
        return None
    path = unquote(parsed.path)
    if not path:
        return None
    if path.startswith("/"):
        return REPO / path.lstrip("/")
    return (source.parent / path).resolve()


def markdown_links() -> list[tuple[Path, str]]:
    links: list[tuple[Path, str]] = []
    pattern = re.compile(r"!?\[[^\]]*\]\(([^)\n]+)\)")
    for source in CODEX.rglob("*.md"):
        text = source.read_text(encoding="utf-8", errors="replace")
        text = re.sub(r"```.*?```", "", text, flags=re.DOTALL)
        text = re.sub(r"`[^`\n]*`", "", text)
        for match in pattern.finditer(text):
            raw = match.group(1).strip()
            if " " in raw and not raw.startswith("<"):
                raw = raw.split()[0]
            links.append((source, raw))
    return links


class HrefParser(HTMLParser):
    def __init__(self) -> None:
        super().__init__()
        self.links: list[str] = []

    def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
        if tag not in {"a", "img", "link", "script"}:
            return
        names = {"a": "href", "img": "src", "link": "href", "script": "src"}
        for key, value in attrs:
            if key == names[tag] and value and not value.startswith("data:"):
                self.links.append(value)


def verify_links() -> None:
    missing: list[str] = []
    external_repo_links = 0
    for source, raw in markdown_links():
        target = local_target(source, raw)
        if target is not None and not target.exists():
            if not REPOSITORY_CONTEXT and CODEX not in target.parents:
                external_repo_links += 1
                continue
            missing.append(f"{source.relative_to(REPO)} -> {raw}")
    for source in (CODEX / "index.html", CODEX / "report_delivery_index.html"):
        parser = HrefParser()
        parser.feed(source.read_text(encoding="utf-8"))
        for raw in parser.links:
            target = local_target(source, raw)
            if target is not None and not target.exists():
                missing.append(f"{source.relative_to(REPO)} -> {raw}")
    check(not missing, f"local Markdown/entry-page links resolve ({len(missing)} missing)")
    if external_repo_links:
        skip(f"{external_repo_links} repository-context links are unavailable in the portable copy")
    for item in missing[:20]:
        print("       ", item)


def png_size(path: Path) -> tuple[int, int]:
    data = path.read_bytes()[:24]
    if len(data) != 24 or data[:8] != b"\x89PNG\r\n\x1a\n":
        raise ValueError("invalid PNG signature")
    return struct.unpack(">II", data[16:24])


def verify_figures() -> None:
    report_svg = sorted((CODEX / "diagrams/report").glob("*.svg"))
    qa_svg = sorted((CODEX / "diagrams/qa3").glob("*.svg"))
    report_png = sorted((CODEX / "diagrams/report").glob("*.png"))
    qa_png = sorted((CODEX / "diagrams/qa3").glob("*.png"))
    check(len(report_svg) == len(report_png) == 21, "report figure set is 21 SVG + 21 PNG")
    check(len(qa_svg) == len(qa_png) == 25, "QA figure set is 25 SVG + 25 PNG")
    bad_svg: list[str] = []
    for path in report_svg + qa_svg:
        try:
            ElementTree.parse(path)
        except (OSError, ElementTree.ParseError):
            bad_svg.append(path.name)
    check(not bad_svg, f"all {len(report_svg) + len(qa_svg)} SVG files parse")
    bad_png: list[str] = []
    for path in report_png + qa_png:
        try:
            width, height = png_size(path)
            if width < 600 or height < 300:
                bad_png.append(f"{path.name} ({width}x{height})")
        except (OSError, ValueError):
            bad_png.append(path.name)
    check(not bad_png, f"all {len(report_png) + len(qa_png)} PNG files are nontrivial")


def verify_counts() -> None:
    archive = (CODEX / "New_Implementation_Archive.md").read_text(encoding="utf-8")
    ids = sorted({int(value) for value in re.findall(r"\bF(\d{2,3})\b", archive)})
    check(ids == list(range(297)), "feature IDs are continuous F00-F296 (297 total)")
    if REPOSITORY_CONTEXT:
        config = (REPO / "docs/Configuration.txt").read_text(encoding="utf-8")
        names = set(re.findall(r"SYMCC_[A-Z0-9_]+", config))
        check(len(names) == 375, "Configuration.txt contains 375 unique SYMCC_* names")
        tests = [path for path in (REPO / "test").iterdir()
                 if path.is_file() and not path.is_symlink()]
        check(len(tests) == 231, "test/ contains 231 top-level regular files")
    else:
        skip("repository-only Configuration.txt and test/ counts")


def verify_pptx() -> None:
    path = CODEX / "Project_Progress_Presentation_2026-07-30.pptx"
    try:
        with zipfile.ZipFile(path) as archive:
            bad = archive.testzip()
            names = archive.namelist()
            slides = [n for n in names if re.fullmatch(r"ppt/slides/slide\d+\.xml", n)]
            notes = [n for n in names if re.fullmatch(r"ppt/notesSlides/notesSlide\d+\.xml", n)]
            media = [n for n in names if re.fullmatch(r"ppt/media/image\d+\.png", n)]
            slide14 = archive.read("ppt/slides/slide14.xml").decode("utf-8")
            notes13 = archive.read("ppt/notesSlides/notesSlide13.xml").decode("utf-8")
        check(bad is None, "PPTX ZIP members pass CRC")
        check(len(slides) == 28 and len(notes) == 27, "PPTX contains 28 slides and 27 notes")
        check(len(media) == 24, "PPTX contains 24 embedded PNG figures")
        check("本轮三类复用命中均为 0" in slide14 and "命中均为 0" in notes13,
              "PPTX context-reuse boundary is synchronized")
    except (OSError, KeyError, zipfile.BadZipFile) as error:
        check(False, f"PPTX structural verification: {error}")


def verify_report() -> None:
    md = (CODEX / "Project_Report_2026-07.md").read_text(encoding="utf-8")
    required = [
        "5.7k 行",
        "43.8k 行",
        "`poly_cache_hits` | **0**",
        "`prefix_context_entries / hits` | **0 / 0**",
        "LLVM 18 `209/209`",
        "LLVM 17 `208 passed + 1 unsupported`",
        "Python unittest `475/475`",
    ]
    check(all(item in md for item in required), "main report contains current audited facts")
    html = (CODEX / "Project_Report_2026-07.html").read_text(encoding="utf-8")
    check(html.count('src="data:image/') >= 46, "HTML report embeds all figure resources")


def verify_package() -> None:
    path = CODEX / "SymCC_Project_Report_2026-07-30-reviewed.zip"
    prefix = "symcc-report-codex/"
    try:
        with zipfile.ZipFile(path) as archive:
            check(archive.testzip() is None, "reviewed ZIP members pass CRC")
            raw = archive.read(prefix + "PACKAGE_MANIFEST.sha256").decode("utf-8")
            mismatches: list[str] = []
            for line in raw.splitlines():
                expected, rel = line.split("  ", 1)
                actual = hashlib.sha256(archive.read(prefix + rel)).hexdigest()
                if actual != expected:
                    mismatches.append(rel)
            check(not mismatches, "reviewed ZIP matches its SHA-256 content manifest")
    except (OSError, KeyError, ValueError, zipfile.BadZipFile) as error:
        check(False, f"reviewed ZIP structural verification: {error}")


def main() -> int:
    verify_links()
    verify_figures()
    verify_counts()
    verify_pptx()
    verify_report()
    verify_package()
    if ERRORS:
        print(f"\n{len(ERRORS)} verification check(s) failed.")
        return 1
    print("\nAll Codex delivery checks passed.")
    return 0


if __name__ == "__main__":
    sys.exit(main())
