#!/usr/bin/env python3
"""Re-run the primary-docs check behind the Viallo post on AirDrop alternatives.

Reads dataset.json next to this file, fetches every cited page, and reports per
row whether each supporting quote still appears on it. Rows whose support is a
line of Viallo's own code (url "repo:<path>") are read from the repository this
file sits in; outside a checkout they are skipped. Also confirms dataset.csv
carries the same values as the table rows of dataset.json.

Exit 0 when every checked row holds, 1 otherwise. --recheck re-reads the pages
the same way; this check has no lab step to skip.

Standard library only.
"""

import argparse
import csv
import html
import json
import re
import sys
import urllib.request
from pathlib import Path

HERE = Path(__file__).resolve().parent
REPO = HERE.parents[4] if len(HERE.parents) > 4 else HERE
UA = "Mozilla/5.0"
TIMEOUT = 40
PROPS = ["works_with", "recipient_account", "albums", "free_cap", "link_lifetime", "link_revocable", "delivered_resolution"]


def normalise(text: str) -> str:
    text = re.sub(r"(?s)<!--.*?-->", " ", text)
    text = re.sub(r"(?s)<[^>]+>", " ", text)
    text = html.unescape(text)
    for a, b in (("’", "'"), ("‘", "'"), ("“", '"'), ("”", '"'), ("\xa0", " ")):
        text = text.replace(a, b)
    return re.sub(r"\s+", " ", text).strip()


def fetch(url: str) -> str:
    req = urllib.request.Request(url, headers={"User-Agent": UA, "Accept-Language": "en-US,en;q=0.9"})
    with urllib.request.urlopen(req, timeout=TIMEOUT) as resp:
        if resp.status != 200:
            raise RuntimeError(f"HTTP {resp.status}")
        return normalise(resp.read().decode("utf-8", errors="replace"))


def csv_matches(data: dict) -> list:
    problems = []
    with (HERE / "dataset.csv").open(newline="") as fh:
        rows = {r["option"]: r for r in csv.DictReader(fh)}
    for res in data["results"]:
        if not res.get("inTable"):
            continue
        cell = rows.get(res["option"], {}).get(res["property"])
        if cell != res["value"]:
            problems.append(f"dataset.csv {res['option']} / {res['property']} is {cell!r}, dataset.json says {res['value']!r}")
    return problems


def main() -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--recheck", action="store_true", help="re-read the pages; there is no lab step to skip")
    args = parser.parse_args()

    data = json.loads((HERE / "dataset.json").read_text())
    pages: dict = {}
    failures = []
    checked = skipped = 0

    for problem in csv_matches(data):
        print(f"FAIL  {problem}")
        failures.append(problem)

    for row in data["results"]:
        label = f"{row['option']} / {row['property']}"
        if not row.get("checked"):
            print(f"SKIP  {label}: product positioning, not re-read")
            skipped += 1
            continue

        url = row["url"]
        if url not in pages:
            try:
                if url.startswith("repo:"):
                    path = REPO / url[len("repo:"):]
                    pages[url] = path.read_text() if path.exists() else None
                else:
                    pages[url] = fetch(url)
            except Exception as err:
                pages[url] = err
        body = pages[url]

        if body is None:
            print(f"SKIP  {label}: {url} is not in this checkout")
            skipped += 1
            continue
        if isinstance(body, Exception):
            print(f"FAIL  {label}: {url} unreachable ({body})")
            failures.append(label)
            continue

        quotes = row.get("quotes") or []
        if not quotes:
            print(f"OK    {label}: page loads; it published nothing on this when checked")
            checked += 1
            continue
        missing = [q for q in quotes if q not in body]
        if missing:
            for q in missing:
                print(f"FAIL  {label}: quote gone from {url}: {q!r}")
            failures.append(label)
        else:
            print(f"OK    {label}: {len(quotes)} quote(s) still on the page")
            checked += 1

    print()
    print(f"{checked} checked, {skipped} skipped, {len(failures)} failed" + (" (recheck)" if args.recheck else ""))
    return 1 if failures else 0


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