"""Recount God/GOD/god in pinned KJV text and raw characters in pinned CUV text.

Python 3 standard library only. Both compressed inputs are checked before writing.
"""

import argparse
from collections import Counter
import csv
import gzip
import hashlib
import io
import json
from pathlib import Path
import re
import urllib.error
import urllib.request


SOURCE_URL = "https://trustbible.org/data/kjv.jsonl.gz"
SOURCE_SHA256 = "0c3b8afefa4efaa8bd72576682c8ad060e4ccb499e81943a9678de49ab9416ad"
CUV_URL = "https://trustbible.org/data/cuv-simplified.jsonl.gz"
CUV_SHA256 = "63727b793d1fb2da5c62d59ec7e13349b8e47bf25361ea515ef1b740b8ea7a5c"
STANDALONE = r"(?<![\w-])%s(?![\w-])"
BOUNDARY = r"\b%s\b"
# Each entry is an executable rule, not an inference about another site's method.
RULES = {
    "God_capitalised": (STANDALONE % "God", 0),
    "GOD_small_caps": (STANDALONE % "GOD", 0),
    "god_lowercase": (STANDALONE % "god", 0),
    "gods_plural": (STANDALONE % "gods", re.IGNORECASE),
    "any_case_god": (STANDALONE % "god", re.IGNORECASE),
    "LORD": (STANDALONE % "LORD", 0),
    "Lord": (STANDALONE % "Lord", 0),
    "possessives": ((STANDALONE % "god") + r"(?=['’])", re.IGNORECASE),
    "God_or_GOD": (STANDALONE % "(?:God|GOD)", 0),
    "any_case_god_or_gods": (STANDALONE % "gods?", re.IGNORECASE),
    "God_including_hyphens": (BOUNDARY % "God", 0),
    "God_or_GOD_including_hyphens": (BOUNDARY % "(?:God|GOD)", 0),
    "any_case_god_including_hyphens": (BOUNDARY % "god", re.IGNORECASE),
    "any_case_god_or_gods_including_hyphens": (BOUNDARY % "gods?", re.IGNORECASE),
    "any_case_without_possessives": ((STANDALONE % "god") + r"(?!['’])", re.IGNORECASE),
    "any_case_without_possessives_including_hyphens": ((BOUNDARY % "god") + r"(?!['’])", re.IGNORECASE),
    "God_without_possessives": ((STANDALONE % "God") + r"(?!['’])", 0),
    "God_without_possessives_including_hyphens": ((BOUNDARY % "God") + r"(?!['’])", 0),
    "God_or_Gods_including_hyphens": (BOUNDARY % "Gods?", 0),
    "God_or_GOD_without_possessives": ((STANDALONE % "(?:God|GOD)") + r"(?!['’])", 0),
    "God_or_GOD_without_possessives_including_hyphens": ((BOUNDARY % "(?:God|GOD)") + r"(?!['’])", 0),
    "any_case_god_substring": ("god", re.IGNORECASE),
    "God_substring": ("God", 0),
    "GOD_substring": ("GOD", 0),
    "god_lowercase_substring": ("god", 0),
    "God_word_prefix": (r"\bGod\w*", 0),
    "God_or_GOD_substring": ("(?:God|GOD)", 0),
    "Lord_GOD_phrase": (STANDALONE % "Lord GOD", 0),
    "LORD_God_phrase": (STANDALONE % "LORD God", 0),
}
CSV_RULES = list(RULES)[:8] + [
    "any_case_god_including_hyphens", "any_case_god_or_gods_including_hyphens",
]
FIELDS = ["reference", "testament", "book", *CSV_RULES, "hyphen_joined_god", "kjv_text", "context_url"]
COMPOUNDS = ["神迹", "精神", "鬼神", "假神", "别神", "诸神", "众神", "列神", "神像", "外邦神", "女神", "神明", "出神", "心神", "神气", "神不守舍"]


def require(condition, message):
    if not condition:
        raise ValueError(message)


def load_source(path, url, expected_hash, expected_records):
    if path:
        raw = path.read_bytes()
    else:
        request = urllib.request.Request(url, headers={"User-Agent": "TrustBible-God-count/1.0"})
        with urllib.request.urlopen(request, timeout=60) as response:
            raw = response.read()
    actual = hashlib.sha256(raw).hexdigest()
    require(actual == expected_hash, f"Source SHA-256 mismatch: expected {expected_hash}, got {actual}. No output written.")
    records = [json.loads(line) for line in gzip.decompress(raw).decode("utf-8").splitlines()]
    require(len(records) == expected_records, f"Expected {expected_records} verse records.")
    keys = set()
    for verse in records:
        require(isinstance(verse.get("book"), str) and isinstance(verse.get("text"), str), "Invalid book or text.")
        require(all(type(verse.get(k)) is int and verse[k] > 0 for k in ("chapter", "verse")), "Invalid verse reference.")
        key = (verse["book"], verse["chapter"], verse["verse"])
        require(key not in keys, f"Duplicate verse: {key}")
        keys.add(key)
    require(len({v["book"] for v in records}) == 66, "Expected 66 books.")
    return records


def count_pair(counts):
    return {"occurrences": sum(counts), "verses": sum(n > 0 for n in counts)}


def summarize(records, patterns, books):
    output = {}
    values = {}
    for name, (pattern, flags) in patterns.items():
        regex = re.compile(pattern, flags)
        counts = [len(regex.findall(verse["text"])) for verse in records]
        values[name] = counts
        per_book = {book: count_pair([n for verse, n in zip(records, counts) if verse["book"] == book]) for book in books}
        output[name] = {
            "pattern": pattern,
            "flags": "re.IGNORECASE (Unicode default)" if flags else "0 (case-sensitive; Unicode default)",
            "totals": count_pair(counts),
            "testaments": {label: count_pair([n for verse, n in zip(records, counts) if verse["book"] in selected])
                           for label, selected in (("OT", books[:39]), ("NT", books[39:]))},
            "per_book": per_book,
            "books_with_zero": [book for book, pair in per_book.items() if pair["occurrences"] == 0],
        }
    return output, values


def reference(verse):
    return f"{verse['book']} {verse['chapter']}:{verse['verse']}"


def context_url(verse):
    slug = verse["book"].lower().replace(" ", "-")
    return f"https://trustbible.org/en/bible/{slug}/{verse['chapter']}/#verse-{verse['verse']}"


def recount(kjv, cuv):
    books = list(dict.fromkeys(v["book"] for v in kjv))
    require(books[38:40] == ["Malachi", "Matthew"], "Unexpected OT/NT ordering.")
    require(set(books) == {v["book"] for v in cuv}, "KJV/CUV book names differ.")
    rules, values = summarize(kjv, RULES, books)
    rows = []
    for i, verse in enumerate(kjv):
        if "god" not in verse["text"].lower():
            continue
        rows.append({
            "reference": reference(verse), "testament": "OT" if verse["book"] in books[:39] else "NT", "book": verse["book"],
            **{name: values[name][i] for name in CSV_RULES},
            "hyphen_joined_god": values["any_case_god_including_hyphens"][i] - values["any_case_god"][i],
            "kjv_text": verse["text"], "context_url": context_url(verse),
        })
    claims = [
        (4094, None, None, "Circulating Bible-facts figure, often paired with Lord 6,781; historical method not established."),
        (4473, 3893, None, "Circulating figure attributed to Strong's on Q&A sites; source edition and rule unstated."),
        (4118, None, {"OT": 2879, "NT": 1239}, "Circulating figure with a quoted testament split; historical method not established."),
        (4008, None, None, "Circulating figure; historical method not established."),
        (4370, None, None, "Circulating figure; historical method not established."),
        (4446, 3877, None, "Bible Says About, exact-word total."),
        (4716, 4063, {"OT": 3335, "NT": 1381}, "Bible Says About, every-form total (god, gods, god's)."),
    ]
    reconciliations = []
    for occurrences, verses, split, attribution in claims:
        matching = [name for name, rule in rules.items()
                    if rule["totals"]["occurrences"] == occurrences
                    and (verses is None or rule["totals"]["verses"] == verses)
                    and (split is None or all(rule["testaments"][t]["occurrences"] == n for t, n in split.items()))]
        reconciliations.append({
            "quoted_occurrences": occurrences, "quoted_verses": verses, "quoted_testament_occurrences": split,
            "attribution": attribution, "matching_rules": matching,
            "result": "Reproduced under the listed rules in this input; this does not establish another source's method." if matching else "Not reproduced under any tested rule.",
            **({"comparison": "Within one of our hyphen-joined any-case count; source edition and rule unstated.",
                "occurrence_difference": occurrences - rules["any_case_god_including_hyphens"]["totals"]["occurrences"],
                "verse_difference": verses - rules["any_case_god_including_hyphens"]["totals"]["verses"]} if occurrences == 4473 else {}),
            **({"source_url": "https://www.biblesaysabout.com/words/god", "observed_on": "2026-10-27"} if occurrences in (4446, 4716) else {}),
        })
    cuv_rules, _ = summarize(cuv, {s: (re.escape(s), 0) for s in ["神", "耶和华", "上帝", *COMPOUNDS, "神人"]}, books)
    bigrams = Counter(text[i:i+2] for verse in cuv for text in [verse["text"]]
                      for i in range(len(text)-1) if "神" in text[i:i+2])
    return rows, {
        "dataset": "God word-count audit in a pinned English KJV; separate raw CUV character counts",
        "method_version": "1.0", "verified_on": "2026-10-27", "headline_rule": "any_case_god",
        "source": {"url": SOURCE_URL, "sha256": SOURCE_SHA256, "upstream": "https://github.com/aruljohn/Bible-kjv",
                   "imported_on": "2026-09-06", "printing": "Unknown", "verse_records": len(kjv), "unique_references": len(kjv),
                   "scope": "Verse text only; 66-book Protestant canon, no headings, notes or metadata."},
        "boundary": "Headline: reject adjacency to Python word characters OR ASCII hyphen; include possessives. Same rule as the Jesus guide.",
        "possessives": "Matches followed immediately by U+0027 or U+2019. Already included, never added again.",
        "rules": rules, "circulating_figures": reconciliations,
        "hyphen_joined_verses": [{"reference": row["reference"], "excluded_occurrences": row["hyphen_joined_god"], "context_url": row["context_url"]}
                                 for row in rows if row["hyphen_joined_god"]],
        "cuv": {
            "source": {"url": CUV_URL, "repository_path": "data/cuv-simplified.jsonl.gz", "sha256": CUV_SHA256,
                       "upstream": "https://github.com/seven1m/open-bibles", "imported_on": "2026-09-06",
                       "edition": "Simplified Chinese Union Version, Shen edition (神版)", "verse_records": len(cuv)},
            "rules": cuv_rules,
            "top_40_bigrams_containing_shen": [{"bigram": term, "occurrences": n} for term, n in bigrams.most_common(40)],
            "compound_examples": COMPOUNDS,
            "interpretation": "Raw 神 counts characters, including compounds and other gods; no subtraction-derived count of references to God is published. 神人 remains included. Compound counts overlap and must not be summed as exclusions.",
            "edition_note": "The Shangti edition uses 上帝 where the Shen edition uses 神 for God; this is not a global character replacement of every compound.",
            "corpus_note": "CUV has 31,100 records; KJV has 31,102. These are different translations and verse inventories, not aligned totals.",
        },
        "limits": ["A word search does not identify every reference to God; pronouns and titles such as Almighty are not counted.",
                   "GOD and LORD are stored as uppercase letters in this plain-text input, not typographic small caps.",
                   "Totals, per-book values and testament splits use every source verse. CSV LORD/Lord columns cover only god-substring candidate verses, so their CSV sums are not corpus totals."],
    }


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--input", type=Path, help="Offline copy of the exact pinned KJV gzip.")
    parser.add_argument("--cuv-input", type=Path, help="Offline copy of the exact pinned CUV gzip.")
    parser.add_argument("--output-dir", type=Path, default=Path("."))
    args = parser.parse_args()
    try:
        kjv = load_source(args.input, SOURCE_URL, SOURCE_SHA256, 31102)
        cuv = load_source(args.cuv_input, CUV_URL, CUV_SHA256, 31100)
        rows, method = recount(kjv, cuv)
        output = io.StringIO(newline="")
        writer = csv.DictWriter(output, fieldnames=FIELDS, lineterminator="\n")
        writer.writeheader()
        writer.writerows(rows)
        csv_bytes = output.getvalue().encode("utf-8-sig")
        method["audit"] = {"url": "https://trustbible.org/data/god-count-audit.csv", "sha256": hashlib.sha256(csv_bytes).hexdigest(),
                           "encoding": "UTF-8 with BOM; comma-delimited, LF", "candidate_pattern": "god", "candidate_flags": "re.IGNORECASE",
                           "candidate_rows": len(rows), "columns": FIELDS,
                           "zero_headline_rows": [row["reference"] for row in rows if row["any_case_god"] == 0]}
        method["reproduction"] = {"script_url": "https://trustbible.org/data/count-god.py", "script_sha256": hashlib.sha256(Path(__file__).read_bytes()).hexdigest(),
                                  "requires": "Python 3 standard library only; internet for default input downloads.",
                                  "command": "python count-god.py --output-dir results",
                                  "offline_command": "python count-god.py --input kjv.jsonl.gz --cuv-input cuv-simplified.jsonl.gz --output-dir results",
                                  "outputs": ["god-count-audit.csv", "god-count-method.json"]}
        args.output_dir.mkdir(parents=True, exist_ok=True)
        (args.output_dir / "god-count-audit.csv").write_bytes(csv_bytes)
        (args.output_dir / "god-count-method.json").write_text(json.dumps(method, ensure_ascii=False, indent=2) + "\n", encoding="utf-8", newline="\n")
    except (OSError, ValueError, KeyError, urllib.error.URLError) as error:
        parser.exit(1, f"Recount failed: {error}\n")
    print("PASS: both source hashes and unique verse inventories verified.")
    for name, rule in method["rules"].items():
        print(f"{name}: {rule['totals']['occurrences']:,} occurrences / {rule['totals']['verses']:,} verses")
    for claim in method["circulating_figures"]:
        print(f"{claim['quoted_occurrences']:,}: {', '.join(claim['matching_rules']) or claim['result']}")
    print(f"CSV candidate rows: {len(rows)}. Output: {args.output_dir.resolve()}")


if __name__ == "__main__":
    main()
