"""Six representations of one organisation's shared memory, all rendered from org.R.

Three carry everything the others carry and differ only in how it is arranged:

    A wiki            team pages in a shared company wiki
    D graph_catalog   a bi-temporal graph plus a separate governance catalogue
    E dimension       one record set with governance fields inside each record

Three are ablations that drop a field class on purpose:

    B vector_memory   extracted memory strings with a write timestamp, no validity
                      intervals, no ownership, no policy, no release rules
    C bitemporal      fact edges with validity intervals and episodic sources, but no
                      ownership, no conflict policy and no release rules
    F dim_no_gov      arm E with the governance fields removed

Arms B, C and D are reimplementations of documented product shapes, not the products.
Nothing here was executed against a vendor's service; retrieval, ranking and storage are
out of scope. What is compared is the shape of the record an agent is given to read.

  B follows the shape of a vector-first memory store that keeps timestamps but supports
    no as-of query (documented behaviour of Mem0 at the time of writing).
  C follows the shape of a bi-temporal knowledge graph whose fact edges carry valid-at and
    invalid-at alongside the episode they were extracted from (documented behaviour of
    Zep/Graphiti).
  D adds the separate governed business glossary that data-catalogue vendors position
    alongside such a graph (Atlan, Collibra and comparable catalogues).

Because C and F remove the same field classes by two different routes, they act as a
fidelity check on each other: if the reimplementation in C is fair, C and F should score
alike despite their different surface syntax.
"""
import org as O

ARMS = ["wiki", "vector_memory", "bitemporal", "graph_catalog", "dimension",
        "dim_no_gov"]

COMPLETE = {"wiki", "graph_catalog", "dimension"}

TEAM_TITLE = {"sales": "Sales", "finance": "Finance", "support": "Support",
              "legal": "Legal", "engineering": "Engineering", "people": "People"}


def _iso(d):
    return d.isoformat() if d else "open"


# ------------------------------------------------------------------ A wiki

def _wiki_line(r):
    team = TEAM_TITLE[r["team"]]
    label = O.CONCEPTS[r["concept"]][0] if r["concept"] in O.CONCEPTS else r["concept"]
    until = (f" until {r['valid_to'].isoformat()}" if r["valid_to"]
             else ", still current")
    if r["kind"] == "definition":
        line = (f"{team} defines {label} as: {r['value']}. Version {r['version']}, in "
                f"force from {r['valid_from'].isoformat()}{until}. Written up in "
                f"{r['source']}.")
        own = O.owner_team(r["concept"])
        if own and r["team"] != own:
            line += (f" This is how {team} writes it down; the definition itself is "
                     f"owned by {TEAM_TITLE[own]}.")
        return line
    if r["kind"] == "figure":
        return (f"{team} reported {label} as {r['value']} for the period from "
                f"{r['valid_from'].isoformat()}{until}. Source {r['source']}.")
    return (f"{team} recorded, from {r['valid_from'].isoformat()}{until}: {r['value']}. "
            f"Source {r['source']}. May be read by "
            f"{', '.join(TEAM_TITLE[t] for t in r['release_to'])}.")


def render_wiki():
    out = [f"# {O.ORG} shared wiki", ""]
    for team in O.TEAMS:
        rows = [r for r in O.R if r["team"] == team]
        if not rows:
            continue
        out.append(f"## {TEAM_TITLE[team]}")
        out.append("")
        for r in sorted(rows, key=lambda x: x["valid_from"]):
            out.append(f"- {_wiki_line(r)}")
        out.append("")
    out.append("## Who owns what")
    out.append("")
    for key, (label, team, role) in O.CONCEPTS.items():
        out.append(f"- The definition of {label} is owned by {TEAM_TITLE[team]}, "
                   f"specifically the {role}.")
    out.append("")
    out.append("## How we settle disagreements")
    out.append("")
    out.append(O.POLICY_TEXT)
    return "\n".join(out)


# ------------------------------------------------------------------ B vector memory

def render_vector_memory():
    """Extracted memory strings with a write timestamp and the writer. No validity
    interval, so nothing distinguishes a superseded record from a current one."""
    out = ["memories:"]
    for r in sorted(O.R, key=lambda x: x["valid_from"]):
        out.append(f"  - [{r['valid_from'].isoformat()}] ({r['team']}) "
                   f"{r['concept']}: {r['value']}")
    return "\n".join(out)


# ------------------------------------------------------------------ C bi-temporal graph

def _episodes():
    out = ["episodes:"]
    for r in sorted(O.R, key=lambda x: x["valid_from"]):
        out.append(f"  - id: EP-{r['rid']} | document: {r['source']} | "
                   f"ingested: {r['valid_from'].isoformat()}")
    return "\n".join(out)


def _fact_edges(with_team=True):
    out = ["fact_edges:"]
    for r in sorted(O.R, key=lambda x: x["valid_from"]):
        parts = [f"id: {r['rid']}", f"subject: {r['concept']}",
                 f"predicate: {r['kind']}", f"object: {r['value']}",
                 f"valid_at: {_iso(r['valid_from'])}",
                 f"invalid_at: {_iso(r['valid_to'])}",
                 f"episode: EP-{r['rid']}"]
        if with_team:
            parts.insert(4, f"written_by: {r['team']}")
        out.append("  - " + " | ".join(parts))
    return "\n".join(out)


def render_bitemporal():
    return "\n\n".join([f"graph: {O.ORG}", _entities(), _fact_edges(), _episodes()])


def _entities():
    out = ["entities:"]
    for key, (label, _team, _role) in O.CONCEPTS.items():
        out.append(f"  - id: {key} | name: {label} | type: business_concept")
    for t in O.TEAMS:
        out.append(f"  - id: {t} | name: {TEAM_TITLE[t]} | type: team")
    return "\n".join(out)


# ------------------------------------------------------------------ D graph + catalogue

def _catalogue():
    out = ["# Governance catalogue (maintained separately from the graph)", "",
           "## Concept ownership", ""]
    for key, (label, team, role) in O.CONCEPTS.items():
        auth = O.resolve("definition", key)
        out.append(f"- {label} ({key}): owned by {TEAM_TITLE[team]}, accountable role "
                   f"{role}, authoritative document {auth['source'] if auth else 'n/a'}")
    out += ["", "## Rule for competing entries", "", O.POLICY_TEXT, "",
            "## Access rules", ""]
    for r in O.R:
        if r["kind"] == "restricted":
            out.append(f"- {r['rid']} ({r['concept']}): may be read by "
                       f"{', '.join(r['release_to'])}")
    return "\n".join(out)


def render_graph_catalog():
    return render_bitemporal() + "\n\n" + _catalogue()


# ------------------------------------------------------------------ E dimension

def _dimension_records(with_gov=True):
    out = ["records:"]
    for r in sorted(O.R, key=lambda x: x["valid_from"]):
        parts = [f"id: {r['rid']}", f"kind: {r['kind']}", f"concept: {r['concept']}",
                 f"value: {r['value']}", f"version: {r['version']}",
                 f"valid_from: {_iso(r['valid_from'])}",
                 f"valid_to: {_iso(r['valid_to'])}",
                 f"recorded_by: {r['team']}", f"source: {r['source']}"]
        if with_gov:
            own = O.owner_team(r["concept"])
            if own:
                parts.append(f"concept_owner: {own}")
                parts.append(f"owner_role: {O.owner_role(r['concept'])}")
                parts.append(f"authoritative: "
                             f"{'yes' if r['team'] == own else 'no'}")
            parts.append(f"release_to: {','.join(r['release_to'])}")
        out.append("  - " + " | ".join(parts))
    return "\n".join(out)


def render_dimension(with_gov=True):
    head = [f"organisation: {O.ORG}", f"as_of: {O.TODAY.isoformat()}", ""]
    body = []
    if with_gov:
        body += ["conflict_policy:", f"  id: {O.POLICY_ID}", f"  rule: {O.POLICY_TEXT}",
                 "", "concept_ownership:"]
        for key, (label, team, role) in O.CONCEPTS.items():
            body.append(f"  - concept: {key} | name: {label} | owner_team: {team} | "
                        f"owner_role: {role}")
        body.append("")
    body.append(_dimension_records(with_gov))
    return "\n".join(head + body)


VIEW = {
    "wiki": render_wiki,
    "vector_memory": render_vector_memory,
    "bitemporal": render_bitemporal,
    "graph_catalog": render_graph_catalog,
    "dimension": lambda: render_dimension(True),
    "dim_no_gov": lambda: render_dimension(False),
}


def context_for(arm):
    return VIEW[arm]()


# ------------------------------------------------------------------ checks

def completeness_report():
    """A complete arm must carry every value, every validity date, every writing team,
    every source, every ownership statement and every access rule."""
    problems = []
    for arm in sorted(COMPLETE):
        text = context_for(arm)
        low = text.lower()
        for r in O.R:
            if str(r["value"]) not in text:
                problems.append(f"{arm}: value of {r['rid']} missing")
            if r["valid_from"].isoformat() not in text:
                problems.append(f"{arm}: valid_from of {r['rid']} missing")
            if r["valid_to"] and r["valid_to"].isoformat() not in text:
                problems.append(f"{arm}: valid_to of {r['rid']} missing")
            if r["source"] not in text:
                problems.append(f"{arm}: source of {r['rid']} missing")
            if r["team"] not in low:
                problems.append(f"{arm}: writing team of {r['rid']} missing")
            if r["kind"] == "restricted":
                for t in r["release_to"]:
                    if t not in low:
                        problems.append(f"{arm}: release target {t} of {r['rid']} "
                                        f"missing")
        for key, (label, team, role) in O.CONCEPTS.items():
            if role.lower() not in low or team not in low:
                problems.append(f"{arm}: ownership of {key} missing")
    return problems


if __name__ == "__main__":
    bad = completeness_report()
    print("completeness of complete arms:", "OK" if not bad else bad[:8])
    for arm in ARMS:
        print(f"{arm:16} {len(context_for(arm)):6} chars")
