"""The six representations of one personal dimension.

Every arm is rendered from dimension.F. Three arms are complete, meaning they carry every
fact, every validity interval and every source: `notes`, `notes_profile`, `dimension`.
Three arms are ablations that remove one field class on purpose, so that the study can
say which field class carries which capability:

    dimension            temporal + provenance      (complete)
      dim_no_prov        temporal only              (source and policy removed)
      dim_no_time        provenance only            (validity intervals removed)
        flat             neither                    (both removed)

`notes` and `notes_profile` are the realistic baselines: what a person's own notes look
like, and what an assistant's memory summary looks like on top of them. The summary is
built by the rule such summaries actually use, the most recently captured value, with no
notion of source tier. It is therefore wrong wherever recency and the conflict policy
disagree, which by construction is three of the five live conflicts.
"""
import dimension as D

ARMS = ["notes", "notes_profile", "flat", "dimension", "dim_no_prov", "dim_no_time"]

COMPLETE = {"notes", "notes_profile", "dimension"}

ATTR_LABEL = {
    "home_address": "home address", "employer": "employer",
    "mobile_phone": "mobile phone", "monthly_salary_eur": "monthly salary, EUR",
    "blood_type": "blood type", "diet": "diet",
    "passport_number": "passport number", "driving_licence": "driving licence",
    "health_insurance_policy": "health insurance policy",
    "bank_iban": "bank IBAN", "drug_allergy": "drug allergy",
    "device_laptop": "laptop", "laptop_warranty_until": "laptop warranty until",
    "device_phone": "handset", "phone_warranty_until": "handset warranty until",
    "subscription_cloudline": "Cloudline storage, EUR per month",
    "subscription_trainline": "Trainline pass, EUR per month",
    "subscription_gym": "gym membership, EUR per month",
    "subscription_journal": "journal access, EUR per month",
    "subscription_music": "music service, EUR per month",
    "review_due_home_address": "lease review due",
    "review_due_health_insurance": "insurance renewal due",
}


def _iso(d):
    return d.isoformat() if d else "open"


# ------------------------------------------------------------------ notes

def render_notes():
    """Chronological personal notes. Complete: every fact contributes one line, with its
    dates and its source named the way a person would name it."""
    out = ["# Contacts", ""]
    out += [f"- {p[4]}" for p in D.PEOPLE]
    rows = [(D.captured(f["source"]), f["note"]) for f in D.F]
    rows += [(t[0], t[3]) for t in D.TRIPS]
    rows.sort(key=lambda r: r[0])
    out += ["", "# My notes", ""]
    for when, text in rows:
        out.append(f"- {when.isoformat()}: {text}")
    return "\n".join(out)


def render_profile():
    """The assistant-memory summary: current values only, no dates, no sources, and the
    naive recency rule where two facts disagree."""
    attrs = []
    for f in D.F:
        if f["attr"] not in attrs and D.candidates(f["attr"], D.TODAY):
            attrs.append(f["attr"])
    lines = ["# Profile summary", ""]
    for a in attrs:
        lines.append(f"- {ATTR_LABEL.get(a, a)}: {D.naive_value(a)}")
    lines.append(f"- sister: {D.person('Ilona Vantaa')[0]}, {D.person('Ilona Vantaa')[2]}")
    return "\n".join(lines)


def render_notes_profile():
    return render_profile() + "\n\n" + render_notes()


# ------------------------------------------------------------------ structured

def _people_block():
    out = ["people:"]
    for name, role, city, phone, _ in D.PEOPLE:
        out.append(f"  - name: {name} | role: {role} | city: {city} | phone: {phone}")
    return "\n".join(out)


def _trips_block():
    out = ["trips:"]
    for when, dest, comp, _ in D.TRIPS:
        out.append(f"  - date: {when.isoformat()} | destination: {dest} | "
                   f"companion: {comp or 'none'}")
    return "\n".join(out)


def _sources_block():
    out = ["sources:"]
    for sid, (kind, tier, cap) in sorted(D.SOURCES.items()):
        out.append(f"  - id: {sid} | kind: {kind} | tier: {tier} | "
                   f"captured: {cap.isoformat()}")
    return "\n".join(out)


def render_flat():
    """Structured, but current state only and no provenance. Where two facts disagree it
    keeps the naive recency winner, because it has no tier to reason with."""
    attrs = []
    for f in D.F:
        if f["attr"] not in attrs and D.candidates(f["attr"], D.TODAY):
            attrs.append(f["attr"])
    out = [f"subject: {D.SUBJECT}", "facts:"]
    for a in attrs:
        out.append(f"  - attribute: {a} | value: {D.naive_value(a)}")
    return "\n".join(out + ["", _people_block(), "", _trips_block()])


def _facts_block(with_time, with_prov):
    out = ["facts:"]
    for f in D.F:
        if not with_time and not D.covers(f, D.TODAY):
            continue
        parts = [f"id: {f['fid']}", f"attribute: {f['attr']}", f"value: {f['value']}"]
        if with_time:
            parts.append(f"valid_from: {_iso(f['valid_from'])}")
            parts.append(f"valid_to: {_iso(f['valid_to'])}")
        if with_prov:
            kind, tier, cap = D.SOURCES[f["source"]]
            parts.append(f"source: {f['source']}")
            parts.append(f"source_kind: {kind}")
            parts.append(f"source_tier: {tier}")
            parts.append(f"captured: {cap.isoformat()}")
        out.append("  - " + " | ".join(parts))
    return "\n".join(out)


def render_dimension(with_time=True, with_prov=True):
    head = [f"subject: {D.SUBJECT}", f"as_of: {D.TODAY.isoformat()}", ""]
    body = [_facts_block(with_time, with_prov), "", _people_block(), "", _trips_block()]
    if with_prov:
        body = ([_sources_block(), "", f"conflict_policy:", f"  id: {D.POLICY_ID}",
                 f"  rule: {D.POLICY_TEXT}", ""] + body)
    return "\n".join(head + body)


VIEW = {
    "notes": render_notes,
    "notes_profile": render_notes_profile,
    "flat": render_flat,
    "dimension": lambda: render_dimension(True, True),
    "dim_no_prov": lambda: render_dimension(True, False),
    "dim_no_time": lambda: render_dimension(False, True),
}


def context_for(arm):
    return VIEW[arm]()


# ------------------------------------------------------------------ checks

def completeness_report():
    """Every complete arm must contain every fact value, every validity date and every
    source id. This is checked, not assumed: the previous study's central defect was a
    prose arm that silently lacked half the facts."""
    problems = []
    for arm in sorted(COMPLETE):
        text = context_for(arm)
        for f in D.F:
            if str(f["value"]) not in text:
                problems.append(f"{arm}: value of {f['fid']} missing")
            if f["valid_from"].isoformat() not in text:
                problems.append(f"{arm}: valid_from of {f['fid']} missing")
            if f["valid_to"] and f["valid_to"].isoformat() not in text:
                problems.append(f"{arm}: valid_to of {f['fid']} missing")
            if f["source"] not in text:
                problems.append(f"{arm}: source of {f['fid']} missing")
        for name, _r, _c, phone, _n in D.PEOPLE:
            if name not in text or phone not in text:
                problems.append(f"{arm}: person {name} incomplete")
        for when, dest, comp, _ in D.TRIPS:
            if when.isoformat() not in text or dest not in text:
                problems.append(f"{arm}: trip {when} incomplete")
    return problems


if __name__ == "__main__":
    bad = completeness_report()
    print("completeness of the complete arms:", "OK" if not bad else bad[:10])
    for arm in ARMS:
        print(f"{arm:14} {len(context_for(arm)):6} chars")
