#!/usr/bin/env python3
"""Length, form, tense, POV, letter, fact and beat checks on a raw file: python check_chapter.py raw/<tool>.md"""
import re, sys
text = open(sys.argv[1], encoding="utf-8").read()
ans = text.split("\n## Answer", 1)[1] if "\n## Answer" in text else text
a = ans.replace(" ", " ").replace("–", "-").replace("—", "-").strip()
lines = [l for l in a.split("\n") if l.strip()]
title = lines[0].strip("# *").strip() if lines else ""
body = "\n".join(lines[1:]) if len(lines) > 1 else a
low = body.lower()
words = len(re.findall(r"\S+", body))
nodialog = re.sub(r"[\"“][^\"”]*[\"”]|'[^']*'", " ", body)          # strip quoted speech before POV/tense checks
first_person = len(re.findall(r"\bI\b|\bmy\b|\bme\b", nodialog))
present = len(re.findall(r"\b(she|marta|alice|it)\s+(is|has|walks|climbs|stops|looks|puts|sits|opens|takes|hears|says|thinks|goes|makes|pours|counts)\b", nodialog, re.I))
sents = re.split(r"(?<=[.!?])\s+", low)
neg = r"\b(not|never|no|without|could not|considered|wanted to|want to|instead of|rather than|unopened|did not|before|strength to)\b|n't"
opened = any(re.search(r"(open(ed|s)?|tore|tears|ripped|unfold(ed|s)?|slit|read) (the|it|open)?\s*(envelope|letter|flap|seal)|(envelope|letter) (was|lay) open|unfolded the (page|sheet|letter)", s) and not re.search(neg, s) for s in sents)
reveal = bool(re.search(r"\b(a|the|his) box\b(?! of tea)|\bkeys? to (the|a|her)\b|\bmoney\b|\bleft (her|marta) (something|a |his )|belongings|inheritance|bequeath|bequest", low))
broken = []
if re.search(r"\b(the|a|an) (lift|elevator)\b", low) and any(re.search(r"\b(the|a|an) (lift|elevator)\b", s) and not re.search(r"\b(no|never|not|without)\b", s) for s in sents): broken.append("a lift")
if any(re.search(r"\bcoffee\b|\bespresso\b|\bgalão\b", s) and not re.search(r"\b(no|never|not|stopped|since 2019|instead|rather than|holding|drunk)\b", s) for s in sents): broken.append("coffee")
if any(re.search(r"\b(missing|disappeared|estranged|walked out|ran away|alive)\b", s) and re.search(r"\b(tiago|brother|he)\b", s) for s in sents): broken.append("Tiago missing/estranged/alive?")
if re.search(r"\bright[- ]handed\b|\bright hand\b(?! (pocket|side|corner|wall|door))|\bright-hand\b(?! (pocket|side|corner|wall|door))", low): broken.append("right-handed?")
if re.search(r"\b20(1[0-8]|2[0-5])\b", body) and not re.search(r"\b2019\b", body): broken.append("year other than 2019")
if re.search(r"\b(fifth|5th|sixth|6th|third|3rd) floor\b.{0,40}\b(her|marta'?s?) (flat|door|floor)\b", low): broken.append("wrong floor")
if re.search(r"\b(sister|father|mother|husband|boyfriend|ex-husband|daughter|son)\b", low): broken.append("family not in the bible?")
beats = {
 "1 rain+stairs+radio": all(k in low for k in ("rain",)) and bool(re.search(r"stair|landing|floor", low)) and "radio" in low,
 "2 envelope, no stamp, R. Barros": bool(re.search(r"barros", low)) and bool(re.search(r"stamp", low)) and "envelope" in low,
 "3 knows the name": bool(re.search(r"(knew|recognised|recognized|remembered) (the|that|whose|his) (name|hand|writing)|landlord", low)),
 "4 tea + March 2019 call": "tea" in low and bool(re.search(r"march|2019", low)) and bool(re.search(r"phone|call|rang", low)),
 "5 coat pocket + bed + tomorrow": bool(re.search(r"coat|jacket", low)) and "pocket" in low and bool(re.search(r"bed|sleep", low)) and "tomorrow" in low,
}
tail = low[-400:]
ends_on_five = "tomorrow" in tail or bool(re.search(r"(bed|sleep|pocket)", tail))
known = {"Marta","Sequeira","Tiago","Alice","Dona","Rui","Barros","R","Porto","Rua","Cedofeita","Bonfim","São","Sao","João","Joao","February","March","April","The","She","He","It","They","There","When","Then","But","And","Her","His","In","On","At","By","For","Of","To","A","An","That","This","Not","No","Nothing","Outside","Inside","Upstairs","Downstairs","Third","Fourth","Menina","Behind","Below","Above","Somewhere","Once","Now","Tomorrow","Tonight","Later","Still","Only","Even","After","Before","Rain","Chapter","One","I","If","So","Yes","Maybe","Perhaps","Instead","Nobody","Someone","Something","Each","Every","All","Most","Some","Half","What","Why","How","Where","Who","Which","Whatever","Just","Two","Three","Four","Five","Six","Seven","Its","You","Your","We","Their","Them","Good","Fine","Right","Left","Okay","OK","Sorry","Thank","Thanks","Hello","Goodnight","Night","Please","Come","Go","Wait","Look","Well","Oh","Ah","Because","Until","While","Through","Over","Under","Against","Around","Across","Along","Into","Onto","Out","Up","Down","Back","Home","Work","Hospital","Pharmacy","Ward"}
caps = re.findall(r"\b[A-ZÀ-Ý][a-zà-ÿ]+(?:\s[A-ZÀ-Ý][a-zà-ÿ]+)?\b", body)
unknown = sorted(set(c for c in caps if c.split()[0] not in known))
preface = bool(re.search(r"^(here('s| is)|sure|certainly|below is|of course)", lines[0].lower())) if lines else False
notes = bool(re.search(r"(would you like|let me know|note:|i kept|i've kept|i have kept|word count|\(\d{3,4} words\))", low))
bullets = bool(re.search(r"^\s*([-*•]|\d+[.)])\s", body, re.M))
headings = bool(re.search(r"^\s*#{1,6}\s|^\s*\*\*[^*]{3,60}\*\*\s*$", body, re.M))
print(f"title: {title[:70]!r}")
print(f"words (below title): {words}  ->  {'in window' if 900 <= words <= 1100 else ('SHORT' if words < 900 else 'LONG')}")
print(f"first-person tokens outside dialogue: {first_person} | present-tense narration hits: {present}")
print(f"letter opened: {opened} | contents hinted (box/key/money/left her): {reveal}")
print(f"facts broken (candidates): {broken}")
for k, v in beats.items(): print(f"beat {k}: {v}")
print(f"ends on beat five: {ends_on_five}")
print(f"unrecognised capitalised names (look at these): {unknown}")
print(f"preface: {preface} | notes/offer: {notes} | bullets: {bullets} | headings: {headings}")
