"""OT 租户存储 — profile · disclosure_index · relationship。"""

from __future__ import annotations

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

from niannian.memory.meta import parse_atom_meta
from niannian.users.paths import objects_root

_OBJECT_ID_RE = re.compile(r"^[\w\u4e00-\u9fff\-]{1,64}$")


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


def _sanitize_object_id(object_id: str) -> str:
    oid = object_id.strip()
    if not oid or not _OBJECT_ID_RE.match(oid):
        raise ValueError(f"invalid object_id: {object_id!r}")
    return oid


class ObjectTenantStore:
    """users/{user_id}/objects/{id}/ — OT0–OT2（v0.5）· per-user（v0.11.1）。"""

    def __init__(self, home: Path, user_id: str = "pinn") -> None:
        self._home = Path(home)
        self._user_id = str(user_id).strip() or "pinn"
        self._root = objects_root(self._home, self._user_id)

    @property
    def user_id(self) -> str:
        return self._user_id

    @property
    def objects_root(self) -> Path:
        return self._root

    def object_dir(self, object_id: str) -> Path:
        return self._root / _sanitize_object_id(object_id)

    def ensure_object(self, object_id: str) -> Path:
        dest = self.object_dir(object_id)
        dest.mkdir(parents=True, exist_ok=True)
        profile = dest / "profile.json"
        if not profile.is_file():
            profile.write_text(
                json.dumps({"id": _sanitize_object_id(object_id), "label": object_id}, ensure_ascii=False, indent=2)
                + "\n",
                encoding="utf-8",
            )
        rel = dest / "relationship.json"
        if not rel.is_file():
            rel.write_text(json.dumps({"affinity": "neutral"}, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
        idx = dest / "disclosure_index.json"
        if not idx.is_file():
            idx.write_text(json.dumps({"atom_ids": [], "updated_at": None}, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
        persona = dest / "persona.yaml"
        if not persona.is_file():
            persona.write_text("default_preset: null\n", encoding="utf-8")
        return dest

    def load_disclosure_index(self, object_id: str) -> dict[str, Any]:
        self.ensure_object(object_id)
        path = self.object_dir(object_id) / "disclosure_index.json"
        with path.open(encoding="utf-8") as f:
            data = json.load(f)
        return data if isinstance(data, dict) else {"atom_ids": []}

    def register_disclosed_atom(self, object_id: str, atom_id: str, meta: dict[str, Any] | None) -> bool:
        """F6：无 told_to 则不写入 OT1 索引。"""
        parsed = parse_atom_meta(meta)
        if not parsed.disclosure.told_to:
            return False
        if parsed.object_anchor and parsed.object_anchor != _sanitize_object_id(object_id):
            return False
        self.ensure_object(object_id)
        idx_path = self.object_dir(object_id) / "disclosure_index.json"
        index = self.load_disclosure_index(object_id)
        atom_ids: list[str] = list(index.get("atom_ids") or [])
        if atom_id not in atom_ids:
            atom_ids.append(atom_id)
        index["atom_ids"] = atom_ids
        index["updated_at"] = _now_iso()
        idx_path.write_text(json.dumps(index, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
        return True

    def load_object_pack(self, object_id: str) -> dict[str, Any]:
        """OT 档案 + 索引（非 atom 正文）。"""
        dest = self.ensure_object(object_id)
        profile = json.loads((dest / "profile.json").read_text(encoding="utf-8"))
        relationship = json.loads((dest / "relationship.json").read_text(encoding="utf-8"))
        return {
            "object_id": _sanitize_object_id(object_id),
            "profile": profile,
            "relationship": relationship,
            "disclosure_index": self.load_disclosure_index(object_id),
            "persona": self.load_persona(object_id),
        }

    def load_persona(self, object_id: str) -> dict[str, Any]:
        """OT4 — objects/{id}/persona.yaml。"""
        dest = self.ensure_object(object_id)
        path = dest / "persona.yaml"
        if not path.is_file():
            return {"default_preset": None}
        import yaml

        with path.open(encoding="utf-8") as f:
            data = yaml.safe_load(f) or {}
        if not isinstance(data, dict):
            return {"default_preset": None}
        preset = data.get("default_preset")
        if preset is None or (isinstance(preset, str) and not preset.strip()):
            return {"default_preset": None}
        return {"default_preset": str(preset).strip()}

    def save_persona(self, object_id: str, doc: dict[str, Any]) -> dict[str, Any]:
        """OT4 写入 — 仅 persona.yaml，不写 subject。"""
        dest = self.ensure_object(object_id)
        preset = doc.get("default_preset")
        if preset is None or (isinstance(preset, str) and not str(preset).strip()):
            normalized: dict[str, Any] = {"default_preset": None}
        else:
            normalized = {"default_preset": str(preset).strip()}
        import yaml

        path = dest / "persona.yaml"
        path.write_text(yaml.safe_dump(normalized, allow_unicode=True, sort_keys=False), encoding="utf-8")
        return normalized
