from __future__ import annotations

import json
from datetime import datetime, timezone
from pathlib import Path
from typing import TYPE_CHECKING

from niannian.memory.meta import parse_atom_meta
from niannian.personas import HIPPOCAMPUS
from niannian.soul.firewall import check_f9_sl_adopt, f9_pending_verification
from niannian.schemas.models import Atom

if TYPE_CHECKING:
    from niannian.storage.base import MemoryStore


class HippocampusDream:
    """小海 — 反思提炼与伪记忆清除（raw 不删，只降权/归档）。"""

    def __init__(self, data_dir: Path, *, store: MemoryStore | None = None) -> None:
        self._data_dir = data_dir
        self._data_dir.mkdir(parents=True, exist_ok=True)
        if store is None:
            from niannian.storage.local import LocalMemoryStore

            self._store: MemoryStore = LocalMemoryStore(data_dir.parent)
        else:
            self._store = store

    @property
    def store(self) -> MemoryStore:
        return self._store

    def dream(self, atoms: list[Atom]) -> dict:
        """按 tag + memory_scope 分队列合并摘要（F4/F5）。"""
        summaries = self._store.read_summaries()
        lifecycle = self._store.read_lifecycle()
        by_bucket: dict[str, dict[str, Any]] = {}

        for a in atoms:
            parsed = parse_atom_meta(a.meta if isinstance(a.meta, dict) else None)
            scope = parsed.memory_scope
            atom_meta = a.meta if isinstance(a.meta, dict) else None
            if scope == "subject" and not check_f9_sl_adopt(atom_meta):
                continue
            oid = parsed.object_anchor or "_unknown"
            pending = f9_pending_verification(atom_meta)
            for t in a.tags or ["未分类"]:
                key = f"object:{oid}:{t}" if scope == "object" else f"subject:{t}"
                bucket = by_bucket.setdefault(
                    key,
                    {
                        "texts": [],
                        "memory_scope": scope,
                        "object_anchor": oid if scope == "object" else None,
                        "f9_pending": False,
                    },
                )
                bucket["texts"].append(a.raw_text[:200])
                if pending:
                    bucket["f9_pending"] = True

        now = datetime.now(timezone.utc).isoformat()
        subject_dream = 0
        object_dream = 0
        for key, bucket in by_bucket.items():
            merged = " | ".join(bucket["texts"][-5:])
            entry: dict[str, Any] = {
                "text": merged,
                "updated_at": now,
                "status": "distilled",
                "memory_scope": bucket["memory_scope"],
                "subject_eligible": bucket["memory_scope"] == "subject",
                "f9_sl_adopt_ok": True,
            }
            if bucket.get("f9_pending"):
                entry["verification_status"] = "pending"
            if bucket["object_anchor"]:
                entry["object_anchor"] = bucket["object_anchor"]
            summaries[key] = entry
            if bucket["memory_scope"] == "object":
                object_dream += 1
            else:
                subject_dream += 1

        for a in atoms:
            lifecycle[a.id] = {
                "status": "distilled",
                "dream_at": now,
                "updated_at": now,
                "nas_archived": False,
            }

        self._store.write_summaries(summaries)
        self._store.write_lifecycle(lifecycle)

        from niannian.admin.marginalize import prune_marginalized_atoms

        prune_result = prune_marginalized_atoms(self._data_dir.parent)

        meta_path = self._data_dir.parent / "meta.json"
        meta = json.loads(meta_path.read_text(encoding="utf-8")) if meta_path.exists() else {}
        meta["last_dream_at"] = now
        meta_path.write_text(json.dumps(meta, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
        return {
            "tags_distilled": len(by_bucket),
            "atoms_touched": len(atoms),
            "subject_dream": subject_dream,
            "object_dream": object_dream,
            "marginalized_pruned": prune_result.get("pruned", 0),
        }

    def get_summaries(self, query: str, limit: int = 3) -> list[str]:
        summaries = self._store.read_summaries()
        q = query.lower()
        out: list[str] = []
        for tag, item in summaries.items():
            text = item.get("text", "")
            if q in tag.lower() or q in text.lower():
                out.append(f"【{tag}】{text}")
        return out[:limit]

    @staticmethod
    def trace_line(msg: str) -> str:
        return f"[{HIPPOCAMPUS}] {msg}"
