"""UserManager — in-memory user store with SQLite persistence and git integration."""

from __future__ import annotations

from pathlib import Path
from typing import Any

import time

import structlog

from proxym.storage import SQLiteNamespaceStore
from proxym.dashboard.users._models import User, UserRole
from proxym.dashboard.users._git_support import GitUserIdentityMixin
from proxym.dashboard.users._git_sync import GitSyncMixin
from proxym.dashboard.users._permissions import ensure_single_default, resolve_default_user


logger = structlog.get_logger()


class UserManager(GitSyncMixin, GitUserIdentityMixin):
    """In-memory user store with SQLite persistence and git integration."""

    _instance: UserManager | None = None

    @classmethod
    def get(cls, persist_path: Path | None = None) -> UserManager:
        """Get singleton instance."""
        if cls._instance is None:
            cls._instance = cls(persist_path)
        return cls._instance

    @classmethod
    def reset(cls) -> None:
        """Reset singleton (mainly for tests)."""
        cls._instance = None

    def __init__(self, persist_path: Path | None = None):
        self.users: dict[str, User] = {}
        self._git_sync_meta: dict[str, Any] = {}
        self._persist_path = persist_path
        self._store = SQLiteNamespaceStore(self._persist_path) if self._persist_path is not None else None
        # Handle both Path objects and string paths (e.g., ":memory:")
        if persist_path and (persist_path.exists() if hasattr(persist_path, 'exists') else persist_path != ":memory:"):
            self._load()
        if not self.users and not self._git_sync_meta.get("last_sync_at"):
            # First-ever run with empty DB: seed from git config (cheap)
            self._discover_git_user()

    def create_user(self, user: User) -> User:
        """Create a new user."""
        # Ensure only one default user
        if user.is_default:
            ensure_single_default(self.users, user.id)

        self.users[user.id] = user
        self._save()
        return user

    def get_user(self, user_id: str) -> User | None:
        """Get user by ID."""
        return self.users.get(user_id)

    def find_by_email(self, email: str) -> User | None:
        """Find user by email."""
        for user in self.users.values():
            if user.email.lower() == email.lower():
                return user
        return None

    def find_by_username(self, username: str) -> User | None:
        """Find user by username."""
        for user in self.users.values():
            if user.username.lower() == username.lower():
                return user
        return None

    def list_users(self, active_only: bool = False) -> list[User]:
        """List all users."""
        result = list(self.users.values())
        if active_only:
            result = [u for u in result if u.is_active]
        # Sort by default first, then by name
        result.sort(key=lambda u: (not u.is_default, u.name or u.username or u.id))
        return result

    def get_default_user(self) -> dict[str, Any] | None:
        """Get the default/delegated user."""
        return resolve_default_user(self.users)

    def update_user(self, user_id: str, updates: dict[str, Any]) -> User | None:
        """Update user fields."""
        user = self.users.get(user_id)
        if not user:
            return None

        for key, value in updates.items():
            if key == "is_default" and value is True:
                # Unset default on other users
                ensure_single_default(self.users, user_id)
            if hasattr(user, key):
                setattr(user, key, value)

        user.updated_at = time.time()
        self._save()
        return user

    def delete_user(self, user_id: str) -> bool:
        """Delete a user."""
        if user_id in self.users:
            del self.users[user_id]
            self._save()
            return True
        return False

    # ── Persistence ───────────────────────────────────────────

    def _save(self):
        if not self._persist_path:
            return
        if self._store is None:
            self._store = SQLiteNamespaceStore(self._persist_path)
        self._store.save_namespaces({
            "users": {uid: u.model_dump() for uid, u in self.users.items()},
            "users_meta": {"git_sync": self._git_sync_meta} if self._git_sync_meta else {},
        })

    def _load(self):
        if not self._persist_path or not self._persist_path.exists():
            return
        try:
            data = self._store.load_namespace("users") if self._store else {}
            for uid, udata in data.items():
                self.users[uid] = User(**udata)
            meta = self._store.load_namespace("users_meta") if self._store else {}
            self._git_sync_meta = meta.get("git_sync", {})
        except Exception:
            pass
