#!/usr/bin/env python3
"""Re-check every row of dataset.json against its source.

Help pages and Signal's source files are fetched live (Signal-Android at the
tip of `main`, so a change upstream shows up here). Viallo rows are read from
the repository this file sits in. The receipt row also checks that Signal's
ReceiptMessage enum still has exactly the recorded members.

--recheck does the same: the check has no lab step to skip.

Exit 0 when every row holds, 1 otherwise.
"""

import html
import json
import os
import re
import sys
import urllib.request

HERE = os.path.dirname(os.path.abspath(__file__))
DATASET = os.path.join(HERE, "dataset.json")
# apps/web/public/blog/<slug>/check.py -> repository root
REPO = os.path.abspath(os.path.join(HERE, "..", "..", "..", "..", ".."))
UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0 Safari/537.36"

QUOTES = {"\u2018": "'", "\u2019": "'", "\u201c": '"', "\u201d": '"', "\u00a0": " ", "\u2013": "-", "\u2014": "-"}


def normalise(text):
    text = html.unescape(text)
    for bad, good in QUOTES.items():
        text = text.replace(bad, good)
    text = re.sub(r"\[([^\]]+)\]\([^)]+\)", r"\1", text)  # markdown links from the text proxy
    text = re.sub(r"\n\s*\*(?!/) ?", " ", text)  # Javadoc continuation lines
    text = text.replace("**", "").replace("`", "")
    return re.sub(r"\s+([.,;:])", r"\1", re.sub(r"\s+", " ", text)).strip()


def fetch(url, timeout=45):
    req = urllib.request.Request(url, headers={"User-Agent": UA, "Accept": "text/html,application/json,*/*"})
    with urllib.request.urlopen(req, timeout=timeout) as resp:
        raw = resp.read()
        charset = resp.headers.get_content_charset() or "utf-8"
    return raw.decode(charset, errors="ignore")


class Skip(Exception):
    pass


def source_text(row):
    url = row["url"]
    if url.startswith("repo:"):
        path = os.path.join(REPO, url[len("repo:"):])
        if not os.path.isfile(path):
            raise Skip(f"not in a Viallo checkout ({path})")
        with open(path, encoding="utf-8") as fh:
            return fh.read()
    target = row.get("fetchUrl") or url
    body = fetch(target)
    if "help_center" in target and target.endswith(".json"):
        body = json.loads(body).get("article", {}).get("body", "")
        return re.sub(r"<[^>]+>", " ", body)
    if target.startswith("https://r.jina.ai/") or target.startswith("https://raw.githubusercontent.com/"):
        return body
    body = re.sub(r"<script.*?</script>|<style.*?</style>", " ", body, flags=re.S)
    return re.sub(r"<[^>]+>", " ", body)


def enum_members(text, name):
    block = re.search(r"message\s+" + re.escape(name) + r"\s*\{\s*enum\s+\w+\s*\{(.*?)\}", text, re.S)
    if not block:
        return None
    return re.findall(r"^\s*([A-Z_]+)\s*=", block.group(1), re.M)


def main():
    recheck = "--recheck" in sys.argv
    with open(DATASET, encoding="utf-8") as fh:
        data = json.load(fh)

    failures = skipped = 0
    for row in data["results"]:
        label = f'{row["option"]} / {row["property"]}'
        try:
            raw = source_text(row)
        except Skip as why:
            print(f"SKIP  {label}: {why}")
            skipped += 1
            continue
        except Exception as exc:  # a fetch that fails is a failing row, not a crash
            print(f"ERROR {label}: {exc}")
            failures += 1
            continue
        ok = normalise(row["quote"]) in normalise(raw)
        detail = ""
        if ok and "enum" in row:
            found = enum_members(raw, row["enum"]["name"])
            ok = found == row["enum"]["members"]
            detail = f" (enum {row['enum']['name']}: {found})"
        print(f'{"PASS" if ok else "FAIL"}  {label}{detail}')
        if not ok:
            failures += 1
            print(f'      quote: {row["quote"][:120]}')
            print(f'      source: {row.get("fetchUrl") or row["url"]}')

    total = len(data["results"])
    print(f"\n{total - failures - skipped}/{total} rows hold, {skipped} skipped"
          f'{" (recheck)" if recheck else ""}')
    return 0 if failures == 0 else 1


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