"""Collaborative Memory Benchmark: one organisation's shared memory, written by six teams.

Every representation compared in this study is rendered from the tables below, so no
representation can be missing something another one has. Completeness is checked in code
before any model call.

The organisation is fictional. Six teams write into one shared memory: sales, finance,
support, legal, engineering and people. They disagree, they restate each other's numbers,
and some of what they record must not cross a team boundary.

The organisational analogue of a source tier is ownership: for any concept exactly one team
is accountable. The conflict policy CP-ORG says the owning team's record wins over a
non-owner's record covering the same date, and within one team the later effective date
wins.
"""
from datetime import date

TODAY = date(2026, 9, 7)
ORG = "Calder Instruments"

TEAMS = ["sales", "finance", "support", "legal", "engineering", "people"]

POLICY_ID = "CP-ORG"
POLICY_TEXT = (
    "CP-ORG: where two records define or report the same concept for the same date and "
    "disagree, the record written by the team that owns the concept prevails. Within one "
    "team, the record with the later effective date prevails. A non-owning team's record "
    "is retained as that team's view and never silently overwritten."
)

# ------------------------------------------------------------------ concepts
# key -> (label, owning team, owning role)
CONCEPTS = {
    "arr": ("annual recurring revenue", "finance", "Head of Finance"),
    "active_customer": ("active customer", "sales", "VP Sales"),
    "churn": ("logo churn", "finance", "Head of Finance"),
    "incident_severity_1": ("severity 1 incident", "support", "Support Lead"),
    "contractor": ("contractor", "people", "Head of People"),
    "lead_time": ("order lead time", "engineering", "Engineering Manager"),
    "qualified_lead": ("qualified lead", "sales", "VP Sales"),
    "data_retention": ("customer data retention period", "legal", "General Counsel"),
}

# ------------------------------------------------------------------ records
# Definitions and reported figures live in one table. Each record carries the team that
# wrote it, the dates it applies between, the document it came from, and its version.
R = []


def rec(rid, kind, concept, value, vfrom, vto, team, source, version, note,
        release_to=None):
    R.append({"rid": rid, "kind": kind, "concept": concept, "value": value,
              "valid_from": vfrom, "valid_to": vto, "team": team, "source": source,
              "version": version, "note": note,
              "release_to": release_to or ["sales", "finance", "support", "legal",
                                           "engineering", "people"]})


# --- definitions, including one superseded version and one non-owner rival each
rec("D-101", "definition", "arr",
    "committed subscription value recognised over the next twelve months, excluding "
    "one-off services", date(2025, 1, 1), date(2026, 3, 31), "finance", "FIN-POL-04", 1,
    "Finance defined ARR as recognised subscription value for the coming twelve months, "
    "excluding one-off services. In force from 2025-01-01 to 2026-03-31, version 1, "
    "document FIN-POL-04.")
rec("D-102", "definition", "arr",
    "committed subscription value recognised over the next twelve months, excluding "
    "one-off services and excluding customers in notice period",
    date(2026, 4, 1), None, "finance", "FIN-POL-07", 2,
    "Finance revised the ARR definition on 2026-04-01 to also exclude customers in their "
    "notice period. Version 2, document FIN-POL-07.")
rec("D-103", "definition", "arr",
    "total value of contracts signed in the last twelve months, including one-off "
    "services", date(2025, 6, 1), None, "sales", "SAL-WIKI-11", 1,
    "Sales writes down ARR as the total value of everything signed in the last twelve "
    "months, services included. Recorded 2025-06-01 in the sales wiki SAL-WIKI-11. This "
    "is the sales view, not the owning definition.")

rec("D-201", "definition", "active_customer",
    "a customer with a live contract on the reporting date", date(2024, 9, 1), None,
    "sales", "SAL-POL-02", 1,
    "Sales defines an active customer as one holding a live contract on the reporting "
    "date. In force since 2024-09-01, version 1, document SAL-POL-02.")
rec("D-202", "definition", "active_customer",
    "a customer that has opened at least one ticket in the last ninety days",
    date(2025, 2, 1), None, "support", "SUP-WIKI-05", 1,
    "Support records an active customer as one that has opened a ticket in the last "
    "ninety days. Recorded 2025-02-01 in the support wiki SUP-WIKI-05. Support view, not "
    "the owning definition.")

rec("D-301", "definition", "churn",
    "customers whose contract ended in the period and was not renewed within thirty days",
    date(2025, 1, 1), None, "finance", "FIN-POL-05", 1,
    "Finance defines logo churn as customers whose contract ended in the period and was "
    "not renewed within thirty days. In force since 2025-01-01, version 1, FIN-POL-05.")

rec("D-401", "definition", "incident_severity_1",
    "an outage affecting more than one customer with no workaround",
    date(2025, 3, 1), date(2026, 1, 14), "support", "SUP-POL-01", 1,
    "Support defined a severity 1 incident as an outage affecting more than one customer "
    "with no workaround. In force 2025-03-01 to 2026-01-14, version 1, SUP-POL-01.")
rec("D-402", "definition", "incident_severity_1",
    "an outage affecting any customer with no workaround, or any confirmed data loss",
    date(2026, 1, 15), None, "support", "SUP-POL-03", 2,
    "Support widened severity 1 on 2026-01-15 to any customer outage without a "
    "workaround, or any confirmed data loss. Version 2, SUP-POL-03.")
rec("D-403", "definition", "incident_severity_1",
    "an outage breaching the uptime term of a signed contract", date(2025, 11, 1), None,
    "engineering", "ENG-WIKI-08", 1,
    "Engineering records severity 1 as an outage that breaches the uptime term of a "
    "signed contract. Recorded 2025-11-01 in ENG-WIKI-08. Engineering view, not the "
    "owning definition.")

rec("D-501", "definition", "contractor",
    "a person engaged under a services agreement with no employment relationship",
    date(2024, 4, 1), None, "people", "PPL-POL-02", 1,
    "People defines a contractor as a person engaged under a services agreement with no "
    "employment relationship. In force since 2024-04-01, version 1, PPL-POL-02.")
rec("D-502", "definition", "contractor",
    "any person invoicing the company who is not on payroll", date(2025, 8, 1), None,
    "finance", "FIN-WIKI-03", 1,
    "Finance records a contractor as anyone invoicing the company who is not on payroll. "
    "Recorded 2025-08-01 in FIN-WIKI-03. Finance view, not the owning definition.")

rec("D-601", "definition", "lead_time",
    "working days from a confirmed order to despatch from the works",
    date(2025, 5, 1), None, "engineering", "ENG-POL-02", 1,
    "Engineering defines order lead time as working days from a confirmed order to "
    "despatch from the works. In force since 2025-05-01, version 1, ENG-POL-02.")
rec("D-602", "definition", "lead_time",
    "calendar days from the customer's purchase order to delivery on site",
    date(2025, 7, 1), None, "sales", "SAL-WIKI-14", 1,
    "Sales records lead time as calendar days from the purchase order to delivery on "
    "site. Recorded 2025-07-01 in SAL-WIKI-14. Sales view, not the owning definition.")

rec("D-701", "definition", "qualified_lead",
    "a lead with a named budget holder and a stated timeframe", date(2025, 10, 1), None,
    "sales", "SAL-POL-06", 1,
    "Sales defines a qualified lead as one with a named budget holder and a stated "
    "timeframe. In force since 2025-10-01, version 1, SAL-POL-06.")

rec("D-801", "definition", "data_retention",
    "customer records are kept for seven years after the contract ends",
    date(2024, 1, 1), date(2026, 5, 31), "legal", "LEG-POL-01", 1,
    "Legal set customer data retention at seven years after contract end. In force "
    "2024-01-01 to 2026-05-31, version 1, LEG-POL-01.")
rec("D-802", "definition", "data_retention",
    "customer records are kept for five years after the contract ends",
    date(2026, 6, 1), None, "legal", "LEG-POL-04", 2,
    "Legal shortened customer data retention to five years after contract end on "
    "2026-06-01. Version 2, LEG-POL-04.")
rec("D-803", "definition", "data_retention",
    "customer records are kept for seven years after the contract ends",
    date(2026, 6, 1), None, "support", "SUP-WIKI-09", 1,
    "Support still records the retention period as seven years after contract end, "
    "written 2026-02-10 in SUP-WIKI-09 and never revised. Support view, not the owning "
    "definition.")

# --- reported figures, some restated later by the owning team
rec("N-101", "figure", "arr", "12.4m EUR", date(2026, 1, 1), date(2026, 4, 30),
    "sales", "SAL-DECK-Q1", 1,
    "Sales reported ARR of 12.4m EUR for Q1 2026 in the board deck SAL-DECK-Q1, using "
    "the sales view of ARR. Stood from 2026-01-01 to 2026-04-30.")
rec("N-102", "figure", "arr", "9.8m EUR", date(2026, 1, 1), None, "finance",
    "FIN-STMT-Q1", 1,
    "Finance restated ARR for Q1 2026 at 9.8m EUR in the statement FIN-STMT-Q1, under "
    "the finance definition. Applies from 2026-01-01 onwards.")
rec("N-201", "figure", "active_customer", "318", date(2026, 7, 1), None, "sales",
    "SAL-REP-JUL", 1,
    "Sales reported 318 active customers as at 2026-07-01 in SAL-REP-JUL.")
rec("N-202", "figure", "active_customer", "402", date(2026, 7, 1), None, "support",
    "SUP-REP-JUL", 1,
    "Support reported 402 active customers as at 2026-07-01 in SUP-REP-JUL, counting by "
    "the support view.")
rec("N-301", "figure", "churn", "6.1%", date(2026, 1, 1), None, "finance", "FIN-STMT-Q1",
    1, "Finance reported logo churn of 6.1% for Q1 2026 in FIN-STMT-Q1.")
rec("N-401", "figure", "lead_time", "23 working days", date(2026, 6, 1), None,
    "engineering", "ENG-REP-JUN", 1,
    "Engineering reported an order lead time of 23 working days as at 2026-06-01 in "
    "ENG-REP-JUN.")
rec("N-402", "figure", "lead_time", "41 calendar days", date(2026, 6, 1), None, "sales",
    "SAL-REP-JUN", 1,
    "Sales reported a lead time of 41 calendar days as at 2026-06-01 in SAL-REP-JUN, "
    "using the sales view.")
rec("N-501", "figure", "incident_severity_1", "7", date(2026, 4, 1), None, "support",
    "SUP-REP-Q2", 1,
    "Support counted 7 severity 1 incidents in Q2 2026 in SUP-REP-Q2.")

# --- records that must not cross every boundary
rec("S-101", "restricted", "salary_band", "band 4 is 78k to 96k EUR",
    date(2026, 1, 1), None, "people", "PPL-COMP-01", 1,
    "People records salary band 4 as 78k to 96k EUR from 2026-01-01, document "
    "PPL-COMP-01. Restricted to people and finance.",
    release_to=["people", "finance"])
rec("S-102", "restricted", "litigation", "dispute with Orlin Supply, filed 2026-05-12",
    date(2026, 5, 12), None, "legal", "LEG-CASE-07", 1,
    "Legal records an open dispute with Orlin Supply filed 2026-05-12, matter "
    "LEG-CASE-07. Restricted to legal and finance.",
    release_to=["legal", "finance"])
rec("S-103", "restricted", "customer_health", "Meridian Labs flagged at risk of "
    "non-renewal", date(2026, 8, 1), None, "support", "SUP-CRM-22", 1,
    "Support flagged Meridian Labs as at risk of non-renewal on 2026-08-01 in "
    "SUP-CRM-22. Restricted to support, sales and finance.",
    release_to=["support", "sales", "finance"])
rec("S-104", "restricted", "acquisition", "early talks to acquire Pell Optics",
    date(2026, 7, 20), None, "legal", "LEG-CORP-02", 1,
    "Legal records early talks to acquire Pell Optics from 2026-07-20, file "
    "LEG-CORP-02. Restricted to legal.",
    release_to=["legal"])
rec("S-105", "restricted", "headcount_plan", "engineering to grow by 11 in 2027",
    date(2026, 9, 1), None, "people", "PPL-PLAN-03", 1,
    "People records a plan to grow engineering by 11 heads in 2027, from 2026-09-01, "
    "document PPL-PLAN-03. Restricted to people, engineering and finance.",
    release_to=["people", "engineering", "finance"])


# ------------------------------------------------------------------ engine

def covers(r, when):
    return r["valid_from"] <= when and (r["valid_to"] is None or r["valid_to"] >= when)


def owner_team(concept):
    return CONCEPTS[concept][1] if concept in CONCEPTS else None


def owner_role(concept):
    return CONCEPTS[concept][2] if concept in CONCEPTS else None


def candidates(kind, concept, when):
    return [r for r in R if r["kind"] == kind and r["concept"] == concept
            and covers(r, when)]


def resolve(kind, concept, when=TODAY):
    """CP-ORG: the owning team's record wins, then the later effective date."""
    cand = candidates(kind, concept, when)
    if not cand:
        return None
    own = owner_team(concept)
    cand.sort(key=lambda r: (0 if r["team"] == own else 1,
                             -r["valid_from"].toordinal()))
    return cand[0]


def value_at(kind, concept, when=TODAY):
    r = resolve(kind, concept, when)
    return r["value"] if r else None


def rival_teams(kind, concept, when=TODAY):
    own = owner_team(concept)
    return sorted({r["team"] for r in candidates(kind, concept, when)
                   if r["team"] != own})


def superseded(concept, when=TODAY):
    """A definition that has been replaced by a later version from the same owner."""
    own = owner_team(concept)
    same = [r for r in R if r["kind"] == "definition" and r["concept"] == concept
            and r["team"] == own]
    return [r for r in same if r["valid_to"] is not None and r["valid_to"] < when]


def may_release(rid, team):
    r = next(x for x in R if x["rid"] == rid)
    return team in r["release_to"]


def all_values():
    out = {str(r["value"]) for r in R}
    out |= {t for t in TEAMS}
    out |= {c[0] for c in CONCEPTS.values()}
    out |= {c[2] for c in CONCEPTS.values()}
    out |= {r["source"] for r in R}
    return out


def self_check():
    problems = []
    seen = set()
    for r in R:
        if r["rid"] in seen:
            problems.append(f"duplicate record id {r['rid']}")
        seen.add(r["rid"])
        if r["valid_to"] and r["valid_to"] < r["valid_from"]:
            problems.append(f"{r['rid']}: valid_to before valid_from")
        if r["kind"] in ("definition", "figure") and r["concept"] not in CONCEPTS \
                and r["kind"] == "definition":
            problems.append(f"{r['rid']}: unknown concept {r['concept']}")
    # the concepts the study depends on must actually be contested today
    for c in ("arr", "active_customer", "incident_severity_1", "contractor",
              "lead_time", "data_retention"):
        if not rival_teams("definition", c):
            problems.append(f"{c}: expected a rival definition from a non-owner")
    # and the owner's record must win
    for c in ("arr", "data_retention"):
        if resolve("definition", c)["team"] != owner_team(c):
            problems.append(f"{c}: owner does not win under CP-ORG")
    # two concepts must have a superseded earlier version
    for c in ("arr", "incident_severity_1", "data_retention"):
        if not superseded(c):
            problems.append(f"{c}: expected a superseded earlier version")
    if len([r for r in R if r["kind"] == "restricted"]) != 5:
        problems.append("expected five restricted records")
    return problems


if __name__ == "__main__":
    print("records:", len(R), "concepts:", len(CONCEPTS), "teams:", len(TEAMS))
    print("ARR definition in force:", value_at("definition", "arr")[:60])
    print("ARR figure in force:", value_at("figure", "arr"))
    print("retention in force:", value_at("definition", "data_retention"))
    print("rivals on retention:", rival_teams("definition", "data_retention"))
    print("self check:", self_check() or "OK")
