"""Offline teaching fixture. No HTTP, credentials, production CMS or dependencies.
Run: python cms-readback-demo.py
The JSON file is a simulated store, not a WordPress implementation.
"""
import copy
import json
from pathlib import Path
import tempfile
import unittest

BEFORE = {"id": "demo-article-1", "status": "draft", "title": "Version A", "categories": ["science", "workshop"], "episode_id": "demo-episode-1"}
PATCH = {"title": "Version B", "episode_id": "demo-episode-2"}
PROTECTED = ["id", "status", "categories"]

def write_fixture(path, mode):
    stored = copy.deepcopy(BEFORE)
    if mode == "error_body":
        path.write_text(json.dumps(stored), encoding="utf-8")
        return {"http_status": 200, "body": {"error": "Simulated denial"}}
    stored.update(PATCH)
    if mode == "ignored_field": stored["episode_id"] = BEFORE["episode_id"]
    if mode == "collateral_change": stored["categories"] = ["general"]
    if mode == "wrong_object": stored["id"] = "demo-article-2"
    path.write_text(json.dumps(stored), encoding="utf-8")
    # An optimistic acknowledgement deliberately echoes the requested values.
    return {"http_status": 200, "body": {"id": BEFORE["id"], **PATCH}}

def assess(response, persisted):
    issues = []
    if not 200 <= response["http_status"] < 300: issues.append("http_error")
    if "error" in response["body"]: issues.append("application_error")
    for key, expected in PATCH.items():
        if persisted.get(key) != expected: issues.append("not_saved:" + key)
    for key in PROTECTED:
        if persisted.get(key) != BEFORE[key]: issues.append("unexpected_change:" + key)
    return {"accepted": not issues, "issues": issues}

def scenario(mode):
    with tempfile.TemporaryDirectory(prefix="cms-readback-demo-") as folder:
        path = Path(folder) / "simulated-store.json"
        response = write_fixture(path, mode)
        # A separate file read: never validate only the write acknowledgement.
        persisted = json.loads(path.read_text(encoding="utf-8"))
        return assess(response, persisted)

class ReadbackTests(unittest.TestCase):
    def test_correct_write(self): self.assertTrue(scenario("correct")["accepted"])
    def test_ignored_field(self): self.assertIn("not_saved:episode_id", scenario("ignored_field")["issues"])
    def test_collateral_change(self): self.assertIn("unexpected_change:categories", scenario("collateral_change")["issues"])
    def test_error_inside_200(self): self.assertIn("application_error", scenario("error_body")["issues"])
    def test_wrong_object(self): self.assertIn("unexpected_change:id", scenario("wrong_object")["issues"])

if __name__ == "__main__": unittest.main(verbosity=2)
