#!/usr/bin/env python3
"""facts/audit - evidence & lexicon checks for the 2dph brain.

  bin/facts/audit self      # lexicon: every fact in db has >=2 sources
  bin/facts/audit db        # evidence gate: run against var/kb.lbug

`self` mode checks the repo itself (no network, no runtime deps). It greps
for known-good two-source pairings and confirms the docs are consistent.
`db` mode loads every Leaf with root=facts and asserts each has source_rev
and a non-empty `loc` (the "where did you see it" evidence pointer) and that
'confirmed' facts carry a two-source `source` field.

Exit 0 = all checks pass, 1 = audit failures, 2 = could not evaluate.
"""
from __future__ import annotations

import json
import re
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT / "bin" / "tools"))


def audit_db() -> list[str]:
    from kblib import connect
    from kblib import VAR
    dbpath = VAR / "kb.lbug"
    if not dbpath.exists():
        return ["no database yet; run bin/kb/index first"]
    db, conn = connect(dbpath)
    r = conn.execute("MATCH (l:Leaf {root:'facts'}) RETURN l.id, l.source, l.loc, l.how, l.confidence")
    problems: list[str] = []
    for lid, source, loc, how, conf in r.get_all():
        if conf != "confirmed":
            problems.append(f"{lid}: facts require confidence='confirmed', got '{conf}'")
        if not source or " x " not in source:
            problems.append(f"{lid}: needs 2-source evidence in source, got '{source}'")
        if not loc:
            problems.append(f"{lid}: missing loc (evidence pointer)")
        if not how:
            problems.append(f"{lid}: missing how")
    conn.close()
    db.close()
    return problems


def audit_self() -> list[str]:
    problems: list[str] = []
    plan = (ROOT / "PLAN.md").read_text()

    if "recall@5" not in plan:
        problems.append("PLAN.md missing recall@5 gate")
    if re.search(r"(?i)facts must have.*2 sources|2.source", plan) is None:
        problems.append("PLAN.md missing the two-source evidence rule for facts")
    if re.search(r"(?i)HNSW|BM25|deduction", (ROOT / "README.md").read_text()) is None:
        problems.append("README.md missing search/retrieval description")
    return problems


def main(argv: list[str]) -> int:
    import argparse
    p = argparse.ArgumentParser(description="evidence & lexicon audit")
    p.add_argument("mode", choices=("self", "db"))
    p.add_argument("--json", action="store_true")
    a = p.parse_args(argv)

    problems = audit_self() if a.mode == "self" else audit_db()
    out = {"mode": a.mode, "ok": not problems, "problems": problems}
    if a.json:
        print(json.dumps(out, indent=2))
    else:
        from yamlout import to_yaml
        print(to_yaml(out))
    return 0 if not problems else 1


if __name__ == "__main__":
    sys.exit(main(sys.argv[1:]))