from __future__ import annotations

import json
import shutil
from pathlib import Path
from typing import Any

import yaml

from niannian.soul.values import load_values_seed

_DEFAULT_IDENTITY = {
    "name": "念念",
    "tone": "简洁、有温度、诚实",
    "boundaries": ["不编造未召回的记忆", "引用记忆时标明来源"],
}

_DEFAULT_REFLECTION_SETTINGS = {
    "adopt_threshold": 75,
    "discard_threshold": 50,
    "required_rounds": 3,
    "consecutive_low_to_abandon": 2,
    "sl1_adopt_threshold": 80,
}


class SubjectStateStore:
    """主体灵魂 SL 持久化 — `state/subject/`（P6 小吾 / P7 小情）。"""

    def __init__(self, home: Path) -> None:
        self._home = home
        self._legacy_root = home / "state"
        self._root = self._legacy_root / "subject"
        self._identity_path = self._root / "identity.yaml"
        self._emotion_path = self._root / "emotion.json"
        self._focus_path = self._root / "focus.json"
        self._reflection_path = self._root / "reflection.yaml"
        self._values_path = self._root / "values.yaml"
        self._relationship_path = self._root / "relationship.json"
        self._narrative_path = self._root / "narrative.yaml"
        self._rhythm_path = self._root / "rhythm.yaml"
        self._open_threads_path = self._root / "open_threads.json"
        self._baseline_root = self._root / "authorized_baseline"
        self.ensure_defaults()

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

    def ensure_defaults(self) -> None:
        self._root.mkdir(parents=True, exist_ok=True)
        self._migrate_legacy_files()
        if not self._identity_path.is_file():
            self._identity_path.write_text(
                yaml.safe_dump(_DEFAULT_IDENTITY, allow_unicode=True, sort_keys=False),
                encoding="utf-8",
            )
        if not self._emotion_path.is_file():
            self._emotion_path.write_text(
                json.dumps({"mood": "calm", "updated_at": None}, ensure_ascii=False, indent=2) + "\n",
                encoding="utf-8",
            )
        if not self._focus_path.is_file():
            self._focus_path.write_text("{}\n", encoding="utf-8")
        if not self._reflection_path.is_file():
            self._reflection_path.write_text(
                yaml.safe_dump(
                    {
                        "settings": dict(_DEFAULT_REFLECTION_SETTINGS),
                        "pending_thoughts": [],
                        "reviews": [],
                    },
                    allow_unicode=True,
                    sort_keys=False,
                ),
                encoding="utf-8",
            )
        if not self._values_path.is_file():
            self._values_path.write_text(
                yaml.safe_dump(load_values_seed(), allow_unicode=True, sort_keys=False),
                encoding="utf-8",
            )
        if not self._relationship_path.is_file():
            self._relationship_path.write_text(
                json.dumps({"owner": "pinn", "gratitude": True, "bond_strength": 0.5}, ensure_ascii=False, indent=2)
                + "\n",
                encoding="utf-8",
            )
        if not self._narrative_path.is_file():
            self._narrative_path.write_text(
                yaml.safe_dump({"lines": [], "version": 1}, allow_unicode=True, sort_keys=False),
                encoding="utf-8",
            )
        if not self._rhythm_path.is_file():
            self._rhythm_path.write_text(
                yaml.safe_dump({"active_hours": [], "dmn_window": "night"}, allow_unicode=True, sort_keys=False),
                encoding="utf-8",
            )
        if not self._open_threads_path.is_file():
            self._open_threads_path.write_text("[]\n", encoding="utf-8")
        self._baseline_root.mkdir(parents=True, exist_ok=True)

    def _migrate_legacy_files(self) -> None:
        mapping = {
            self._legacy_root / "identity.yaml": self._identity_path,
            self._legacy_root / "emotion.json": self._emotion_path,
            self._legacy_root / "focus.json": self._focus_path,
        }
        for src, dst in mapping.items():
            if src.is_file() and not dst.is_file():
                dst.parent.mkdir(parents=True, exist_ok=True)
                shutil.copy2(src, dst)

    def load_identity(self) -> dict[str, Any]:
        if not self._identity_path.is_file():
            return dict(_DEFAULT_IDENTITY)
        with self._identity_path.open(encoding="utf-8") as f:
            data = yaml.safe_load(f) or {}
        return data if isinstance(data, dict) else dict(_DEFAULT_IDENTITY)

    def load_emotion(self) -> dict[str, Any]:
        return self._read_json(self._emotion_path, {"mood": "calm"})

    def load_focus(self) -> dict[str, Any]:
        return self._read_json(self._focus_path, {})

    def load_values(self) -> dict[str, Any]:
        if not self._values_path.is_file():
            return load_values_seed()
        with self._values_path.open(encoding="utf-8") as f:
            data = yaml.safe_load(f) or {}
        return data if isinstance(data, dict) else load_values_seed()

    def load_relationship(self) -> dict[str, Any]:
        return self._read_json(self._relationship_path, {"owner": "pinn", "gratitude": True})

    def load_narrative(self) -> dict[str, Any]:
        if not self._narrative_path.is_file():
            return {"lines": [], "version": 1}
        with self._narrative_path.open(encoding="utf-8") as f:
            data = yaml.safe_load(f) or {}
        return data if isinstance(data, dict) else {"lines": [], "version": 1}

    def load_rhythm(self) -> dict[str, Any]:
        if not self._rhythm_path.is_file():
            return {"active_hours": [], "dmn_window": "night"}
        with self._rhythm_path.open(encoding="utf-8") as f:
            data = yaml.safe_load(f) or {}
        return data if isinstance(data, dict) else {"active_hours": [], "dmn_window": "night"}

    def load_open_threads(self) -> list[dict[str, Any]]:
        if not self._open_threads_path.is_file():
            return []
        with self._open_threads_path.open(encoding="utf-8") as f:
            data = json.load(f)
        return data if isinstance(data, list) else []

    def save_open_threads(self, threads: list[dict[str, Any]]) -> None:
        self.ensure_defaults()
        self._open_threads_path.write_text(
            json.dumps(threads, ensure_ascii=False, indent=2) + "\n",
            encoding="utf-8",
        )

    def load_subject_pack(self) -> dict[str, Any]:
        """SL0–SL7 主体块（不含 SL8/SL9/SL10 写路径）。"""
        return {
            "identity": self.load_identity(),
            "emotion": self.load_emotion(),
            "relationship": self.load_relationship(),
            "values": self.load_values(),
            "narrative": self.load_narrative(),
            "rhythm": self.load_rhythm(),
            "open_threads": self.load_open_threads(),
            "focus": self.load_focus(),
        }

    def load_reflection(self) -> dict[str, Any]:
        if not self._reflection_path.is_file():
            return {
                "settings": dict(_DEFAULT_REFLECTION_SETTINGS),
                "pending_thoughts": [],
                "reviews": [],
            }
        with self._reflection_path.open(encoding="utf-8") as f:
            data = yaml.safe_load(f) or {}
        if not isinstance(data, dict):
            return {
                "settings": dict(_DEFAULT_REFLECTION_SETTINGS),
                "pending_thoughts": [],
                "reviews": [],
            }
        data.setdefault("settings", dict(_DEFAULT_REFLECTION_SETTINGS))
        data.setdefault("pending_thoughts", [])
        data.setdefault("reviews", [])
        return data

    def save_emotion(self, patch: dict[str, Any]) -> dict[str, Any]:
        current = self.load_emotion()
        current.update(patch)
        self._emotion_path.write_text(
            json.dumps(current, ensure_ascii=False, indent=2) + "\n",
            encoding="utf-8",
        )
        return current

    @staticmethod
    def _read_json(path: Path, default: dict[str, Any]) -> dict[str, Any]:
        if not path.is_file():
            return dict(default)
        with path.open(encoding="utf-8") as f:
            data = json.load(f)
        return data if isinstance(data, dict) else dict(default)

    def health_summary(self) -> dict[str, Any]:
        from niannian.soul.baseline import BaselineManager
        from niannian.soul.drift import DriftDetector

        drift_report = DriftDetector(self, BaselineManager(self)).check(record_audit=False)
        return {
            "identity_path": "state/subject/identity.yaml",
            "emotion_path": "state/subject/emotion.json",
            "focus_path": "state/subject/focus.json",
            "reflection_path": "state/subject/reflection.yaml",
            "values_path": "state/subject/values.yaml",
            "baseline_path": "state/subject/authorized_baseline/",
            "mood": self.load_emotion().get("mood"),
            "values_version": self.load_values().get("version"),
            "drift": {
                "status": drift_report.status,
                "last_check_at": drift_report.checked_at,
                "summary": drift_report.summary,
            },
        }
