#!/usr/bin/env python3
"""Deterministic lexical retrieval of curated fictional facts; no model inference."""
import argparse
import json
import re
from datetime import date
from pathlib import Path

ROOT = Path(__file__).resolve().parent
NORMAL_FORMS = {"teammates": "teammate", "invitations": "invitation", "members": "member", "invoices": "invoice"}


def tokens(text):
    return {NORMAL_FORMS.get(token, token) for token in re.findall(r"[a-z0-9]+", text.lower())}


def load_documents(folder):
    documents, ids = [], set()
    for path in sorted(folder.glob("*.md")):
        text = path.read_text(encoding="utf-8")
        if not text.startswith("---\n"):
            raise ValueError(f"Missing metadata: {path.name}")
        header, body = text[4:].split("\n---\n", 1)
        meta = {}
        for line in header.splitlines():
            key, value = line.split(":", 1)
            if key.strip() in meta:
                raise ValueError(f"Duplicate metadata: {path.name}")
            meta[key.strip()] = value.strip()
        for key in ("id", "title", "approved_on", "status", "fact_key", "retrieval_terms"):
            if not meta.get(key):
                raise ValueError(f"Missing {key}: {path.name}")
        date.fromisoformat(meta["approved_on"])
        if meta["id"] in ids:
            raise ValueError(f"Duplicate document ID: {meta['id']}")
        ids.add(meta["id"])
        if meta["status"] != "approved":
            continue
        quote = " ".join(line.strip() for line in body.splitlines() if line.strip() and not line.startswith("#"))
        if not quote:
            raise ValueError(f"Empty source: {path.name}")
        documents.append({**meta, "path": "docs/" + path.name, "quote": quote})
    if not documents:
        raise ValueError("No approved documents available")
    return documents


def evidence(doc):
    return {key: doc[key] for key in ("id", "title", "approved_on", "status", "path", "fact_key", "quote")}


def retrieve(question, documents):
    query_tokens = tokens(question)
    facts = {}
    for doc in documents:
        group = facts.setdefault(doc["fact_key"], {"documents": [], "terms": set()})
        group["documents"].append(doc)
        group["terms"].update(tokens(doc["retrieval_terms"]))
    ranking = []
    for fact_key, group in facts.items():
        matched = sorted(query_tokens & group["terms"])
        if matched:
            ranking.append({"fact_key": fact_key, "score": len(matched), "matched_terms": matched})
    ranking.sort(key=lambda row: (-row["score"], row["fact_key"]))
    result = {"question": question, "method": "Deterministic term overlap over manually curated fact metadata", "generation": "not_run", "ranking": ranking, "evidence": []}
    if not ranking:
        return {**result, "status": "unknown", "abstained": True, "message": "No matching approved fact was found. No answer can be supported by this small corpus."}
    tied = [row for row in ranking if row["score"] == ranking[0]["score"]]
    if len(tied) > 1:
        result["evidence"] = [evidence(doc) for row in tied for doc in facts[row["fact_key"]]["documents"]]
        return {**result, "status": "ambiguous", "abstained": True, "message": "Several fact groups have the same score. Clarify the question before selecting evidence."}
    best_key = ranking[0]["fact_key"]
    selected = facts[best_key]["documents"]
    result.update(fact_key=best_key, evidence=[evidence(doc) for doc in selected])
    # Conflict handling is explicit and narrow: distinct normalized statements under
    # the same manually assigned fact key. This is not semantic contradiction detection.
    statements = {" ".join(doc["quote"].lower().split()) for doc in selected}
    if len(statements) > 1:
        return {**result, "status": "conflict", "abstained": True, "message": "Approved sources disagree for this fact. Ask the document owners to resolve the conflict; the newer date alone does not supersede a source."}
    return {**result, "status": "evidence_found", "abstained": False, "message": "Relevant approved excerpt retrieved. No generated answer or interpretation is included."}


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("question")
    parser.add_argument("--docs", type=Path, default=ROOT / "docs")
    parser.add_argument("--output", type=Path)
    args = parser.parse_args()
    result = retrieve(args.question, load_documents(args.docs))
    serialized = json.dumps(result, indent=2, ensure_ascii=False) + "\n"
    if args.output:
        args.output.write_text(serialized, encoding="utf-8")
    else:
        print(serialized, end="")


if __name__ == "__main__":
    main()
