from __future__ import annotations

import json
import re
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

NIANNIAN_CORE_SPACE = "global"

CHAT_MODE_SPACES: dict[str, str] = {
    "auto": "auto",
    "default": "normal",
    "work": "work",
    "companion": "companion",
    "education": "education",
}

# 内置空间不可从注册表删除（碎片数据仍保留在 NAS）
PROTECTED_MEMORY_SPACES: frozenset[str] = frozenset(
    {"global", "auto", "normal", "work", "companion", "education"}
)

_BUILTIN_REGISTRY_SEED: tuple[str, ...] = (
    "global",
    "auto",
    "normal",
    "work",
    "companion",
    "education",
)

_SPACE_RE = re.compile(r"^[a-z][a-z0-9_-]{0,63}$")


class MemorySpaceError(ValueError):
    """记忆空间参数无效或缺失。"""


def _utc_now_iso() -> str:
    return datetime.now(timezone.utc).isoformat()


def normalize_memory_space(name: str) -> str:
    raw = str(name or "").strip()
    if not raw:
        raise MemorySpaceError("memory_space_required")
    if not _SPACE_RE.match(raw):
        raise MemorySpaceError(f"invalid memory_space: {raw}")
    return raw


def require_mcp_memory_space(project: str | None) -> str:
    """MCP 调用必须显式指定记忆空间，无默认。"""
    if project is None or not str(project).strip():
        raise MemorySpaceError(
            "MCP 调用必须指定 memory_space（project 参数）；无默认记忆空间"
        )
    return normalize_memory_space(project)


def memory_space_for_chat_mode(mode: str | None) -> str:
    key = str(mode or "auto").strip().lower()
    return CHAT_MODE_SPACES.get(key, "work")


def _registry_path(home: Path) -> Path:
    return home / "persist" / "memory_spaces.json"


def read_memory_space_registry(home: Path) -> dict[str, Any]:
    path = _registry_path(home)
    if not path.is_file():
        return {"spaces": [NIANNIAN_CORE_SPACE], "updated_at": None}
    try:
        data = json.loads(path.read_text(encoding="utf-8"))
    except json.JSONDecodeError:
        data = {}
    spaces = sorted(set(data.get("spaces") or []) | {NIANNIAN_CORE_SPACE} | set(_BUILTIN_REGISTRY_SEED))
    return {"spaces": spaces, "updated_at": data.get("updated_at")}


def ensure_memory_space(home: Path, name: str) -> str:
    """注册记忆空间；不存在则新建。"""
    space = normalize_memory_space(name)
    path = _registry_path(home)
    path.parent.mkdir(parents=True, exist_ok=True)
    data = read_memory_space_registry(home)
    spaces = set(data.get("spaces") or [])
    if space not in spaces:
        spaces.add(space)
        data["spaces"] = sorted(spaces)
        data["updated_at"] = _utc_now_iso()
        path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
    return space


def delete_memory_space(home: Path, name: str) -> str:
    """从注册表移除记忆空间（不删 NAS 内已有碎片）。"""
    space = normalize_memory_space(name)
    if space in PROTECTED_MEMORY_SPACES:
        raise MemorySpaceError(f"protected memory_space: {space}")
    path = _registry_path(home)
    data = read_memory_space_registry(home)
    spaces = set(data.get("spaces") or [])
    if space not in spaces:
        raise MemorySpaceError(f"memory_space_not_found: {space}")
    spaces.remove(space)
    data["spaces"] = sorted(spaces)
    data["updated_at"] = _utc_now_iso()
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
    return space
