#!/usr/bin/env python
# @Project ：django_base_ai
# @File    : login.py
# @Author  : cx
# @Time    : 11/2/2025
# @Desc    : 登录、登出、Token 刷新、验证码。
"""
登录相关接口：验证码获取、账号密码登录（含验证码/单点登录）、Token 刷新、登出；
以及接口文档登录、无验证码登录等扩展入口。依赖 rest_framework_simplejwt 与 dispatch 系统配置。
"""
import base64
import logging
from datetime import datetime, timedelta

from captcha.views import CaptchaStore, captcha_image
from django.conf import settings
from django.conf import settings as config
from django.core.cache import cache
from django.utils.translation import gettext_lazy as _
from jwt import decode as jwt_decode
from rest_framework import serializers
from rest_framework.views import APIView
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer, TokenRefreshSerializer
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework_simplejwt.views import TokenObtainPairView, TokenViewBase

from django_base_ai import dispatch
from django_base_ai.system.models import Users
from django_base_ai.utils.json_response import DetailResponse
from django_base_ai.utils.request_util import save_login_log
from django_base_ai.utils.serializers import CustomModelSerializer
from django_base_ai.utils.validator import CustomValidationError

logger = logging.getLogger(__name__)


class CaptchaView(APIView):
    authentication_classes = []
    permission_classes = []

    def get(self, request):
        logger.info("请求获取验证码")
        hashkey = CaptchaStore.generate_key()
        id = CaptchaStore.objects.filter(hashkey=hashkey).first().id
        imgage = captcha_image(request, hashkey)
        # 将图片转换为base64
        image_base = base64.b64encode(imgage.content)
        data = {
            "key": id,
            "image_base": "data:image/png;base64," + image_base.decode("utf-8"),
        }
        logger.info(f"验证码生成成功: key={id}")
        return DetailResponse(data=data)


class LoginSerializer(TokenObtainPairSerializer):
    """
    登录的序列化器:
    重写djangorestframework-simplejwt的序列化器
    """

    captcha = serializers.CharField(max_length=6, required=False, allow_null=True, allow_blank=True)

    class Meta:
        model = Users
        fields = "__all__"
        read_only_fields = ["id"]

    default_error_messages = {"no_active_account": _("账号/密码错误")}

    def validate(self, attrs):
        logger.info("开始验证登录信息")
        login_type = dispatch.get_system_config_values("third.login_type", "")
        logger.info(f"当前登录类型配置: {login_type}")
        if login_type == "SSOLOGIN":
            logger.warning(f"尝试通过用户名密码登录，但配置为{login_type}登录")
            return {"code": 4000, "msg": f"无法通过用户名密码登录,请通过{login_type}登录", "data": None}

        captcha = self.initial_data.get("captcha", None)
        logger.debug(f"验证码参数: {captcha is not None}")
        if dispatch.get_system_config_values("base.captcha_state"):
            if captcha is None:
                raise CustomValidationError("验证码不能为空")
            self.image_code = CaptchaStore.objects.filter(id=self.initial_data["captchaKey"]).first()
            five_minute_ago = datetime.now() - timedelta(hours=0, minutes=5, seconds=0)
            if self.image_code and five_minute_ago > self.image_code.expiration:
                logger.warning("验证码已过期")
                self.image_code and self.image_code.delete()
                raise CustomValidationError("验证码过期")
            else:
                if self.image_code and (self.image_code.response == captcha or self.image_code.challenge == captcha):
                    self.image_code and self.image_code.delete()
                else:
                    logger.warning("图片验证码错误")
                    self.image_code and self.image_code.delete()
                    raise CustomValidationError("图片验证码错误")
        data = super().validate(attrs)
        logger.info(f"用户 {self.user.username} 登录验证成功")
        data["name"] = self.user.name
        data["userId"] = self.user.id
        data["avatar"] = self.user.avatar
        data["user_type"] = self.user.user_type
        data["skin"] = self.user.skin
        data["pwd_defaulted"] = self.user.pwd_defaulted
        data["user_config"] = self.user.user_config
        data["is_staff"] = self.user.is_staff
        dept = getattr(self.user, "dept", None)
        if dept:
            data["dept_info"] = {"dept_id": dept.id, "dept_name": dept.name}
        role = getattr(self.user, "role", None)
        if role:
            data["role_info"] = role.values("id", "name", "key")
        request = self.context.get("request")
        request.user = self.user
        # 记录登录日志
        save_login_log(request=request)
        # 是否开启单点登录
        if dispatch.get_system_config_values("base.single_login"):
            logger.info(f"检测到单点登录已开启，处理用户 {self.user.username} 的旧token")
            # 将之前登录用户的token加入黑名单
            user = Users.objects.filter(id=self.user.id).values("last_token").first()
            last_token = user.get("last_token")
            if last_token:
                try:
                    token = RefreshToken(last_token)
                    token.blacklist()
                except Exception:
                    pass
            # 将最新的token保存到用户表
        Users.objects.filter(id=self.user.id).update(last_token=data.get("access"))
        return {"code": 2000, "msg": "请求成功", "data": data}


class CustomTokenRefreshSerializer(TokenRefreshSerializer):
    def validate(self, attrs):
        logger.info("开始刷新token")
        try:
            data = super().validate(attrs)
            decoded_data = jwt_decode(data.get("access"), config.SECRET_KEY, algorithms=["HS256"])
            user_id = decoded_data["user_id"]
            logger.info(f"为用户 ID {user_id} 刷新token")
            Users.objects.filter(id=user_id).update(last_token=data.get("access"))
            logger.info(f"token刷新成功: 用户 ID {user_id}")
            return {"code": 0, "msg": "ok", "data": data}
        except Exception as e:
            logger.error(f"token刷新失败: {e}")
            logger.exception(e)
            return {"code": 401}


class CustomTokenRefreshView(TokenViewBase):
    """
    自定义刷新token refresh: 刷新token的元素
    """

    serializer_class = CustomTokenRefreshSerializer


class LoginView(TokenObtainPairView):
    """
    登录接口
    """

    serializer_class = LoginSerializer
    permission_classes = []


class LoginTokenSerializer(TokenObtainPairSerializer):
    """
    登录的序列化器:
    """

    class Meta:
        model = Users
        fields = "__all__"
        read_only_fields = ["id"]

    default_error_messages = {"no_active_account": _("账号/密码不正确")}

    def validate(self, attrs):
        if not getattr(settings, "LOGIN_NO_CAPTCHA_AUTH", False):
            return {"code": 4000, "msg": "该接口暂未开通!", "data": None}
        data = super().validate(attrs)
        data["name"] = self.user.name
        data["userId"] = self.user.id
        return {"code": 2000, "msg": "请求成功", "data": data}


class LoginTokenView(TokenObtainPairView):
    """
    登录获取token接口
    """

    serializer_class = LoginTokenSerializer
    permission_classes = []


class LogoutView(APIView):
    def post(self, request):
        logger.info(f"用户 {request.user.username} 开始注销")
        user_id = request.user.id
        Users.objects.filter(id=user_id).update(last_token=None)
        # 将注销的token 放在缓存中
        cache.set(request.auth, user_id, settings.SIMPLE_JWT.get("ACCESS_TOKEN_LIFETIME", 300).seconds)  # 有效期5分钟
        logger.info(f"用户 {request.user.username} 注销成功")
        return DetailResponse(msg="注销成功")


class ApiLoginSerializer(CustomModelSerializer):
    """接口文档登录-序列化器"""

    username = serializers.CharField()
    password = serializers.CharField()

    class Meta:
        model = Users
        fields = ["username", "password"]
