#!/usr/bin/env python3
"""officeTalk: a language for talking to AI, so that what you say you want matches what you feel you want.
The same rules as officetalk.js, for the command line. Syntax, and why: https://officetalk.teachsite.org/syntax.html

  officetalk.py check FILE   say what is wrong with the file, and ask what it leaves unclear
  officetalk.py read  FILE   read your file back to you in plain sentences
  officetalk.py note  FILE   print the note that goes with the work
"""
import re
import sys
from pathlib import Path

VERSION = "1.0.2"  # the language version; see https://officetalk.teachsite.org/CHANGELOG.md
WORDS = ["WANT", "FOR", "NOT", "DONE", "UNSURE", "HOLD", "MATCH", "SIGN"]
VAGUE = ["good", "better", "nice", "interesting", "engaging", "impactful", "meaningful",
         "effective", "clean", "modern", "unique", "creative", "powerful", "beautiful"]

COMMON = {"that", "this", "with", "what", "from", "have", "they", "them", "their", "there", "which", "would", "about", "into", "when", "then", "than", "some", "just", "will", "been", "were"}
words = lambda text: {w for w in re.findall(r"[a-z']+", text.lower()) if len(w) > 3 and w not in COMMON}


def read(path):
    lines = []
    for n, raw in enumerate(Path(path).read_text().splitlines(), 1):
        text = raw.strip()
        if not text or text.startswith("#"):
            continue
        word, _, value = text.partition(":")
        lines.append((n, word.strip().upper(), value.strip()))
    return lines


def problems(lines):
    found = []
    for n, word, value in lines:
        if word not in WORDS:
            found.append(f"line {n}: '{word}' is not a word of the language")
        elif not value:
            found.append(f"line {n}: {word} is empty")
        elif word == "WANT" and len(re.findall(r"[.!?](\s|$)", value)) > 1:
            found.append(f"line {n}: a WANT is one sentence; if you need two, you want two things")
        elif word == "MATCH" and not re.match(r"^(yes\b|no\b.*\w.*\w)", value.lower()):
            found.append(f"line {n}: after MATCH: no, write what feels different")
    if not lines or lines[0][1] != "WANT":
        found.append("the first line must be WANT")
    return found


def questions(lines):
    """What the file leaves unclear. Code asks; only the student can answer."""
    pick = lambda w: [v for _, x, v in lines if x == w]
    want = pick("WANT")[-1]
    asked = [f"What does '{v}' look like here, so that someone else could see it?"
             for v in VAGUE if re.search(rf"\b{v}\b", " ".join([want] + pick("DONE")).lower())]
    for no in pick("NOT"):
        for w in sorted(words(want) & words(no)):
            asked.append(f"Your WANT and a NOT both say '{w}'. Does the NOT narrow what you want, or pull against it?")
    if not pick("FOR"):
        asked.append("Who is this for? Name a person or a group, not 'everyone'.")
    if not pick("DONE"):
        asked.append("How will you know it is done? Write something another person could check.")
    if not pick("NOT"):
        asked.append("What do you not want? One thing is enough.")
    return asked


def unmatched(lines):
    """None if the latest version has been read back and matched, else what is still missing."""
    last = lambda kinds: max((i for i, (_, w, _) in enumerate(lines) if w in kinds), default=-1)
    changed, match = last(["WANT", "FOR", "NOT", "DONE"]), last(["MATCH"])
    if match < changed:
        return "read it back, then add a last line to what you wrote: MATCH: yes, or MATCH: no and what feels different"
    if not lines[match][2].lower().startswith("yes"):
        return "your last MATCH was no; rewrite what feels different, then add a new MATCH line"
    return None


def main():
    args = sys.argv[1:]
    if len(args) != 2 or args[0] not in ("check", "read", "note"):
        sys.exit(__doc__.strip())
    lines = read(args[1])
    found = problems(lines)
    if found:
        sys.exit("\n".join(found))
    pick = lambda w: [v for _, x, v in lines if x == w]
    if args[0] == "check":
        asked = questions(lines)
        print("\n".join(asked) if asked else "ok")
        if unmatched(lines):
            print("\nNot matched yet: " + unmatched(lines))
    elif args[0] == "read":
        print("You want this: " + pick("WANT")[-1])
        for line in pick("FOR"):
            print("It is for " + line + ".")
        for line in pick("NOT"):
            print("You do not want " + line + ".")
        for line in pick("DONE"):
            print("You will know it is done when " + line + ".")
        for line in pick("UNSURE"):
            print("You do not know yet: " + line + ".")
        if pick("HOLD"):
            print(f"You are keeping {len(pick('HOLD'))} thing(s) unwritten.")
        print("\nDoes this match what you feel? Add a last line to your file: MATCH: yes, or MATCH: no and what feels different.")
    else:
        if unmatched(lines) or not pick("SIGN"):
            sys.exit("not yet: " + (unmatched(lines) or "sign it with SIGN"))
        wants = pick("WANT")
        print("What I wanted at first: " + wants[0])
        if len(wants) > 1:
            print(f"What I want now, after {len(wants) - 1} rewrite(s): " + wants[-1])
        different = [m[2:].strip(" ,:-—") for m in pick("MATCH") if m.lower().startswith("no")]
        print("What felt different on the way: " + ("; ".join(different) or "nothing"))
        print("Still unsure: " + ("; ".join(pick("UNSURE")) or "nothing"))
        print("Signed: " + "; ".join(pick("SIGN")))


if __name__ == "__main__":
    main()
