"""
用户服务

提供用户信息的获取、创建和认证功能。
支持从数据库和缓存获取用户信息。
"""

import logging
import json
from typing import Optional, Dict, Any
from datetime import datetime
from passlib.context import CryptContext

from sqlmodel import Session, select
from src.auth.models import UserAuth
from src.config.settings import REDIS_ENABLED
from src.config.database import engine
from src.models.entities import UserDB
from src.services.session_service import session_service

logger = logging.getLogger(__name__)

# 密码哈希上下文
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")


class UserService:
    """用户服务"""

    _instance = None
    _redis_client = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    async def _get_redis(self):
        """Lazy initialize Redis connection"""
        if not REDIS_ENABLED:
            return None
        if self._redis_client is None:
            try:
                import redis.asyncio as aioredis
                from src.config.settings import REDIS_URL
                self._redis_client = await aioredis.from_url(
                    REDIS_URL,
                    encoding="utf-8",
                    decode_responses=True
                )
            except Exception as e:
                logger.warning(f"Redis not available for user service: {e}")
                return None
        return self._redis_client

    def _verify_password(self, plain_password: str, hashed_password: str) -> bool:
        """验证密码"""
        return pwd_context.verify(plain_password, hashed_password)

    def _hash_password(self, password: str) -> str:
        """哈希密码"""
        return pwd_context.hash(password)

    def _user_db_to_auth(self, user_db: UserDB) -> UserAuth:
        """将 UserDB 转换为 UserAuth"""
        return UserAuth(
            id=user_db.id,
            username=user_db.username,
            email=user_db.email,
            avatar_url=user_db.avatar_url,
            is_active=user_db.is_active,
            is_superuser=user_db.is_superuser,
            permissions=user_db.permissions or [],
            roles=user_db.roles or [],
            created_at=user_db.created_at,
            last_login=user_db.last_login
        )

    async def get_user_by_id(self, user_id: str) -> Optional[UserAuth]:
        """
        根据用户ID获取用户信息

        优先从缓存获取，缓存未命中则从数据库获取。
        """
        # 1. 尝试从缓存获取
        user = await self._get_user_from_cache(user_id)
        if user:
            return user

        # 2. 从数据库获取
        with Session(engine) as session:
            user_db = session.exec(select(UserDB).where(UserDB.id == user_id)).first()
            if user_db:
                user = self._user_db_to_auth(user_db)
                # 写入缓存
                await self._cache_user(user)
                return user

        return None

    async def get_user_by_username(self, username: str) -> Optional[UserAuth]:
        """根据用户名获取用户"""
        with Session(engine) as session:
            user_db = session.exec(
                select(UserDB).where(UserDB.username == username)
            ).first()
            if user_db:
                return self._user_db_to_auth(user_db)
        return None

    async def get_user_by_email(self, email: str) -> Optional[UserAuth]:
        """根据邮箱获取用户"""
        with Session(engine) as session:
            user_db = session.exec(
                select(UserDB).where(UserDB.email == email)
            ).first()
            if user_db:
                return self._user_db_to_auth(user_db)
        return None

    async def update_last_login(self, user_id: str) -> None:
        """更新用户最后登录时间"""
        with Session(engine) as session:
            user_db = session.exec(
                select(UserDB).where(UserDB.id == user_id)
            ).first()
            if user_db:
                user_db.last_login = datetime.now().timestamp()
                user_db.updated_at = user_db.last_login
                session.add(user_db)
                session.commit()
                await self.invalidate_user_cache(user_id)

    async def authenticate_user(self, username: str, password: str) -> Optional[UserAuth]:
        """
        认证用户

        Args:
            username: 用户名
            password: 密码

        Returns:
            认证成功返回 UserAuth，失败返回 None
        """
        with Session(engine) as session:
            user_db = session.exec(
                select(UserDB).where(
                    (UserDB.username == username) | (UserDB.email == username)
                )
            ).first()

            if not user_db:
                return None

            if not self._verify_password(password, user_db.password_hash):
                return None

            # 更新最后登录时间
            user_db.last_login = datetime.now().timestamp()
            session.add(user_db)
            session.commit()

            user = self._user_db_to_auth(user_db)
            await self._cache_user(user)
            return user

    async def create_user(
        self,
        username: str,
        email: str,
        password: str,
        is_superuser: bool = False
    ) -> UserAuth:
        """
        创建新用户

        Args:
            username: 用户名
            email: 邮箱
            password: 密码
            is_superuser: 是否超级用户

        Returns:
            创建的用户

        Raises:
            ValueError: 用户名或邮箱已存在
        """
        with Session(engine) as session:
            # 检查用户名是否已存在
            existing = session.exec(
                select(UserDB).where(UserDB.username == username)
            ).first()
            if existing:
                raise ValueError(f"Username '{username}' already exists")

            # 检查邮箱是否已存在
            if email:
                existing = session.exec(
                    select(UserDB).where(UserDB.email == email)
                ).first()
                if existing:
                    raise ValueError(f"Email '{email}' already exists")

            # 创建用户
            now = datetime.now().timestamp()
            user_db = UserDB(
                username=username,
                email=email,
                password_hash=self._hash_password(password),
                is_active=True,
                is_superuser=is_superuser,
                permissions=[],
                roles=["admin"] if is_superuser else ["user"],
                created_at=now,
                updated_at=now,
                last_login=None
            )

            session.add(user_db)
            session.commit()
            session.refresh(user_db)

            user = self._user_db_to_auth(user_db)
            await self._cache_user(user)

            logger.info(f"User created: {username} (id={user_db.id})")
            return user

    async def update_user(
        self,
        user_id: str,
        **kwargs
    ) -> Optional[UserAuth]:
        """
        更新用户信息

        Args:
            user_id: 用户ID
            **kwargs: 要更新的字段

        Returns:
            更新后的用户，或 None（如果用户不存在）
        """
        with Session(engine) as session:
            user_db = session.exec(
                select(UserDB).where(UserDB.id == user_id)
            ).first()

            if not user_db:
                return None

            original_is_active = user_db.is_active
            original_permissions = list(user_db.permissions or [])
            original_roles = list(user_db.roles or [])

            # 更新允许的字段
            allowed_fields = ['email', 'is_active', 'permissions', 'roles', 'avatar_url']
            for field in allowed_fields:
                if field in kwargs:
                    setattr(user_db, field, kwargs[field])

            # 如果更新密码
            if 'password' in kwargs:
                user_db.password_hash = self._hash_password(kwargs['password'])

            user_db.updated_at = datetime.now().timestamp()
            session.add(user_db)
            session.commit()
            session.refresh(user_db)

            user = self._user_db_to_auth(user_db)
            await self._cache_user(user)
            await self.invalidate_user_cache(user_id)

            should_revoke_sessions = False
            revoke_reason = "user_updated"

            if 'password' in kwargs:
                should_revoke_sessions = True
                revoke_reason = "password_changed"
            elif 'is_active' in kwargs and kwargs['is_active'] is False and original_is_active:
                should_revoke_sessions = True
                revoke_reason = "user_deactivated"
            elif 'permissions' in kwargs and list(kwargs['permissions'] or []) != original_permissions:
                should_revoke_sessions = True
                revoke_reason = "permissions_changed"
            elif 'roles' in kwargs and list(kwargs['roles'] or []) != original_roles:
                should_revoke_sessions = True
                revoke_reason = "roles_changed"

            if should_revoke_sessions:
                session_service.revoke_user_sessions(user_id, reason=revoke_reason)

            return user

    async def _get_user_from_cache(self, user_id: str) -> Optional[UserAuth]:
        """从Redis缓存获取用户"""
        redis = await self._get_redis()
        if not redis:
            return None

        try:
            cache_key = f"user:{user_id}"
            data = await redis.get(cache_key)
            if data:
                user_dict = json.loads(data)
                return UserAuth(**user_dict)
        except Exception as e:
            logger.error(f"Failed to get user from cache: {e}")

        return None

    async def _cache_user(self, user: UserAuth, ttl: int = 300) -> None:
        """缓存用户信息"""
        redis = await self._get_redis()
        if not redis:
            return

        try:
            cache_key = f"user:{user.id}"
            user_dict = user.model_dump()
            # Convert datetime to timestamp for JSON serialization
            if user_dict.get('created_at'):
                user_dict['created_at'] = float(user_dict['created_at'])
            if user_dict.get('last_login'):
                user_dict['last_login'] = float(user_dict['last_login'])

            await redis.setex(
                cache_key,
                ttl,
                json.dumps(user_dict)
            )
        except Exception as e:
            logger.error(f"Failed to cache user: {e}")

    async def invalidate_user_cache(self, user_id: str) -> None:
        """使用户缓存失效"""
        redis = await self._get_redis()
        if not redis:
            return

        try:
            cache_key = f"user:{user_id}"
            await redis.delete(cache_key)
        except Exception as e:
            logger.error(f"Failed to invalidate user cache: {e}")


# 全局用户服务实例
user_service = UserService()
