"""pinn 一次性迁移 — 全局 persona/objects → users/pinn/。"""

from __future__ import annotations

import shutil
from dataclasses import dataclass
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

import yaml

from niannian.users.paths import objects_root, persona_registry_path, user_root


@dataclass(frozen=True)
class PersonaMigrationResult:
    status: str
    user_id: str
    registry_copied: bool
    objects_migrated: int
    marker_path: str | None = None


def _now_iso() -> str:
    return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")


def migration_marker_path(home: Path, user_id: str = "pinn") -> Path:
    return user_root(home, user_id) / ".migration_persona_local"


def resolve_legacy_registry_source(home: Path, config: dict[str, Any]) -> Path | None:
    """迁移前全局 registry 候选路径（第一个存在的）。"""
    cognitive = config.get("cognitive") or {}
    candidates: list[Path] = []
    raw = cognitive.get("persona_registry")
    if raw:
        p = Path(str(raw))
        candidates.append(p)
        candidates.append(home / str(raw))
    candidates.extend(
        [
            home / "cognitive" / "persona_registry.yaml",
            home / "persona_registry.yaml",
            Path("config/cognitive/persona_registry.yaml"),
        ]
    )
    seen: set[Path] = set()
    for cand in candidates:
        resolved = cand.resolve() if cand.is_file() else cand
        if cand.is_file() and cand not in seen:
            seen.add(cand)
            return cand
    return None


def migrate_persona_to_users_pinn(
    home: Path,
    config: dict[str, Any],
    *,
    user_id: str = "pinn",
    registry_source: Path | None = None,
    dry_run: bool = False,
) -> PersonaMigrationResult:
    """Docker 一次性迁移：全局 registry + legacy objects → users/{user_id}/。"""
    home = Path(home)
    uid = str(user_id).strip() or "pinn"
    marker = migration_marker_path(home, uid)
    if marker.is_file():
        return PersonaMigrationResult(
            status="already_migrated",
            user_id=uid,
            registry_copied=False,
            objects_migrated=0,
            marker_path=str(marker),
        )

    dest_reg = persona_registry_path(home, uid)
    src_reg = registry_source or resolve_legacy_registry_source(home, config)
    registry_copied = False
    if src_reg is not None and src_reg.is_file():
        if not dry_run:
            dest_reg.parent.mkdir(parents=True, exist_ok=True)
            shutil.copy2(src_reg, dest_reg)
        registry_copied = True

    legacy_objects = home / "objects"
    dest_objects = objects_root(home, uid)
    migrated_count = 0
    if legacy_objects.is_dir():
        for child in legacy_objects.iterdir():
            if child.name.startswith("."):
                continue
            target = dest_objects / child.name
            if target.exists():
                continue
            migrated_count += 1
            if not dry_run:
                dest_objects.mkdir(parents=True, exist_ok=True)
                if child.is_dir():
                    shutil.copytree(child, target)
                else:
                    shutil.copy2(child, target)

    if not dry_run:
        marker.parent.mkdir(parents=True, exist_ok=True)
        marker.write_text(
            yaml.safe_dump(
                {
                    "migrated_at": _now_iso(),
                    "user_id": uid,
                    "registry_source": str(src_reg) if src_reg else None,
                    "registry_dest": str(dest_reg),
                    "objects_migrated": migrated_count,
                },
                allow_unicode=True,
                sort_keys=False,
            ),
            encoding="utf-8",
        )

    return PersonaMigrationResult(
        status="migrated" if not dry_run else "dry_run",
        user_id=uid,
        registry_copied=registry_copied,
        objects_migrated=migrated_count,
        marker_path=str(marker) if not dry_run else None,
    )
