"""
认证服务
"""
from datetime import datetime, timedelta
from typing import Optional

from sqlalchemy.ext.asyncio import AsyncSession

from app.core.exceptions import UnauthorizedError
from app.core.logger import logger
from app.core.security import create_access_token, create_refresh_token, decode_token
from app.models.user import User
from app.schemas.auth import Token
from app.services.user_service import UserService


class AuthService:
    """认证服务"""

    def __init__(self, db: AsyncSession):
        self.db = db
        self.user_service = UserService(db)

    async def login(self, username: str, password: str) -> tuple[User, Token]:
        """
        用户登录

        Args:
            username: 用户名/邮箱/手机号
            password: 密码

        Returns:
            用户对象和令牌
        """
        # 验证用户
        user = await self.user_service.authenticate(
            username=username, password=password
        )

        if not user:
            logger.warning(
                "登录失败：用户名或密码错误",
                username=username,
            )
            raise UnauthorizedError("用户名或密码错误")

        if not user.is_active:
            logger.warning(
                "登录失败：用户已被禁用",
                user_id=user.id,
                username=user.username,
            )
            raise UnauthorizedError("用户已被禁用")

        # 生成令牌
        access_token = create_access_token(subject=user.id)
        refresh_token = create_refresh_token(subject=user.id)

        # 更新最后登录时间
        await self.user_service.update_last_login(user.id)

        # 记录日志
        logger.info(
            "用户登录成功",
            user_id=user.id,
            username=user.username,
            role=user.role,
        )

        return user, Token(
            access_token=access_token,
            refresh_token=refresh_token,
        )

    async def refresh_token(self, refresh_token: str) -> Token:
        """
        刷新访问令牌

        Args:
            refresh_token: 刷新令牌

        Returns:
            新的令牌
        """
        try:
            # 解码刷新令牌
            payload = decode_token(refresh_token)

            # 验证令牌类型
            if payload.get("type") != "refresh":
                raise UnauthorizedError("无效的刷新令牌")

            # 获取用户ID
            user_id = int(payload.get("sub"))

            # 获取用户
            user = await self.user_service.get_by_id(user_id)
            if not user:
                raise UnauthorizedError("用户不存在")

            if not user.is_active:
                raise UnauthorizedError("用户已被禁用")

            # 生成新的访问令牌
            access_token = create_access_token(subject=user.id)

            logger.info(
                "令牌刷新成功",
                user_id=user.id,
                username=user.username,
            )

            return Token(
                access_token=access_token,
                refresh_token=refresh_token,  # 保持原刷新令牌
            )

        except Exception as e:
            logger.error(
                "令牌刷新失败",
                error=str(e),
            )
            raise UnauthorizedError("无效的刷新令牌")

    async def logout(self, user_id: int) -> None:
        """
        用户登出

        注意：JWT是无状态的，实际的登出需要在客户端删除令牌
        这里只是记录日志，如果需要可以将令牌加入黑名单

        Args:
            user_id: 用户ID
        """
        user = await self.user_service.get_by_id(user_id)
        if user:
            logger.info(
                "用户登出",
                user_id=user.id,
                username=user.username,
            )
