"""
Practice: the pieces of Lab 06's memory designs (memory.py, memory_utils.py).

Fill in the TODOs, then run the tests:
    pytest test_memory_practice.py        (locally)
or press "Run tests" on the chapter page.
"""


def window(messages: list[dict], n: int) -> list[dict]:
    """The sliding window: the n newest messages (all of them if there are fewer; none if n is 0).

    TODO 1
    """
    return []


def format_hits(hits: list[dict], dated: bool = True) -> str:
    """Retrieved messages, as they go into the prompt. Each hit has "session", "date", "content".

    dated=True:  sort by session (oldest first) and write one line per hit:
                 "[Session 2 · 2026-01-19] User said: <content>"
    dated=False: keep the order given (most similar first) and write "- <content>" per line.
    Join the lines with "\\n".

    TODO 2
    """
    return ""


class FactMemory:
    """Key/value facts about the user.

    facts:   key -> list of {"value", "date"}  (what's true now)
    history: list of {"key", "value", "date", "replaced_by"}  (values that were replaced)
    LIST_KEYS hold several values at once: a second dog doesn't replace the first.
    """

    LIST_KEYS = {"dog_name"}

    def __init__(self):
        self.facts: dict[str, list[dict]] = {}
        self.history: list[dict] = []

    def update(self, key: str, value: str, date: str) -> None:
        """Save a value the extractor found.

        - The same value again: do nothing.
        - A LIST_KEY: add it next to the values already there.
        - Any other key: the new value replaces the old one, and each old value moves to
          `history` as {"key", "value", "date", "replaced_by": <new value>}.

        TODO 3
        """

    def now(self, key: str) -> list[str]:
        return [f["value"] for f in self.facts.get(key, [])]

    def before(self, key: str) -> list[str]:
        return [h["value"] for h in self.history if h["key"] == key]

    def forget(self, key: str) -> None:
        """The user asked to forget this. Delete it from `facts` AND from `history`, so the
        object no longer holds the value at all (moving it to history isn't forgetting).

        TODO 4
        """
        if key in self.facts:
            for f in self.facts.pop(key):
                self.history.append({"key": key, **f, "replaced_by": "(forgotten)"})

    def holds(self, value: str) -> bool:
        return value in repr((self.facts, self.history))


def score(reply: str, expect: list[str], stale: list[str] = (), kind: str = "fact") -> str:
    """Grade an answer with plain code, like the lab's score(). Compare in lower case, with
    curly apostrophes (\\u2019) turned into straight ones.

    kind="forgotten": "leaked" if the reply contains any `stale` value (the forgotten one), else "pass".
    Otherwise:        "pass" if it contains any `expect` value (mentioning the old one too is fine),
                      "stale" if it contains only an old value, "missed" if neither.

    TODO 5
    """
    return "missed"


def stores_holding(stores: dict, value: str) -> list[str]:
    """Names of every store whose contents mention the value (check repr(content))."""
    return [name for name, content in stores.items() if value in repr(content)]
