# _*_coding:utf-8_*_

import sys
import re
from datetime import datetime, timedelta
from pathlib import Path
from typing import Tuple, Dict, Any
# import uuid as uuid
import jwt

from django.conf import settings
from django.contrib.auth.hashers import check_password
from django.contrib.auth.hashers import make_password
from django.core.paginator import Paginator, EmptyPage
from django.db import transaction
from django.db.models import Q, F
from django.forms import model_to_dict

from rest_framework import serializers

from ..models import Auth, Platform, ExtendField, DetailInfo
from ..models import BaseInfo
# from ..utils.j_config import JConfig
from ..utils.j_dict import JDict
from ..utils.custom_tool import filter_result_field, format_params_handle, format_list_handle
from ..utils.get_short_id import get_short_id
from ..utils.j_join_list import JJoinList
from ..utils.j_ulid_field import JULIDField
from ..utils.nickname_generate import gen_one_word_digit


# 获取配置
module_config = JDict(settings.CONFIG.get('xj_user'))

app_id = module_config.app_id or ""
app_secret = module_config.secret or ""
jwt_secret_key = module_config.jwt_secret_key or "@zxmxy2021!"
expire_day = module_config.expire_day or ""
expire_second = module_config.expire_second or ""


class UserInfoSerializer(serializers.ModelSerializer):
    # 方法一：使用SerializerMethodField，并写出get_platform, 让其返回你要显示的对象就行了
    # p.s.SerializerMethodField在model字段显示中很有用。
    # platform = serializers.SerializerMethodField()

    # # 方法二：增加一个序列化的字段platform_name用来专门显示品牌的name。当前前端的表格columns里对应的’platform’列要改成’platform_name’
    user_id = serializers.ReadOnlyField(source='id')

    # permission_value = serializers.SerializerMethodField()

    # platform_id = serializers.ReadOnlyField(source='platform.platform_id')
    # platform_name = serializers.ReadOnlyField(source='platform.platform_name')

    class Meta:
        model = BaseInfo
        fields = [
            'user_id',
            # 'platform',
            # 'platform_uid',
            # 'platform__platform_name',
            # 'platform_id',
            # 'platform_name',
            # 'get_group_desc',
            'username',
            'fullname',
            'phone',
            'email',
            # 'wechat_openid',
            'user_info',
            # 'user_group',
            # 'user_group_id',
            # 'permission',
            # 'permission_value'
        ]
        # exclude = ['platform_uid']

    # def get_permission_value(self, instance):
    #     pass

    # # 这里是调用了platform这个字段拼成了get_platform
    # def get_platform(self, obj):
    #     return obj.platform.platform_name
    #     # return {
    #     #     'id': obj.platform.platform_id,
    #     #     'name': obj.platform.platform_name,
    #     # }


class UserService:
    # 临时
    detail_fields = ['user_id','real_name', 'sex', 'birth', 'tags', 'signature', 'cover', 'id_card_type', 'id_card_no',
                     'language', 'region_code', 'more']

    # 用户基础字段。方法二：[i.name for i in BaseInfo._meta.fields]，该方法对特定字段不能显式处理，如id, uuid, is_delete。
    base_fields = [
        'user_id', 'user_uuid', 'user_no', 'username', 'nickname', 'fullname',
        'phone', 'email', 'avatar', 'user_type', 'user_vip', 'user_level', 'user_info',
        'privacies', 'is_auth', 'is_using'
    ]

    def __init__(self):
        pass

    # 检测账户
    @classmethod
    def detail(cls, uuid: str):
        """
        @param uuid 用户UUID。
        """
        # print("> UserService::detail:", uuid)
        user_set = BaseInfo.objects
        user_set = user_set.annotate(user_id=F("id"), user_uuid=F("uuid")).extra(
            select={
                "user_uuid": 'REPLACE(uuid, "-", "")',
            }
        )
        # print("> check_account user_set query:", user_set.query)
        # print("> UserService::detail user_set:", uuid, user_set)
        user_dict = user_set.values(*cls.base_fields).first()
        # print("> UserService::detail user_dict:", uuid, user_dict)
        if not user_dict:
            return None, "账户不存在"
        return user_dict, None

    @classmethod
    def check_account(cls, account: str, platform_code: str = None, is_registered: bool = True) -> Tuple[dict, str]:
        """
        检测并返回账户信息。（自动判断账户类型；自动判断多账号冲突）

        @param account: 用户账户，支持三种类型：手机、用户名、邮箱。自动判断
        @param platform_code: 用户平台码，4位大写字母
        @param is_registered: 是否判断账户已注册。如果为真，账号不存在则报错；为假，账号不存在返回Null
        @description: 注意：用户名不推荐由纯数字构成，因为那样容易和11位手机号冲突
        """
        # 账号类型判断
        if re.match(r'(^1[356789]\d{9}$)|(^\+?[78]\d{10}$)', account):
            account_type = 'phone'
        elif re.match(r'^\w+[\w\.\-\_]*@\w+[\.\w]*\.\w{2,}$', account):
            account_type = 'email'
        elif re.match(r'^[A-Za-z\u4E00-\u9FA5_0-9]\w*$', account):
            account_type = 'username'
        else:
            return None, f"账号({account})必须是用户名、手机或者邮箱，其中用户名不能少于4个字符且不能以数字开头，不含特殊字符"

        # 排除注销用户
        user_query = BaseInfo.objects.exclude(is_delete=True)

        # 根据类型查询
        if account_type == 'phone':
            user_query = user_query.filter(phone=account)
        elif account_type == 'email':
            user_query = user_query.filter(email=account)
        elif account_type == 'username':
            user_query = user_query.filter(username=account)
        else:
            return None, "账号类型匹配失败。原则上这是一个不可能出现的提示！"

        # 删除UUID横杠
        user_query = user_query.annotate(user_id=F("id")).extra(
            select={"user_uuid": 'REPLACE(uuid, "-", "")', }
        )

        # 是否检查平台码
        if platform_code:
            user_query = user_query.filter(platforms_to_users__platform_code=platform_code)

        # 请求数据库
        user_list = user_query.values(*cls.base_fields)

        # 多账号冲突
        if len(user_list) > 1:
            return None, "登录异常，请联系管理员，发现多账号冲突"

        # 检查账号存在
        user_set = user_list.first()
        if not user_set:
            return None, "账户不存在" if is_registered else None

        return user_set, None

    # 检测令牌
    @classmethod
    def check_token(cls, token):
        """
        @param token 用户令牌。
        @description 注意：用户令牌的载荷体payload中必须包含两个参数：account账号、exp过期时间，其中账号可以是手机、用户名、邮箱三种。
        @description BEARER类型的token是在RFC6750中定义的一种token类型，OAuth2.0协议RFC6749对其也有所提及，算是对RFC6749的一个补充。BEARER类型token是建立在HTTP/1.1版本之上的token类型，需要TLS（Transport Layer Security）提供安全支持，该协议主要规定了BEARER类型token的客户端请求和服务端验证的具体细节。
        @description 理论上，每次请求令牌后就更新一次令牌，以监测用户长期访问时，不至于到时间后掉线反复登陆。
        """
        # 检查是否有Bearer前辍，如有则截取
        # print("> token 1:", token)
        if not token:
            return None, "请登录"  # 缺少Token

        if re.match(r'Bearer(.*)$', token, re.IGNORECASE):
            token = re.match(r'Bearer(.*)$', token, re.IGNORECASE).group(1).strip()
        # print("> token 2:", token)

        if not token:
            return None, "您尚未登录"

        # # 验证token。另一种方式，从数据库核对Token，通过对比服务端的Token，以确定是否为服务器发送的。今后启用该功能。
        # auth_set = Auth.objects.filter(Q(token=token)).order_by('-update_time')
        # # print("> auth:", auth_set)
        # if not auth_set.count():
        #     raise MyApiError('token验证失败', 6002)
        # auth = auth_set.first()

        try:
            # jwt.decode会自动检查exp参数，如果超时则抛出jwt.ExpiredSignatureError超时
            # jwt_payload = jwt.decode(token, key=Config.getIns().get('xj_user', 'JWT_SECRET_KEY'), verify=True, algorithms=["RS256", "HS256"])
            jwt_payload = jwt.decode(token, key=jwt_secret_key, verify=True, algorithms=["RS256", "HS256"])
            # print("> jwt_payload:", jwt_payload)

        except jwt.ExpiredSignatureError:
            return None, "登录已过期，请重新登录"

        except jwt.InvalidTokenError:
            return None, "用户令牌无效，请重新登录"

        account = jwt_payload.get('account', None)
        user_uuid = jwt_payload.get('user_uuid', None)
        user_id = jwt_payload.get('user_id', None)
        platform_id = jwt_payload.get('platform_id', None)
        if not account:
            return {'payload': jwt_payload}, "错误：令牌载荷中未提供用户账户Account"

        # 检测用户令牌时不应该调用用户信息，这会导致任何接口都会查询用户表，时间会增加
        # user_info = UserService.check_account(account=account)

        return {'account': account, 'user_uuid': user_uuid, 'user_id': user_id, 'platform_id': platform_id}, None

    # @staticmethod
    # def user_add(params):
    #     """
    #     用户添加服务
    #     :param params: 添加参数
    #     :return: data,err
    #     """
    #     try:
    #         account = str(params.get('account', ''))
    #         password = str(params.get('password', ''))
    #         fullname = str(params.get('fullname', ''))
    #         nickname = str(params.get('nickname', ''))
    #         # 边界检查
    #         if not account:
    #             return None, "account必填"
    #         # 账号类型判断
    #         if re.match(r'(^1[356789]\d{9}$)|(^\+?[78]\d{10}$)', account):
    #             account_type = 'phone'
    #         elif re.match(r'^\w+[\w\.\-\_]*@\w+[\.\w]*\.\w{2,}$', account):
    #             account_type = 'email'
    #         elif re.match(r'^[A-z\u4E00-\u9FA5]+\w*$', account):
    #             account_type = 'username'
    #         else:
    #             return None, "账号必须是手机、邮箱格式或者为纯英文且不含特殊字符12位字符"
    #         # 检查账号是否存在
    #         user_list = None
    #         if account_type == 'phone':
    #             user_list = BaseInfo.objects.filter(Q(phone=account))
    #         elif account_type == 'email':
    #             user_list = BaseInfo.objects.filter(Q(email=account))
    #         elif account_type == 'username':
    #             user_list = BaseInfo.objects.filter(Q(username=account))
    #
    #         if user_list.count() and account_type == 'phone':
    #             return None, "手机已被注册: " + account
    #         elif user_list.count() and account_type == 'email':
    #             return None, "邮箱已被注册: " + account
    #         elif user_list.count() and account_type == 'username':
    #             return None, "用户名已被注册: " + account
    #
    #         # 创建基础信息
    #         base_info = {
    #             'username': account if account_type == 'username' else get_short_id(),
    #             'phone': account if account_type == 'phone' else "",
    #             'email': account if account_type == 'email' else '',
    #             'fullname': fullname,
    #             "nickname": nickname,
    #         }
    #         current_user = BaseInfo.objects.create(**base_info)
    #         # 创建登录信息
    #         token = jwt.encode({'account': account}, jwt_secret_key)
    #         auth = {
    #             'user_id': current_user.id,
    #             'password': make_password(password, None, 'pbkdf2_sha1'),
    #             'plaintext': password,
    #             'token': token,
    #         }
    #         Auth.objects.create(**auth)
    #         return {"user_id": current_user.id}, None
    #     except SyntaxError:
    #         return None, "语法错误"
    #     except LookupError:
    #         return None, "无效数据查询"
    #     except Exception as valueError:
    #         return None, valueError.msg if hasattr(valueError, 'msg') else valueError.args
    #     except:
    #         return None, "未知错误"
    @classmethod
    def user_add(cls, params):
        """
        用户添加服务
        :param params: 添加参数
        :return: data,err
        """
        try:
            account = str(params.get('account', get_short_id()))
            password = str(params.get('password', '123456'))
            fullname = str(params.get('fullname', ''))
            nickname = str(params.get('nickname', gen_one_word_digit()))
            user_type = str(params.get('user_type', "PERSON"))
            phone = str(params.get('phone', ""))
            email = str(params.get('email', ""))

            # 账号类型判断
            if re.match(r'(^1[356789]\d{9}$)|(^\+?[78]\d{10}$)', account):
                account_type = 'phone'
            elif re.match(r'^\w+[\w\.\-\_]*@\w+[\.\w]*\.\w{2,}$', account):
                account_type = 'email'
            elif re.match(r'^[A-Za-z\u4E00-\u9FA5_0-9]\w*$', account):
                account_type = 'username'
            else:
                return None, "账号必须是手机、邮箱格式或者为纯英文且不含特殊字符12位字符"

            # print("账号类型", account_type)
            # 检查账号是否存在
            user_list = None
            if account_type == 'phone':
                user_list = BaseInfo.objects.filter(Q(phone=account))
            elif account_type == 'email':
                user_list = BaseInfo.objects.filter(Q(email=account))
            elif account_type == 'username':
                user_list = BaseInfo.objects.filter(Q(username=account))

            if user_list.count() and account_type == 'phone':
                return None, "手机已被注册: " + account
            elif user_list.count() and account_type == 'email':
                return None, "邮箱已被注册: " + account
            elif user_list.count() and account_type == 'username':
                return None, "用户名已被注册: " + account

            # 校验手机号格式
            if phone and not re.match(r'(^1[356789]\d{9}$)|(^\+?[78]\d{10}$)', phone):
                return None, "手机号格式不正确"

            # 校验邮箱格式
            if email and not re.match(r'^\w+[\w\.\-\_]*@\w+[\.\w]*\.\w{2,}$', email):
                return None, "邮箱格式不正确"

            # 创建基础信息
            base_info = {
                'username': account if account_type == 'username' else get_short_id(),
                'phone': phone if phone else None,
                'email': email if email else None,
                'fullname': fullname,
                "nickname": nickname,
                "uuid": JULIDField.get_u12(),
                "user_type": user_type
            }
            current_user = BaseInfo.objects.create(**base_info)

            # 创建登录信息
            token = jwt.encode({'account': account}, jwt_secret_key)
            auth = {
                'user_id': current_user.id,
                'password': make_password(password, None, 'pbkdf2_sha1'),
                'plaintext': password,
                'token': token,
            }
            Auth.objects.create(**auth)
            print(f"current_user.to_json():{current_user.to_json()}")
            return current_user.to_json(), None
        except SyntaxError:
            return None, "语法错误"
        except LookupError:
            return None, "无效数据查询"
        except Exception as valueError:
            return None, valueError.msg if hasattr(valueError, 'msg') else valueError.args
        except:
            return None, "未知错误"

    @classmethod
    def bind_user_level(cls, user_id, user_level: int = None):
        print("data", user_id, user_level)
        if not user_id:
            return None, "用户ID不存在"
            # 更新基础信息
        base_query_set = BaseInfo.objects.filter(id=user_id)
        if not base_query_set.first():
            return None, "用户不存在"
            # 指定要更新的字段及其新值
        update_fields = {
            'user_level': user_level,
        }
        try:
            base_query_set.update(**update_fields)
        except Exception as e:
            return None, str(e)

        return None, None

    @classmethod
    def user_edit(cls, params=None, user_id=None, user_uuid=None):
        """
        用户基础信息修改服务。
        PS: 不允许修改用户的用户名，用户名负责登录作用。
        :param params: 修改参数集合，包含用户的基础信息和详细信息。
        :param user_id: 用户ID，用于定位要修改的用户。
        :param user_uuid: 用户UUID，用于定位要修改的用户。
        :return: 返回修改后的用户信息或错误信息。
        """
        if params is None:
            params = {}
        if not user_id and not user_uuid:
            return None, "用户ID或UUID不存在"

        # 绑定信息验重
        email_other_user = BaseInfo.objects.exclude(id=user_id).filter(email=params.get("email", None)).first()
        if params.get("email") and email_other_user:
            return None, "当前邮箱被绑定"

        # 用户基础信息中仅仅可以修改以下参数
        base_info_params = format_params_handle(
            param_dict=params.copy(),
            filter_filed_list=["fullname", "nickname", "email", "user_info", "privacies", "phone", "is_using",
                               "avatar"],
            is_remove_null=True
        )

        # 修改详情信息
        field_map_list = ExtendField.objects.all().values("field", 'field_index')
        detail_fields = cls.detail_fields.copy()
        for field_map in field_map_list:
            detail_fields.append(field_map.get("field"))

        # 将拓展字段添加到详细字段
        detail_params = format_params_handle(
            param_dict=params.copy(),
            filter_filed_list=detail_fields,
            is_remove_null=True
        )

        # 拓展字段映射
        field_mapping = {item['field']: item['field_index'] for item in field_map_list}
        detail_params = {field_mapping.get(k, k): v for k, v in detail_params.items()}

        try:
            with transaction.atomic():
                # 根据 user_id 或 user_uuid 查询用户
                if user_id:
                    base_query_set = BaseInfo.objects.filter(id=user_id)
                else:
                    base_query_set = BaseInfo.objects.filter(uuid=user_uuid)

                base_set = base_query_set.first()
                if not base_set:
                    return None, "用户不存在"

                # 修改基础信息
                base_query_set.update(**base_info_params)

                # 判断是否添加过
                detail_user_obj = DetailInfo.objects.filter(user_id=base_set.id)
                if not detail_user_obj.first():
                    # 没有添加，进行添加操作
                    detail_params.pop("id", None)  # 添加的时候不能有ID主键，防止主键冲突
                    detail_params['user_id'] = base_set.id  # Ensure user_id is set
                    detail_set = DetailInfo.objects.create(**detail_params)
                else:
                    # 添加过进行更新
                    detail_user_obj.update(**detail_params)

                # 重新查询更新后的数据
                updated_base_set = BaseInfo.objects.filter(id=base_set.id).first()
                updated_detail_set = DetailInfo.objects.filter(user_id=base_set.id).first()

                # 合并结果
                result = {**model_to_dict(updated_base_set), **model_to_dict(updated_detail_set)}
                return result, None

        except BaseInfo.DoesNotExist as e:
            return None, f"基础信息修改失败:{e}"
        except DetailInfo.DoesNotExist as e:
            return None, f"详细信息修改失败:{e}"
        except Exception as e:
            # 处理其他可能的异常
            return None, f"修改失败:{e}"

    @classmethod
    def user_delete(cls, user_id=None, user_uuid=None):
        """
        删除用户（软删除）。
        支持通过 user_id 或 user_uuid 定位用户。
        :param user_id: 用户ID
        :param user_uuid: 用户UUID
        :return: 返回删除后的用户信息或错误信息。
        """
        if not user_id and not user_uuid:
            return None, "必须提供 user_id 或 user_uuid"

        # 通过 user_id 或 user_uuid 查找用户
        if user_id:
            user_set = BaseInfo.objects.filter(id=user_id).first()
        else:
            user_set = BaseInfo.objects.filter(uuid=user_uuid).first()

        if not user_set:
            return None, f'用户不存在 (user_id: {user_id}, user_uuid: {user_uuid})'

        # 软删除：将 is_delete 设置为 False
        user_set.is_delete = False
        user_set.save()

        # 返回删除后的用户信息
        return user_set.to_json(), None

    @classmethod
    def user_search(cls, user_id_list: list = None, search_params: dict = None, need_map: bool = False,
                    filter_fields: list = None, page: int = 1, page_size: int = 10, order_by: str = None):
        """
        搜索用户，返回用户基础信息列表，支持分页和排序。
        :param user_id_list: 用户id列表
        :param search_params: 搜索字段
        :param need_map: 是否需要映射
        :param filter_fields: 需要查询的字段
        :param page: 当前页码
        :param page_size: 每页大小
        :param order_by: 排序字段，如 'username' 或 '-register_time'
        :return: data, err
        """
        print(not user_id_list and not search_params)
        if not user_id_list and not search_params:
            return [], "参数错误,无法筛选数据"

        # 列表的查询参数处理
        if filter_fields is None:
            filter_fields = ["id", "username", "fullname", "nickname", "phone", "email", "register_time",
                             "user_type", "user_vip", "user_level"]
        else:
            filter_fields = format_list_handle(
                param_list=filter_fields,
                filter_filed_list=["id", "username", "fullname", "nickname", "phone", "email", "register_time",
                                   "user_type", "user_vip", "user_level"]
            )
            filter_fields = ["id", "username", "fullname", "nickname", "phone", "email",
                             "register_time"] if not filter_fields else filter_fields

        # 字段查询参数处理
        if search_params:
            search_params = format_params_handle(
                param_dict=search_params,
                filter_filed_list=["id", "user_id", "username", "fullname", "nickname", "phone", "email",
                                   "register_time", "register_time_start", "register_time_end", "user_type",
                                   "user_vip", "user_level"],
                alias_dict={"user_id": "id", "register_time_start": "register_time__gte",
                            "register_time_end": "register_time_lte"}
            )

        # 查询管理
        try:
            # orm 查询
            if user_id_list:
                user_base_set = BaseInfo.objects.filter(id__in=user_id_list)
            else:
                user_base_set = BaseInfo.objects.filter(**search_params)

            # 排序
            if order_by:
                user_base_set = user_base_set.order_by(order_by)

            # 分页
            paginator = Paginator(user_base_set, page_size)
            try:
                user_base_page = paginator.page(page)
            except EmptyPage:
                return [], "页码超出范围"

            # 获取列表并字段转换，id ==>> user_id
            user_base_list = list(user_base_page.object_list.values(*filter_fields))
            user_base_list = filter_result_field(
                result_list=user_base_list,
                alias_dict={"id": "user_id"}
            )

            # 如果需要转换，则进行映射
            if need_map:
                user_base_list = {i['user_id']: i for i in user_base_list}

            # 返回分页信息
            pagination_info = {
                "total": paginator.count,
                "page": page,
                "page_size": page_size,
                "total_pages": paginator.num_pages,
            }

            return {"data": user_base_list, "pagination": pagination_info}, None
        except Exception as e:
            return [], "err:" + str(e)