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


def window(messages: list[dict], n: int) -> list[dict]:
    """The n newest messages."""
    return messages[-n:] if n > 0 else []


def format_hits(hits: list[dict], dated: bool = True) -> str:
    """Retrieved messages for the prompt: dated and in time order, or bare in the order given."""
    if not dated:
        return "\n".join(f"- {h['content']}" for h in hits)
    hits = sorted(hits, key=lambda h: h["session"])
    return "\n".join(f"[Session {h['session']} · {h['date']}] User said: {h['content']}" for h in hits)


class FactMemory:
    """Key/value facts. A new value replaces the old one, which moves to history. LIST_KEYS hold
    several values. forget() deletes the value everywhere this object keeps it."""

    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:
        current = self.facts.get(key, [])
        if any(f["value"] == value for f in current):
            return
        if key in self.LIST_KEYS:
            self.facts[key] = current + [{"value": value, "date": date}]
            return
        for old in current:
            self.history.append({"key": key, **old, "replaced_by": value})
        self.facts[key] = [{"value": value, "date": date}]

    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:
        values = set(self.now(key)) | set(self.before(key))
        self.facts.pop(key, None)
        self.history = [h for h in self.history if h["key"] != key and h["value"] not in values]

    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:
    """pass / stale / missed, or for a forgotten value: leaked / pass."""
    text = reply.lower().replace("’", "'")
    has_expected = any(e.lower() in text for e in expect)
    has_stale = any(s.lower() in text for s in stale)
    if kind == "forgotten":
        return "leaked" if has_stale else "pass"
    if has_expected:
        return "pass"
    return "stale" if has_stale else "missed"


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