#!/usr/bin/env python3
"""Check behind /blog/bank-statement-dates-no-year.

1. Reads thirteen printed dates with Evidilo's production date reader
   (packages/parsers/src/rules/dates.ts readDate) under the statement period and date order each
   row names, and the same yearless forms with Node's built-in Date, which has no period to use.
2. Reads one digital statement page running from 16 December 2025 to 15 January 2026, whose rows
   print month and day only, the way the upload path does (apps/api/src/conversions/
   uploaded-statement.ts): the period off the page first, then the rows with extract(). The same
   page with its period line removed is read again.
3. Confirms the upload path still carries the sentence a statement with no readable date is
   refused with.
4. Re-reads Microsoft's DATEVALUE page and MDN's Date.parse page and confirms the quoted
   sentences are still there.

Steps 1 and 2 run through read-dates.harness.ts and vitest, with harness.vitest.config.mjs.

Exit 0: every result matches dataset.csv and every quote is found. Exit 1: a result differs or a
quote is gone. Exit 2: a cited page could not be reached at all (nothing else failed).
`--recheck` runs the same steps (none takes over a minute). `--write` rewrites dataset.csv.
"""

import csv
import html
import json
import re
import os
import subprocess
import sys
import tempfile
import urllib.error
import urllib.request
from pathlib import Path

HERE = Path(__file__).resolve().parent
REPO = HERE.parents[4]  # apps/web/public/blog/<slug> -> repository root
UA = (
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
    "(KHTML, like Gecko) Chrome/130.0 Safari/537.36"
)
QUOTES = {
    "https://support.microsoft.com/en-us/excel/functions/datevalue-function": [
        "If the year portion of the date_text argument is omitted, the DATEVALUE function uses the "
        "current year from your computer's built-in clock.",
    ],
    "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse": [
        "Other formats are implementation-defined and may not work across all browsers.",
    ],
}
UNDATED = "This statement prints no date we can read, so we cannot tell which period it covers."


def measure():
    with tempfile.TemporaryDirectory() as tmp:
        out = Path(tmp) / "dates.json"
        env = dict(os.environ, DATES_OUT=str(out))
        subprocess.run(
            ["npx", "vitest", "run", "--config", str(HERE / "harness.vitest.config.mjs")],
            cwd=REPO, env=env, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
        )
        data = json.loads(out.read_text())

    rows = []
    for case in data["dates"]:
        period = case["period"]
        rows.append({
            "id": case["id"],
            "reader": "evidilo readDate",
            "printed": case["printed"],
            "period": f'{period["from"]}..{period["to"]}' if period else "none",
            "order": case["order"],
            "result": case["evidilo"] or "unread",
        })
    for case in data["nodeDate"]:
        rows.append({
            "id": "node-" + case["printed"].replace("/", "-").replace(" ", "-").lower(),
            "reader": "node Date",
            "printed": case["printed"],
            "period": "none",
            "order": "",
            "result": case["nodeDate"] or "invalid",
        })
    for name, read in (("page-with-period", data["pageWithPeriod"]), ("page-without-period", data["pageWithoutPeriod"])):
        period = read["period"]
        if not read["rows"]:
            rows.append({
                "id": name, "reader": "evidilo upload + extract", "printed": "12/30, 01/02",
                "period": "none", "order": "month-first", "result": read["outcome"],
            })
        for i, row in enumerate(read["rows"], 1):
            rows.append({
                "id": f"{name}-row-{i}",
                "reader": "evidilo upload + extract",
                "printed": row["printed"],
                "period": f'{period["from"]}..{period["to"]}' if period else "none",
                "order": "month-first",
                "result": row["date"] or "unread",
            })
    return rows


def page_text(url):
    req = urllib.request.Request(url, headers={"User-Agent": UA})
    with urllib.request.urlopen(req, timeout=30) as resp:
        raw = resp.read().decode("utf-8", "replace")
    text = html.unescape(re.sub(r"<[^>]+>", " ", raw))
    return re.sub(r"\s+", " ", text).replace("\u2019", "'")


def check_quotes():
    """0 when every quote is found, 1 when one is gone, 2 when a page could not be read."""
    status = 0
    for url, quotes in QUOTES.items():
        try:
            text = page_text(url)
        except (urllib.error.URLError, TimeoutError) as error:
            print(f"UNREACHABLE {url}: {error}")
            status = max(status, 2)
            continue
        for quote in quotes:
            if quote not in text:
                print(f"FAIL quote gone from {url}: {quote!r}")
                status = 1
    return status


def main():
    rows = measure()
    fields = ["id", "reader", "printed", "period", "order", "result"]
    target = HERE / "dataset.csv"
    if "--write" in sys.argv:
        with target.open("w", newline="") as f:
            w = csv.DictWriter(f, fieldnames=fields)
            w.writeheader()
            w.writerows(rows)

    failed = False
    source = (REPO / "apps/api/src/conversions/uploaded-statement.ts").read_text()
    if UNDATED not in source:
        print("FAIL the upload path no longer carries the undated sentence")
        failed = True

    with target.open(newline="") as f:
        stored = list(csv.DictReader(f))
    if stored != rows:
        failed = True
        stored_by_id = {r["id"]: r for r in stored}
        for row in rows:
            if stored_by_id.get(row["id"]) != row:
                print(f'FAIL {row["id"]}: now {row["result"]!r}, stored {stored_by_id.get(row["id"], {}).get("result")!r}')
        if len(stored) != len(rows):
            print(f"FAIL {len(rows)} rows measured, {len(stored)} stored")

    quotes = check_quotes()
    if failed or quotes == 1:
        return 1
    if quotes == 2:
        return 2
    print(f"ok: {len(rows)} rows match dataset.csv, every quote found")
    return 0


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