"""
认证服务示例代码
"""
from typing import Optional
from datetime import datetime, timedelta

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from fastapi import HTTPException, status

from app.core.security import verify_password, get_password_hash, create_access_token, create_refresh_token
from app.core.exceptions import UnauthorizedError, ConflictError, ForbiddenError
from app.core.logger import logger
from app.models.user import User
from app.schemas.auth import UserRegister, Token


class AuthService:
    """认证服务类"""
    
    def __init__(self, db: AsyncSession):
        self.db = db
    
    async def authenticate_user(self, username: str, password: str) -> Optional[User]:
        """
        验证用户身份
        
        Args:
            username: 用户名或邮箱
            password: 密码
            
        Returns:
            验证成功返回User对象，失败返回None
        """
        # 查询用户（支持用户名或邮箱登录）
        query = select(User).where(
            (User.username == username) | (User.email == username)
        )
        result = await self.db.execute(query)
        user = result.scalar_one_or_none()
        
        if not user:
            logger.warning("登录失败：用户不存在", username=username)
            return None
        
        # 验证密码
        if not verify_password(password, user.password_hash):
            logger.warning("登录失败：密码错误", user_id=user.id, username=username)
            # TODO: 记录失败次数，实现账号锁定
            return None
        
        # 检查账号状态
        if not user.is_active:
            logger.warning("登录失败：账号已禁用", user_id=user.id, username=username)
            raise ForbiddenError("账号已被禁用")
        
        logger.info("用户登录成功", user_id=user.id, username=user.username)
        return user
    
    async def create_user(self, user_data: UserRegister) -> User:
        """
        创建新用户
        
        Args:
            user_data: 用户注册数据
            
        Returns:
            创建的用户对象
            
        Raises:
            ConflictError: 用户名或邮箱已存在
        """
        # 检查用户名是否存在
        query = select(User).where(User.username == user_data.username)
        result = await self.db.execute(query)
        if result.scalar_one_or_none():
            raise ConflictError("用户名已存在")
        
        # 检查邮箱是否存在
        query = select(User).where(User.email == user_data.email)
        result = await self.db.execute(query)
        if result.scalar_one_or_none():
            raise ConflictError("邮箱已存在")
        
        # 创建用户
        user = User(
            username=user_data.username,
            email=user_data.email,
            password_hash=get_password_hash(user_data.password),
            is_active=True,
            is_superuser=False,
            role="trainee"  # 默认角色为学员
        )
        
        self.db.add(user)
        await self.db.commit()
        await self.db.refresh(user)
        
        logger.info("用户注册成功", user_id=user.id, username=user.username)
        return user
    
    async def create_tokens(self, user: User) -> Token:
        """
        为用户创建访问令牌和刷新令牌
        
        Args:
            user: 用户对象
            
        Returns:
            Token对象
        """
        # 创建访问令牌
        access_token = create_access_token(
            subject=user.id,
            role=user.role,
            username=user.username
        )
        
        # 创建刷新令牌
        refresh_token = create_refresh_token(subject=user.id)
        
        return Token(
            access_token=access_token,
            refresh_token=refresh_token,
            token_type="bearer",
            expires_in=1800,  # 30分钟
            user={
                "id": user.id,
                "username": user.username,
                "email": user.email,
                "role": user.role
            }
        )
    
    async def logout(self, token: str) -> None:
        """
        用户登出，将token加入黑名单
        
        Args:
            token: 要失效的token
        """
        # TODO: 将token加入Redis黑名单
        # redis_key = f"blacklist:{token}"
        # await redis.setex(redis_key, 3600, "1")  # 设置1小时过期
        
        logger.info("用户登出成功")
    
    async def refresh_access_token(self, refresh_token: str) -> Token:
        """
        使用刷新令牌获取新的访问令牌
        
        Args:
            refresh_token: 刷新令牌
            
        Returns:
            新的Token对象
        """
        # TODO: 验证刷新令牌
        # TODO: 检查是否在黑名单
        # TODO: 生成新的访问令牌
        # TODO: 可选 - 轮换刷新令牌
        
        pass
