"""config/self 白名单 — 念念自主改参门禁。"""

from __future__ import annotations

from pathlib import Path
from typing import Any

import yaml

from niannian.core.config import resolve_config_path
from niannian.soul.evolution_log import append_evolution

_PKG_ROOT = Path(__file__).resolve().parent.parent.parent


class EvolveForbidden(Exception):
    def __init__(self, path: str, reason: str) -> None:
        self.path = path
        self.reason = reason
        super().__init__(f"{path}: {reason}")


def _whitelist_path() -> Path:
    override = resolve_config_path().parent / "soul" / "evolve_whitelist.yaml"
    if override.is_file():
        return override
    return _PKG_ROOT / "config" / "soul" / "evolve_whitelist.yaml"


def load_evolve_whitelist() -> dict[str, Any]:
    path = _whitelist_path()
    if not path.is_file():
        return {"allowed_self_paths": [], "forbidden_self_paths": []}
    with path.open(encoding="utf-8") as f:
        data = yaml.safe_load(f) or {}
    return data if isinstance(data, dict) else {}


def assert_config_self_allowed(rel_path: str) -> None:
    name = Path(rel_path).name
    cfg = load_evolve_whitelist()
    forbidden = {str(x) for x in (cfg.get("forbidden_self_paths") or [])}
    allowed = {str(x) for x in (cfg.get("allowed_self_paths") or [])}
    if name in forbidden:
        raise EvolveForbidden(name, "forbidden_self_path")
    if allowed and name not in allowed:
        raise EvolveForbidden(name, "not_in_allowed_self_paths")


def write_config_self_file(
    subject_root: Path,
    rel_path: str,
    content: str,
    *,
    actor: str = "system",
) -> Path:
    assert_config_self_allowed(rel_path)
    target = subject_root / rel_path
    target.parent.mkdir(parents=True, exist_ok=True)
    target.write_text(content, encoding="utf-8")
    append_evolution(
        subject_root / "evolution.jsonl",
        action="config_self",
        actor=actor,
        target={"kind": "config", "id": rel_path},
        detail={"bytes": len(content.encode("utf-8"))},
    )
    return target
