"""VP pack.yaml — stage 字段骨架。"""

from __future__ import annotations

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

import yaml

if TYPE_CHECKING:
    from niannian.virtual_persona.store import VirtualPersonaStore

PACK_NAME = "pack.yaml"

VALID_STAGES: tuple[str, ...] = ("customize", "deep_learn", "daily", "perfect")
STAGES: frozenset[str] = frozenset(VALID_STAGES)

DEFAULT_PACK: dict[str, Any] = {
    "schema_version": 1,
    "stage": "customize",
    "equipped_skills": [],
}


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


def fresh_default_pack(*, now: str | None = None) -> dict[str, Any]:
    ts = now or _now_iso()
    return {
        **DEFAULT_PACK,
        "created_at": ts,
        "updated_at": ts,
    }


def _pack_path(vp_dir: Path) -> Path:
    return vp_dir / PACK_NAME


def normalize_equipped_skills(raw: Any) -> list[str]:
    from niannian.virtual_persona.store import VirtualPersonaValidationError

    if raw is None:
        return []
    if not isinstance(raw, list):
        raise VirtualPersonaValidationError("equipped_skills must be a list")
    names: list[str] = []
    seen: set[str] = set()
    for item in raw:
        name = str(item or "").strip()
        if not name or name in seen:
            continue
        seen.add(name)
        names.append(name)
    return names


def validate_stage(stage: str) -> str:
    from niannian.virtual_persona.store import VirtualPersonaValidationError

    value = str(stage or "").strip()
    if value not in STAGES:
        raise VirtualPersonaValidationError(f"invalid stage: {stage!r}")
    return value


def read_pack(store: VirtualPersonaStore, vp_id: str) -> dict[str, Any]:
    dest = store.vp_dir(vp_id)
    path = _pack_path(dest)
    if not path.is_file():
        raise FileNotFoundError(f"pack.yaml not found for virtual persona: {vp_id}")
    with path.open(encoding="utf-8") as f:
        data = yaml.safe_load(f) or {}
    if not isinstance(data, dict):
        from niannian.virtual_persona.store import VirtualPersonaValidationError

        raise VirtualPersonaValidationError("pack.yaml must be a mapping")
    data.setdefault("schema_version", DEFAULT_PACK["schema_version"])
    data["stage"] = validate_stage(str(data.get("stage") or DEFAULT_PACK["stage"]))
    data["equipped_skills"] = normalize_equipped_skills(data.get("equipped_skills"))
    return data


def read_equipped_skills(store: VirtualPersonaStore, vp_id: str) -> list[str]:
    pack = read_pack(store, vp_id)
    return list(pack.get("equipped_skills") or [])


def patch_pack_fields(
    store: VirtualPersonaStore,
    vp_id: str,
    *,
    stage: str | None = None,
    equipped_skills: list[str] | None = None,
) -> dict[str, Any]:
    pack = read_pack(store, vp_id)
    changed = False
    if stage is not None:
        pack["stage"] = validate_stage(stage)
        changed = True
    if equipped_skills is not None:
        pack["equipped_skills"] = normalize_equipped_skills(equipped_skills)
        changed = True
    if changed:
        pack["updated_at"] = _now_iso()
        write_pack(store, vp_id, pack)
    return pack


def write_pack(store: VirtualPersonaStore, vp_id: str, data: dict[str, Any]) -> None:
    if not isinstance(data, dict):
        from niannian.virtual_persona.store import VirtualPersonaValidationError

        raise VirtualPersonaValidationError("pack data must be a mapping")
    payload = dict(data)
    if "stage" in payload:
        payload["stage"] = validate_stage(payload["stage"])
    dest = store.vp_dir(vp_id)
    dest.mkdir(parents=True, exist_ok=True)
    with _pack_path(dest).open("w", encoding="utf-8") as f:
        yaml.safe_dump(payload, f, allow_unicode=True, sort_keys=False)


def ensure_pack_on_create(store: VirtualPersonaStore, vp_id: str) -> None:
    dest = store.vp_dir(vp_id)
    path = _pack_path(dest)
    if not path.is_file():
        write_pack(store, vp_id, fresh_default_pack())
