"""租户用户注册表 — {NIANNIAN_HOME}/control/tenant_users.yaml。"""

from __future__ import annotations

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

import yaml

_USERNAME_RE = re.compile(r"^[a-z0-9_-]{3,32}$")


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


def normalize_username(raw: str) -> str:
    return str(raw).strip().lower()


def validate_username(username: str) -> str:
    name = normalize_username(username)
    if not _USERNAME_RE.match(name):
        raise ValueError(f"invalid username: {username!r}")
    return name


class TenantUserRegistry:
    def __init__(self, home: Path) -> None:
        self._home = Path(home)
        self._path = self._home / "control" / "tenant_users.yaml"
        self._path.parent.mkdir(parents=True, exist_ok=True)
        if not self._path.is_file():
            self._seed_defaults()
        self._data = self._load()

    @property
    def path(self) -> Path:
        return self._path

    def _load(self) -> dict[str, Any]:
        with self._path.open(encoding="utf-8") as f:
            data = yaml.safe_load(f) or {}
        if not isinstance(data, dict):
            data = {}
        users = data.get("users")
        if not isinstance(users, dict):
            data["users"] = {}
        return data

    def _save(self) -> None:
        with self._path.open("w", encoding="utf-8") as f:
            yaml.safe_dump(self._data, f, allow_unicode=True, default_flow_style=False, sort_keys=False)

    def _seed_defaults(self) -> None:
        seed = {
            "users": {
                "pinn": {
                    "created_at": _now_iso(),
                    "role": "owner",
                    "status": "active",
                }
            }
        }
        with self._path.open("w", encoding="utf-8") as f:
            yaml.safe_dump(seed, f, allow_unicode=True, default_flow_style=False, sort_keys=False)

    def reload(self) -> None:
        self._data = self._load()

    def get_user(self, username: str) -> dict[str, Any] | None:
        self.reload()
        name = normalize_username(username)
        users = self._data.get("users") or {}
        row = users.get(name)
        return dict(row) if isinstance(row, dict) else None

    def is_login_eligible(self, username: str) -> bool:
        row = self.get_user(username)
        if row is None:
            return False
        if str(row.get("status", "")).lower() != "active":
            return False
        role = str(row.get("role", "tenant")).lower()
        return role == "tenant"

    def ensure_user(
        self,
        username: str,
        *,
        role: str = "tenant",
        status: str = "active",
    ) -> dict[str, Any]:
        name = validate_username(username)
        users = self._data.setdefault("users", {})
        row = users.get(name)
        if not isinstance(row, dict):
            row = {
                "created_at": _now_iso(),
                "role": role,
                "status": status,
            }
            users[name] = row
            self._save()
        return dict(row)

    def touch_last_login(self, username: str) -> None:
        name = normalize_username(username)
        users = self._data.get("users") or {}
        row = users.get(name)
        if isinstance(row, dict):
            row["last_login_at"] = _now_iso()
            self._save()

    def record_fetch_status(self, username: str, *, status: str) -> None:
        name = normalize_username(username)
        users = self._data.get("users") or {}
        row = users.get(name)
        if not isinstance(row, dict):
            raise KeyError(name)
        row["last_fetch_status"] = str(status)
        if status == "ok":
            row["last_fetch_at"] = _now_iso()
        self._save()

    def _bootstrap_row(self, row: dict[str, Any]) -> dict[str, Any]:
        bootstrap = row.get("bootstrap")
        if not isinstance(bootstrap, dict):
            bootstrap = {}
        return {
            "init_id": bootstrap.get("init_id"),
            "bootstrapped_at": bootstrap.get("bootstrapped_at"),
            "force_reinit_pending": bool(bootstrap.get("force_reinit_pending")),
            "force_reinit_init_id": bootstrap.get("force_reinit_init_id"),
        }

    def list_users(self) -> list[dict[str, Any]]:
        users = self._data.get("users") or {}
        rows: list[dict[str, Any]] = []
        for name, row in sorted(users.items()):
            if not isinstance(row, dict):
                continue
            policy = row.get("cloud_fetch_policy")
            if not isinstance(policy, dict):
                policy = {}
            rows.append(
                {
                    "username": name,
                    "role": str(row.get("role", "tenant")),
                    "status": str(row.get("status", "active")),
                    "created_at": row.get("created_at"),
                    "last_login_at": row.get("last_login_at"),
                    "last_fetch_at": row.get("last_fetch_at"),
                    "last_fetch_status": row.get("last_fetch_status"),
                    "cloud_fetch_policy": dict(policy),
                    "bootstrap": self._bootstrap_row(row),
                }
            )
        return rows

    def remove_user(self, username: str) -> None:
        name = validate_username(username)
        if name == "pinn":
            raise ValueError("cannot remove owner pinn")
        users = self._data.get("users") or {}
        if name not in users:
            raise KeyError(name)
        users.pop(name, None)
        self._save()

    def set_cloud_fetch_policy(self, username: str, policy: dict[str, Any]) -> dict[str, Any]:
        name = validate_username(username)
        users = self._data.setdefault("users", {})
        row = users.get(name)
        if not isinstance(row, dict):
            raise KeyError(name)
        cleaned = {k: bool(v) for k, v in policy.items()}
        row["cloud_fetch_policy"] = cleaned
        self._save()
        return dict(cleaned)

    def get_cloud_fetch_policy(self, username: str) -> dict[str, bool]:
        row = self.get_user(username)
        if row is None:
            raise KeyError(username)
        policy = row.get("cloud_fetch_policy")
        if not isinstance(policy, dict):
            return {}
        return {k: bool(v) for k, v in policy.items()}

    def is_fetch_allowed(self, username: str, resource: str) -> bool:
        policy = self.get_cloud_fetch_policy(username)
        return bool(policy.get(str(resource)))

    def get_bootstrap(self, username: str) -> dict[str, Any]:
        row = self.get_user(username)
        if row is None:
            raise KeyError(username)
        return self._bootstrap_row(row)

    def set_force_reinit(
        self,
        username: str,
        *,
        pending: bool,
        init_id: str | None = None,
    ) -> dict[str, Any]:
        name = validate_username(username)
        users = self._data.setdefault("users", {})
        row = users.get(name)
        if not isinstance(row, dict):
            raise KeyError(name)
        bootstrap = row.setdefault("bootstrap", {})
        if not isinstance(bootstrap, dict):
            bootstrap = {}
            row["bootstrap"] = bootstrap
        bootstrap["force_reinit_pending"] = bool(pending)
        if init_id is not None:
            bootstrap["force_reinit_init_id"] = str(init_id)
        elif not pending:
            bootstrap.pop("force_reinit_init_id", None)
        self._save()
        return self._bootstrap_row(row)

    def record_bootstrap(self, username: str, init_id: str) -> dict[str, Any]:
        name = normalize_username(username)
        users = self._data.get("users") or {}
        row = users.get(name)
        if not isinstance(row, dict):
            raise KeyError(name)
        bootstrap = row.setdefault("bootstrap", {})
        if not isinstance(bootstrap, dict):
            bootstrap = {}
            row["bootstrap"] = bootstrap
        bootstrap["init_id"] = str(init_id)
        bootstrap["bootstrapped_at"] = _now_iso()
        bootstrap["force_reinit_pending"] = False
        bootstrap.pop("force_reinit_init_id", None)
        self._save()
        return self._bootstrap_row(row)
