"""Reproduce SoftwareInspect's synthetic picklist-cleanup example.

Run: python3 replay.py
No network calls, credentials, CRM libraries, or real customer data.
Prints deterministic JSON to stdout. All amounts are invented USD amounts.
Integer revisions model a supported version check; they are not vendor tokens.
"""

import copy
import json
from collections import defaultdict


def summarize(records):
    groups = defaultdict(lambda: {"records": 0, "amount_usd": 0})
    for row in records:
        group = groups[row["source"] if row["source"] is not None else "(blank)"]
        group["records"] += 1
        group["amount_usd"] += row["amount_usd"]
    return dict(sorted(groups.items()))


def run():
    sources = ["partner_referral"] * 3 + ["reseller"] * 3
    sources += ["channel_partner"] * 2 + ["direct"] * 3 + [None]
    before = [
        {"id": f"D{i:02d}", "source": value, "amount_usd": i * 100, "revision": 1}
        for i, value in enumerate(sources, 1)
    ]
    mapping = {"partner_referral": "channel_partner", "reseller": "channel_partner"}
    after = copy.deepcopy(before)
    receipts = []
    for row in after:
        if row["source"] in mapping:
            receipts.append({
                "id": row["id"], "before": row["source"],
                "after": mapping[row["source"]],
                "written_revision": row["revision"] + 1,
            })
            row["source"] = mapping[row["source"]]
            row["revision"] += 1

    # A deliberately naive inverse cannot recover a many-to-one mapping.
    guessed = copy.deepcopy(after)
    for row in guessed:
        if row["source"] == "channel_partner":
            row["source"] = "partner_referral"
    wrong = [
        row["id"] for row, original in zip(guessed, before)
        if row["source"] != original["source"]
    ]

    # Legitimate activity continues after cutover: one correction and one new deal.
    live = copy.deepcopy(after)
    live[1]["source"] = "direct"
    live[1]["revision"] += 1
    live.append({"id": "D13", "source": "channel_partner", "amount_usd": 1300, "revision": 1})
    live_before_rollback = copy.deepcopy(live)
    indexed = {row["id"]: row for row in live}
    decisions = []
    for receipt in receipts:
        row = indexed[receipt["id"]]
        if row["source"] == receipt["after"] and row["revision"] == receipt["written_revision"]:
            row["source"] = receipt["before"]
            row["revision"] += 1
            decision = "restored"
        else:
            decision = "review_later_edit"
        decisions.append({"id": row["id"], "decision": decision})

    # Invariants measure the example, not any vendor's behavior or reliability.
    assert len(before) == len(after) == 12
    assert sum(r["amount_usd"] for r in before) == sum(r["amount_usd"] for r in after) == 7800
    assert len(receipts) == 6 and len(wrong) == 5
    assert summarize(after)["channel_partner"] == {"records": 8, "amount_usd": 3600}
    assert summarize(before)["partner_referral"] == {"records": 3, "amount_usd": 600}
    assert not any(r["source"] == "partner_referral" for r in after)
    assert sum(d["decision"] == "restored" for d in decisions) == 5
    assert sum(d["decision"] == "review_later_edit" for d in decisions) == 1
    assert indexed["D02"]["source"] == "direct" and indexed["D13"]["source"] == "channel_partner"
    assert len(live) == 13 and sum(r["amount_usd"] for r in live) == 9100

    return {
        "method": "Synthetic single-select source-value consolidation and guarded rollback",
        "verification_date": "2026-10-19",
        "limitations": [
            "Invented records and USD amounts; no customer or vendor account data.",
            "Single field, sequential in-memory writes, no failures or side effects.",
            "Revision integers illustrate a version check, not an actual CRM API.",
            "Restoring field values does not reverse workflow side effects or configuration changes.",
        ],
        "mapping": mapping, "before": before, "after_merge": after,
        "before_groups": summarize(before), "after_groups": summarize(after),
        "record_count_before_and_after": 12, "amount_before_and_after_usd": 7800,
        "changed_records": len(receipts), "receipts": receipts,
        "stale_partner_referral_filter": {"before_count": 3, "before_amount_usd": 600, "after_count": 0, "after_amount_usd": 0},
        "naive_inverse_wrong_ids": wrong,
        "before_guarded_rollback": live_before_rollback,
        "rollback_decisions": decisions, "after_guarded_rollback": live,
    }


if __name__ == "__main__":
    print(json.dumps(run(), indent=2, sort_keys=True))
