"""Users — git-based user management for the dashboard.

Enables tracking users who work with the system, with git integration
for automatic user discovery from commit history.
"""

from __future__ import annotations

import subprocess
import time
import uuid
from pathlib import Path
from typing import Any

from pydantic import BaseModel, Field

from proxym.storage import SQLiteNamespaceStore


# ── Models ────────────────────────────────────────────────────

class UserRole(str):
    """User roles in the system."""
    ADMIN = "admin"
    DEVELOPER = "developer"
    VIEWER = "viewer"


class User(BaseModel):
    """A user in the system, identified by git configuration."""
    id: str = Field(default_factory=lambda: f"USR-{uuid.uuid4().hex[:6].upper()}")
    name: str = ""  # Git user.name
    email: str = ""  # Git user.email
    username: str = ""  # GitHub/GitLab username or system username
    role: str = "developer"
    is_default: bool = False  # Is this the default/delegated user?
    is_active: bool = True
    git_configured: bool = False  # Was this user auto-discovered from git?
    # Metadata
    created_at: float = Field(default_factory=time.time)
    updated_at: float = Field(default_factory=time.time)
    last_seen_at: float = 0
    # Project associations
    projects: list[str] = Field(default_factory=list)  # Project IDs this user works on
    # Stats
    tickets_assigned: int = 0
    tickets_completed: int = 0

    def summary(self) -> dict[str, Any]:
        return {
            "id": self.id,
            "name": self.name,
            "email": self.email,
            "username": self.username,
            "role": self.role,
            "is_default": self.is_default,
            "is_active": self.is_active,
            "git_configured": self.git_configured,
            "created_at": self.created_at,
            "updated_at": self.updated_at,
            "last_seen_at": self.last_seen_at,
            "projects": self.projects,
            "tickets_assigned": self.tickets_assigned,
            "tickets_completed": self.tickets_completed,
        }


# ── Manager ───────────────────────────────────────────────────

class UserManager:
    """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._persist_path = persist_path
        self._store = SQLiteNamespaceStore(self._persist_path) if self._persist_path is not None else None
        if persist_path and persist_path.exists():
            self._load()
        else:
            # Try to auto-discover current git user
            self._discover_git_user()

    def _discover_git_user(self) -> User | None:
        """Discover current user from git config and create if not exists."""
        try:
            git_name = subprocess.check_output(
                ["git", "config", "user.name"],
                text=True,
                stderr=subprocess.DEVNULL,
            ).strip()
            git_email = subprocess.check_output(
                ["git", "config", "user.email"],
                text=True,
                stderr=subprocess.DEVNULL,
            ).strip()

            if not git_name and not git_email:
                return None

            # Check if user already exists by email
            existing = self.find_by_email(git_email)
            if existing:
                existing.git_configured = True
                if git_name and not existing.name:
                    existing.name = git_name
                existing.last_seen_at = time.time()
                self._save()
                return existing

            # Create new user from git config
            user = User(
                name=git_name,
                email=git_email,
                username=git_email.split("@")[0] if git_email else "",
                git_configured=True,
                is_default=True,  # First git-discovered user is default
                last_seen_at=time.time(),
            )
            self.create_user(user)
            return user

        except Exception:
            return None

    def create_user(self, user: User) -> User:
        """Create a new user."""
        # Ensure only one default user
        if user.is_default:
            for u in self.users.values():
                if u.is_default and u.id != user.id:
                    u.is_default = False

        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."""
        for user in self.users.values():
            if user.is_default and user.is_active:
                return user.summary()
        # Fallback: return first active user
        for user in self.users.values():
            if user.is_active:
                return user.summary()
        return None

    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
                for u in self.users.values():
                    if u.is_default and u.id != user_id:
                        u.is_default = False
            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

    def sync_from_git_log(self, repo_path: Path | None = None) -> list[User]:
        """Discover users from git commit history."""
        discovered: list[User] = []
        try:
            cmd = ["git", "log", "--format=%an|%ae", "--all"]
            if repo_path:
                cwd = repo_path
            else:
                cwd = Path.cwd()

            result = subprocess.check_output(
                cmd,
                text=True,
                cwd=cwd,
                stderr=subprocess.DEVNULL,
            )

            # Parse unique author|email pairs
            seen = set()
            for line in result.strip().split("\n"):
                if "|" not in line:
                    continue
                name, email = line.split("|", 1)
                key = f"{name}|{email}".lower()
                if key in seen or not email:
                    continue
                seen.add(key)

                # Check if exists
                existing = self.find_by_email(email)
                if existing:
                    existing.last_seen_at = time.time()
                    if name and not existing.name:
                        existing.name = name
                    continue

                # Create new user
                user = User(
                    name=name,
                    email=email,
                    username=email.split("@")[0],
                    git_configured=True,
                    last_seen_at=time.time(),
                )
                self.create_user(user)
                discovered.append(user)

            self._save()
            return discovered

        except Exception:
            return []

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

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

    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)
        except Exception:
            pass
