"""users.views.user_info - auto-generated by split script."""
import os
import sys
import json
import re
import time
import uuid
import random
import string
import requests
import hashlib
import traceback
import decimal
import jwt
import ipaddress
import xmltodict
from datetime import datetime, timedelta
from decimal import Decimal
import defusedxml.ElementTree as ET

from django.conf import settings
from django.db import models, transaction, IntegrityError, DatabaseError, connection
from django.db.models import Q, F, Sum, Count, Case, When, IntegerField, Prefetch
from django.core.cache import cache
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.core.exceptions import ObjectDoesNotExist
from django.shortcuts import get_object_or_404
from django.utils import timezone
from django.http import HttpResponse
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.hashers import make_password

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import permissions, status
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.parsers import MultiPartParser, FormParser, JSONParser
from rest_framework.throttling import AnonRateThrottle
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework_simplejwt.authentication import JWTAuthentication

from gvsdsdk.fluent import db, func, FQ

from utils.oss_utils import validate_image, upload_to_oss, delete_from_oss
from utils.money import yuan_to_fen
from utils.fadan_utils import check_fadan_qiangdan_eligible
from utils.invitationcode_utils import CreateInvitationCode, VerifyInvitationCode
from users.fadan_fenhong_utils import process_fadan_fenhong

from orders.utils import (
    update_daily_payout,
    settle_shangjia_order_guanshi_fenhong
)
from backend.utils import (
    update_dashou_daily_by_action, update_guanshi_daily_by_action,
    update_shangjia_daily, update_zuzhang_daily_by_action,
    verify_kefu_permission
)
from rank.utils import get_tag_fee, create_shenhe_jilu, validate_shenheguan

from ..models import (
    UserBoss, UserDashou, UserShangjia, UserGuanshi,
    UserZuzhang, UserKefu, AdminProfile, OfficialAccountUser,
    TixianAutoRecord, TixianShenheJilu, Tixianjilu, Xiugaijilu,
    RankingRecord
)
from users.business_models import User
from ..tixian_shenhe_services import (
    load_audit_meta_map, refund_balance, sync_audit_and_jilu_status,
    mark_transfer_success, release_collect_quota_for_record,
    close_audit_wechat_failed, query_wechat_transfer_bill,
    check_shijidaozhang_within_limit,
    check_dashou_trial_blocks_commission_withdraw,
    WX_STATE_FAIL, WX_STATE_SUCCESS, WX_STATE_WAIT, WX_STATE_CANCELING,
)
from ..tixian_shenhe_views import process_audit_collect

from orders.models import (
    Order, PlatformOrderExt, MerchantOrderExt, CommissionRate,
    PenaltyRecord, PenaltyEvidenceImage, RefundRecord, PlayerDeliveryImage,
    Penalty, PenaltyAppealImage
)
from products.models import (
    ShangpinLeixing, Huiyuan, Huiyuangoumai, Gsfenhong, Czjilu
)
from config.models import Qunpeizhi
from backend.models import MerchantDailyStats
from rank.models import (
    KaohePayTemp, Chenghao, ShenheJilu,
    KaoheCishuFeiyong, YonghuChenghao
)
from users.models import UserShenheguan
from products.utils import update_shangpin_daily_stat
from shop.utils import update_dianpu_daily_stat
from rank.utils import create_shenhe_jilu_from_temp

import logging

logger = logging.getLogger(__name__)

def huoquYonghuDaili(request):
    """
    获取用户浏览器/设备信息
    """
    user_agent = request.META.get('HTTP_USER_AGENT', '')

    # 这里可以添加更复杂的用户代理解析
    # 为简化，我们只返回原始字符串的前100个字符
    return user_agent[:100] if user_agent else '' 


def huoquKehuduanIP(request):
    """
    强化版：安全、兼容地获取客户端真实IP地址
    """
    ip = None

    # 1. 从标准头部获取IP链
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        # 按逗号分割，取第一个（即最原始的客户端IP）
        ip = x_forwarded_for.split(',')[0].strip()

    # 2. 如果上面没有，则用Nginx传递的X-Real-IP
    if not ip:
        ip = request.META.get('HTTP_X_REAL_IP')

    # 3. 最后降级到连接地址
    if not ip:
        ip = request.META.get('REMOTE_ADDR')

    # ===== 核心：清洗和验证IP格式 =====
    if ip:
        # 情况A: 处理IPv4映射的IPv6地址 (::ffff:192.168.1.1)
        if ip.startswith('::ffff:'):
            ip = ip[7:]

        # 情况B: 处理带端口的IPv4地址 (223.91.110.123:62417)
        # 这是导致您记录“三段”的最可能原因！
        # 如果IP字符串中有冒号，且最后一个冒号前的部分是IPv4，则分离端口。
        if ':' in ip and '.' in ip:
            # 尝试按冒号分割，取第一部分
            possible_ip = ip.rsplit(':', 1)[0]  # 从右边分割一次
            # 验证它是否是有效的IPv4地址
            try:
                ipaddress.IPv4Address(possible_ip)  # 如果通过验证，说明格式正确
                ip = possible_ip
            except (ipaddress.AddressValueError, ValueError):
                # 如果验证失败，说明可能不是IPv4带端口，保持原样或进行其他处理
                pass

        # 情况C: 验证最终结果是否为有效IPv4（可选，但推荐）
        try:
            # 此步骤会抛出异常，如果ip变量此时仍是“223.91.110.”这种格式
            ipaddress.IPv4Address(ip)
        except (ipaddress.AddressValueError, ValueError):
            # 记录一个警告日志，并将IP标记为无效或存为原始字符串
            print(f"警告：获取到无效或非标准IP地址格式: {ip}")
            # 您可以选择在此处 return None 或存储原始字符串以供排查

    return ip


class UserInfoUpdateView(APIView):
    permission_classes = [permissions.IsAuthenticated]
    parser_classes = [MultiPartParser, FormParser, JSONParser]

    # 进程内缓存，防止同一code被重复调用微信（单进程安全，多进程小概率重复，但用户极少可忽略）
    _phone_cache = {}

    def post(self, request):
        try:
            user = request.user
            boss = self._get_or_create_boss(user)
            result = {}
            nick = request.data.get('nicheng')

            # 诊断日志：记录入参和实例状态
            logger.warning(
                "[USERINFO_XIUGAI] 入参 content_type=%s, data_keys=%s, files_keys=%s, "
                "user_pk=%s, user_UserUID=%s, boss_pk=%s, boss_nickname_before=%r",
                request.content_type,
                list(request.data.keys()) if hasattr(request, 'data') else 'N/A',
                list(request.FILES.keys()),
                getattr(user, 'pk', None),
                getattr(user, 'UserUID', None),
                getattr(boss, 'pk', None),
                getattr(boss, 'nickname', None),
            )

            # 头像
            av = request.FILES.get('avatar')
            if av:
                ok, msg = validate_image(av)
                if not ok:
                    return Response({'code': 1, 'msg': msg, 'data': None}, status=400)
                url = self._upload_avatar(user, av)
                if url:
                    result['touxiang'] = url
                    user.Avatar = url
                else:
                    return Response({'code': 2, 'msg': '头像上传失败', 'data': None}, status=500)

            # 昵称
            if nick:
                nick = str(nick).strip()
                if len(nick) > 20:
                    return Response({'code': 3, 'msg': '昵称过长', 'data': None}, status=400)
                boss.nickname = nick
                result['nicheng'] = nick

            # 手机号 —— multipart 表单字段兼容 request.data / request.POST
            code = (
                request.data.get('shoujihao_code')
                or request.POST.get('shoujihao_code')
                or ''
            )
            if isinstance(code, str):
                code = code.strip()
            else:
                code = str(code or '').strip()

            # 手机号强制认证页：同时带头像 + shoujihao_code，且不带 nicheng（资料编辑页会带昵称）
            is_phone_auth_flow = bool(av) and bool(code) and not nick
            phone_error = None

            if is_phone_auth_flow and not code:
                return Response({
                    'code': 6,
                    'msg': '未收到手机号授权，请重新点击获取手机号',
                    'data': None,
                }, status=200)

            if code:
                phone = self._get_phone_safe(code, request)
                if phone is None:
                    phone_error = '手机号获取失败，请重新授权'
                else:
                    user.Phone = phone
                    user.PhoneVerified = True
                    result['phone'] = phone
                    result['phone_verified'] = True

            if not result and not phone_error:
                logger.warning("[USERINFO_XIUGAI] 无更新，result 为空")
                return Response({'code': 5, 'msg': '无更新', 'data': None}, status=400)

            # 诊断日志：save 前的状态
            logger.warning(
                "[USERINFO_XIUGAI] 准备保存 result_keys=%s, boss_pk=%s, boss_nickname_after=%r, "
                "user_Avatar=%r, user_Phone=%r, phone_error=%r",
                list(result.keys()),
                getattr(boss, 'pk', None),
                getattr(boss, 'nickname', None),
                getattr(user, 'Avatar', None),
                getattr(user, 'Phone', None),
                phone_error,
            )

            # 认证流：必须拿到手机号才算成功，避免只存头像却返回 code=0 导致反复弹窗
            if is_phone_auth_flow and 'phone' not in result:
                msg = phone_error or '手机号获取失败，请重新授权'
                if result:
                    with transaction.atomic():
                        user_update_fields = []
                        if 'touxiang' in result:
                            user_update_fields.append('Avatar')
                        if user_update_fields:
                            user.save(update_fields=user_update_fields)
                        if 'nicheng' in result:
                            boss.save()
                return Response({'code': 6, 'msg': msg, 'data': result or None}, status=200)

            # 有字段更新时先保存（资料编辑：手机号失败不阻塞头像/昵称）
            if result:
                user_update_fields = []
                if 'touxiang' in result:
                    user_update_fields.append('Avatar')
                if 'phone' in result:
                    user_update_fields.extend(['Phone', 'PhoneVerified'])
                with transaction.atomic():
                    if user_update_fields:
                        user.save(update_fields=user_update_fields)
                    elif result:
                        user.save()
                    boss.save()
                logger.warning("[USERINFO_XIUGAI] 保存成功 result_keys=%s", list(result.keys()))

            if phone_error:
                return Response({'code': 6, 'msg': phone_error, 'data': result}, status=200)

            return Response({'code': 0, 'msg': '更新成功', 'data': result})

        except Exception as e:
            logger.error("[USERINFO_XIUGAI] 异常: %s", str(e), exc_info=True)
            return Response({'code': 99, 'msg': str(e), 'data': None}, status=500)

    # ---------- 安全手机号（内存去重） ----------
    def _get_phone_safe(self, code, request):
        cache_key = f'{code}:{self._resolve_wx_club_id(request, request.user)}'
        if cache_key in self._phone_cache:
            cached = self._phone_cache[cache_key]
            return cached if cached != '__FAIL__' else None

        self._phone_cache[cache_key] = '__PROCESSING__'
        try:
            phone = self._call_wechat(code, request)
            self._phone_cache[cache_key] = phone
            return phone
        except Exception as e:
            logger.error(f"微信调用失败: {str(e)}")
            self._phone_cache[cache_key] = '__FAIL__'
            return None

    def _resolve_wx_club_id(self, request, user):
        """手机号 code 与小程序 appid 绑定，须用对应俱乐部的 wx 配置换 token。"""
        from jituan.constants import CLUB_ID_DEFAULT
        from jituan.services.club_context import resolve_club_id_from_request
        from jituan.services.club_user import get_user_club_id

        club_id = resolve_club_id_from_request(request)
        if club_id == CLUB_ID_DEFAULT:
            user_club = get_user_club_id(user)
            if user_club:
                return user_club
        return club_id

    # ---------- 微信接口调用（按俱乐部 appid 换 token） ----------
    def _call_wechat(self, code, request):
        club_id = self._resolve_wx_club_id(request, request.user)
        token = self._get_new_token(club_id, force_refresh=True)
        url = f'https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token={token}'
        resp = requests.post(url, json={'code': code}, timeout=10)
        data = resp.json()
        errcode = data.get('errcode')
        if errcode == 0:
            return data['phone_info']['purePhoneNumber']
        if errcode == 40001:
            token = self._get_new_token(club_id, force_refresh=True)
            url = f'https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token={token}'
            resp = requests.post(url, json={'code': code}, timeout=10)
            data = resp.json()
            if data.get('errcode') == 0:
                return data['phone_info']['purePhoneNumber']
        raise Exception(f"微信错误(errcode={errcode}): {data.get('errmsg')}")

    def _get_new_token(self, club_id, force_refresh=False):
        from utils.weixin_token import get_club_mini_access_token

        token = get_club_mini_access_token(club_id, force_refresh=force_refresh)
        if token:
            return token
        raise Exception(f'俱乐部 {club_id} 缺少微信配置或获取 access_token 失败')

    # ---------- 工具 ----------
    def _get_or_create_boss(self, user):
        try:
            return UserBoss.query.get(user=user)
        except UserBoss.DoesNotExist:
            return UserBoss.query.create(user=user, nickname='微信用户')

    def _upload_avatar(self, user, file):
        try:
            uid = user.UserUID
            if not uid: return None
            old = user.Avatar
            if old: delete_from_oss(old)
            ext = os.path.splitext(file.name)[1] or '.jpg'
            name = f"{uuid.uuid4().hex}{ext}"
            path = f"avatar/{uid}/{name}"
            return path if upload_to_oss(file, path) else None
        except:
            return None


class DashouXinxiAPIView(APIView):
    """
    获取打手信息接口
    请求方式：POST
    认证方式：JWT Token
    返回：打手基本信息、资产信息、会员信息，金牌打手额外返回专属字段
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            # 1. 获取当前登录用户
            current_user = request.user
            yonghuid = current_user.UserUID

            # 2. 查询打手扩展表
            try:
                dashou_profile = current_user.DashouProfile
            except UserDashou.DoesNotExist:
                return Response({
                    'code': 400,
                    'message': '您不是打手身份',
                    'data': None
                }, status=status.HTTP_400_BAD_REQUEST)

            # 3. 构建基础信息响应（原有字段完全保留）
            data = {
                'dashounicheng': dashou_profile.nicheng or '',
                'zhanghaostatus': dashou_profile.zhanghaozhuangtai,
                'fengjin_yuanyin': getattr(dashou_profile, 'fengjin_yuanyin', '') or '',
                'zaixianzhuangtai': dashou_profile.zaixianzhuangtai,
                'dashouzhuangtai': dashou_profile.zhuangtai,
                'yongjin': str(dashou_profile.yue) if dashou_profile.yue is not None else '0.00',
                'dongjie_yue': str(getattr(dashou_profile, 'dongjie_yue', 0) or 0),
                'zonge': str(dashou_profile.zonge) if dashou_profile.zonge is not None else '0.00',
                'yajin': str(dashou_profile.yajin) if dashou_profile.yajin is not None else '0.00',
                'jiedanZongliang':dashou_profile.jiedanzongliang if dashou_profile.jiedanzongliang is not None else '0',
                'zuijinTixian': str(dashou_profile.jinritixian_jine) if dashou_profile.jinritixian_jine is not None else '0.00',
                'chengjiaoliang': dashou_profile.chengjiaozongliang,
                'jifen': dashou_profile.jifen,
                'chenghao': dashou_profile.chenghao or '',
            }

            # 身份状态：下拉刷新可即时更新入口（仅正常态为 1）
            try:
                gs = UserGuanshi.query.filter(user=current_user).first()
                data['guanshistatus'] = 1 if gs and int(getattr(gs, 'zhuangtai', 0) or 0) == 1 else 0
                data['guanshi_fengjin_yuanyin'] = (getattr(gs, 'fengjin_yuanyin', '') or '') if gs else ''
            except Exception:
                data['guanshistatus'] = 0
                data['guanshi_fengjin_yuanyin'] = ''
            try:
                zz = UserZuzhang.query.filter(user=current_user).first()
                data['zuzhangstatus'] = 1 if zz and int(getattr(zz, 'zhuangtai', 0) or 0) == 1 else 0
                data['zuzhang_fengjin_yuanyin'] = (getattr(zz, 'fengjin_yuanyin', '') or '') if zz else ''
            except Exception:
                data['zuzhangstatus'] = 0
                data['zuzhang_fengjin_yuanyin'] = ''
            try:
                sj = UserShangjia.query.filter(user=current_user).first()
                data['shangjiastatus'] = 1 if sj and int(getattr(sj, 'zhuangtai', 0) or 0) == 1 else 0
                data['shangjia_fengjin_yuanyin'] = (getattr(sj, 'fengjin_yuanyin', '') or '') if sj else ''
            except Exception:
                data['shangjiastatus'] = 0
                data['shangjia_fengjin_yuanyin'] = ''

            # 4. 查询会员信息（每次请求同步权益 + clumber，体验=正式）
            try:
                from jituan.services.huiyuan_bundle import refresh_grab_pool_membership_state
                from jituan.services.club_context import resolve_effective_club_id
                cid = resolve_effective_club_id(request, request.user)
                _, huiyuan_list = refresh_grab_pool_membership_state(yonghuid, club_id=cid)
            except Exception:
                huiyuan_list = []

            data['clumber'] = huiyuan_list

            # 5. 如果是金牌打手，添加专属字段
            if dashou_profile.chenghao == "金牌打手":
                data.update({
                    'jiedanZongliang': dashou_profile.jiedanzongliang,          # 接单总量
                    'jinriJiedan': str(dashou_profile.jinrishouyi) if dashou_profile.jinrishouyi is not None else '0.00',  # 今日接单金额
                    'zuijinTixian': str(dashou_profile.jinritixian_jine) if dashou_profile.jinritixian_jine is not None else '0.00',  # 最近提现金额（今日已提现）
                })

            # 角标附加字段（不影响旧客户端）
            try:
                from jituan.services.club_context import resolve_effective_club_id
                from orders.services.dashou_badges import dashou_badge_fields
                cid = resolve_effective_club_id(request, request.user)
                data.update(dashou_badge_fields(cid))
            except Exception:
                pass

            # 6. 返回成功响应
            return Response({
                'code': 200,
                'message': '获取成功',
                'data': data
            }, status=status.HTTP_200_OK)

        except Exception as e:
            # 生产环境应使用 logging 记录错误，而不是 print
            # logger.error(f"获取打手信息失败: {e}", exc_info=True)
            return Response({
                'code': 500,
                'message': f'服务器内部错误: {str(e)}',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class HuoQuYaoQingRenView(APIView):
    """
    获取当前打手的邀请人信息
    POST /yonghu/hqwdyqr
    Headers: Authorization: Bearer <token>
    返回邀请人的用户ID、昵称（来自老板扩展表）、头像相对URL
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            current_user = request.user                     # 当前登录用户
            yonghuid = current_user.UserUID                # 7位用户ID

            # 1. 校验打手身份及账号状态（只检查账号状态，不检查接单状态）
            try:
                dashou = UserDashou.query.get(user=current_user)
                if dashou.zhanghaozhuangtai != 1:
                    return Response({
                        'code': 403,
                        'message': '您的账号已被封禁，无法查看邀请人',
                        'data': None
                    })
            except UserDashou.DoesNotExist:
                return Response({
                    'code': 404,
                    'message': '您还不是打手，无法查看邀请人',
                    'data': None
                })

            # 2. 获取邀请人ID
            yaoqingren_id = dashou.yaoqingren
            if not yaoqingren_id:
                return Response({
                    'code': 404,
                    'message': '您没有邀请人',
                    'data': None
                })

            # 3. 查询邀请人主表（头像）
            try:
                inviter_main = User.query.get(UserUID=yaoqingren_id)
                touxiang = inviter_main.Avatar or ''
            except User.DoesNotExist:
                return Response({
                    'code': 404,
                    'message': '邀请人信息不存在',
                    'data': None
                })

            # 4. 查询老板扩展表（昵称），如果没有则使用空字符串
            nicheng = ''
            try:
                boss = UserBoss.query.get(user=inviter_main)
                nicheng = boss.nickname or ''
            except UserBoss.DoesNotExist:
                pass  # 邀请人可能不是老板，昵称留空

            response_data = {
                'uid': yaoqingren_id,
                'nicheng': nicheng,
                'touxiang': touxiang                   # 相对路径，前端自行拼接
            }

            return Response({
                'code': 200,
                'message': '获取成功',
                'data': response_data
            })

        except Exception as e:
            logger.error(f'获取邀请人失败: {str(e)}', exc_info=True)
            return Response({
                'code': 500,
                'message': '系统繁忙，请稍后重试',
                'data': None
            })


class ZaixianZhuangtaiAPIView(APIView):
    """
    更改打手在线状态接口
    请求方式：POST
    认证方式：JWT Token
    请求参数：status（0=离线，1=在线）
    返回：更新后的状态信息
    """

    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            # 1. 获取请求参数
            new_status = request.data.get('status')

            if new_status is None:
                return Response({
                    'code': 400,
                    'message': '缺少状态参数',
                    'data': None
                }, status=status.HTTP_400_BAD_REQUEST)

            # 转换为整数，确保是0或1
            try:
                new_status = int(new_status)
                if new_status not in [0, 1]:
                    return Response({
                        'code': 400,
                        'message': '状态参数必须是0或1',
                        'data': None
                    }, status=status.HTTP_400_BAD_REQUEST)
            except ValueError:
                return Response({
                    'code': 400,
                    'message': '状态参数必须是整数',
                    'data': None
                }, status=status.HTTP_400_BAD_REQUEST)

            # 2. 获取当前登录用户
            current_user = request.user
            yonghuid = current_user.UserUID



            # 3. 查询打手扩展表
            try:
                dashou_profile = current_user.DashouProfile
            except UserDashou.DoesNotExist:

                return Response({
                    'code': 400,
                    'message': '您不是打手身份',
                    'data': None
                }, status=status.HTTP_400_BAD_REQUEST)

            # 4. 检查账号状态（是否被封禁）
            if dashou_profile.zhanghaozhuangtai != 1:

                return Response({
                    'code': 403,
                    'message': '账号状态异常，无法更改在线状态',
                    'data': None
                }, status=status.HTTP_403_FORBIDDEN)

            # 5. 更新在线状态
            old_status = dashou_profile.zaixianzhuangtai

            # 如果状态没有变化，直接返回成功
            if old_status == new_status:

                return Response({
                    'code': 200,
                    'message': '状态未变化',
                    'data': {
                        'zaixianzhuangtai': new_status,
                        'status_text': '在线' if new_status == 1 else '离线'
                    }
                }, status=status.HTTP_200_OK)

            # 更新状态
            dashou_profile.zaixianzhuangtai = new_status
            dashou_profile.save()



            # 6. 返回成功响应
            return Response({
                'code': 200,
                'message': '状态更新成功',
                'data': {
                    'zaixianzhuangtai': new_status,
                    'status_text': '在线' if new_status == 1 else '离线'
                }
            }, status=status.HTTP_200_OK)

        except Exception as e:

            return Response({
                'code': 500,
                'message': f'服务器内部错误: {str(e)}',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class GuanshiXinxiView(APIView):
    """
    获取管事信息接口
    路径：/yonghu/guanshixinxi
    方法：POST
    权限：JWT认证
    功能：获取当前管事的详细信息，包括基本信息、分红信息以及已充值打手数量
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            # 获取当前用户（通过JWT认证）
            current_user = request.user

            # 查询管事扩展表
            try:
                guanshi_profile = UserGuanshi.query.get(user=current_user)

                # 🔥【新增】查询已充值打手数量（从分红表中统计）
                # Gsfenhong 表中 guanshi 字段存储管事ID（yonghuid）
                yichongzhi_count = Gsfenhong.query.filter(guanshi=current_user.UserUID).count()

                # 返回管事信息
                return Response({
                    'code': 200,
                    'msg': '获取成功',
                    'data': {
                        'gszhstatus': guanshi_profile.zhuangtai,                 # 管事账号状态
                        'fengjin_yuanyin': getattr(guanshi_profile, 'fengjin_yuanyin', '') or '',
                        'yaoqingzongshu': guanshi_profile.yaogingshuliang,       # 邀请打手总数
                        'fenyongzonge': float(guanshi_profile.chongzhifenrun),   # 充值分佣总额
                        'fenyongtixian': float(guanshi_profile.yue),             # 可提现余额
                        'dongjie_yue': float(getattr(guanshi_profile, 'dongjie_yue', 0) or 0),
                        'yichongzhiDashou': yichongzhi_count,                    # 已充值打手数量
                    }
                })

            except UserGuanshi.DoesNotExist:
                # 用户不是管事，返回错误
                return Response({
                    'code': 400,
                    'msg': '您还不是管事，请先注册',
                    'data': None
                }, status=400)

        except Exception as e:
            # 捕获所有异常，返回错误信息并记录日志
            logger.error(f"获取管事信息异常: {str(e)}", exc_info=True)
            return Response({
                'code': 500,
                'msg': f'获取信息失败: {str(e)}',
                'data': None
            }, status=500)


class ShangJiaXinXiView(APIView):
    """
    获取商家信息接口 (POST /yonghu/shangjiaxinxi)
    权限：需JWT认证
    返回：商家基本信息 + 称号列表 + 今日/本月统计
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            # 获取商家扩展资料
            shangjia = request.user.ShopProfile
        except ObjectDoesNotExist:
            return Response({
                'code': 4001,
                'msg': '您还未注册成为商家或商家资料不存在',
                'data': None
            }, status=status.HTTP_200_OK)

        user = request.user
        yonghuid = user.UserUID
        today = timezone.now().date()
        this_month_start = today.replace(day=1)

        # ------------------- 1. 从 UserShangjia 获取基础字段 -------------------
        response_data = {
            'nicheng': shangjia.nicheng or '',
            'sjzhzhuangtai': shangjia.zhuangtai,      # 商家账号状态
            'fengjin_yuanyin': getattr(shangjia, 'fengjin_yuanyin', '') or '',
            'fadanzong': shangjia.fabu,               # 发单总量（累计）
            'fabu': shangjia.fabu,
            'tuikuanzong': shangjia.tuikuan,          # 退款总量（累计）
            'sjyue': float(shangjia.yue),             # 商家余额
            # 'riliushui' 和 'yueliushui' 将从统计表计算，不再使用原字段
        }

        # ------------------- 2. 获取称号列表（仅限商家称号） -------------------
        chenghao_list = []
        # 查询当前用户的所有称号关联
        yonghu_chenghao_qs = YonghuChenghao.query.filter(yonghu=user).select_related('chenghao')
        for yc in yonghu_chenghao_qs:
            chenghao_obj = yc.chenghao
            # 只保留类型为“商家”的称号
            if chenghao_obj.leixing == 'shangjia':
                chenghao_list.append({
                    'mingcheng': chenghao_obj.mingcheng,
                    'texiao_json': chenghao_obj.texiao_miaoshu,  # 前端需要的特效描述
                    # 如果还需要其他字段（如图标URL）可在此添加，打手端有 texiao_json
                })
        response_data['chenghao_list'] = chenghao_list

        # ------------------- 3. 从 MerchantDailyStats 获取今日统计 -------------------
        try:
            today_stat = MerchantDailyStats.query.get(MerchantID=yonghuid, Date=today)
            jinri_paifa_dingdan = today_stat.AssignedOrderCount
            jinri_paifa_jine = float(today_stat.AssignedAmount)
            jinri_tuikuan_dingdan = today_stat.RefundOrderCount
        except MerchantDailyStats.DoesNotExist:
            jinri_paifa_dingdan = 0
            jinri_paifa_jine = 0.00
            jinri_tuikuan_dingdan = 0

        response_data['jinripaidan'] = jinri_paifa_dingdan          # 今日派单数（新增）
        response_data['jinrituikuan'] = jinri_tuikuan_dingdan       # 今日退款数（新增）
        response_data['riliushui'] = jinri_paifa_jine               # 今日流水（派发总金额）

        # ------------------- 4. 从 MerchantDailyStats 获取本月统计 -------------------
        month_stats = MerchantDailyStats.query.filter(
            MerchantID=yonghuid,
            Date__gte=this_month_start,
            Date__lte=today
        ).aggregate(
            total_paifa_jine=Sum('AssignedAmount'),
            total_paifa_dingdan=Sum('AssignedOrderCount')
        )
        yue_paifa_jine = float(month_stats.get('total_paifa_jine') or 0.00)
        # yue_paifa_dingdan = month_stats.get('total_paifa_dingdan') or 0  # 如果前端不需要可不加

        response_data['yueliushui'] = yue_paifa_jine                # 今月流水（派发总金额）

        # （可选）如果前端还需要今月派单总数，取消下面注释
        # response_data['jinyuepaidan'] = yue_paifa_dingdan

        return Response({
            'code': 200,
            'msg': '获取商家信息成功',
            'data': response_data
        }, status=status.HTTP_200_OK)


class ShangjiaNichengGengxinView(APIView):
    """商家老板修改昵称 POST /yonghu/shangjiagxnc"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        from merchant_ops.services.authz import is_merchant_owner

        if not is_merchant_owner(request.user):
            return Response({'code': 403, 'msg': '仅商家老板可修改昵称', 'data': None}, status=status.HTTP_200_OK)

        nicheng = (request.data.get('nicheng') or '').strip()
        if not nicheng:
            return Response({'code': 400, 'msg': '昵称不能为空', 'data': None}, status=status.HTTP_200_OK)
        if len(nicheng) > 20:
            return Response({'code': 400, 'msg': '昵称不能超过20字', 'data': None}, status=status.HTTP_200_OK)

        try:
            shangjia = request.user.ShopProfile
        except ObjectDoesNotExist:
            return Response({'code': 4001, 'msg': '商家资料不存在', 'data': None}, status=status.HTTP_200_OK)

        if UserShangjia.query.filter(nicheng=nicheng).exclude(user=request.user).exists():
            return Response({'code': 400, 'msg': '该昵称已被使用', 'data': None}, status=status.HTTP_200_OK)

        shangjia.nicheng = nicheng
        shangjia.save(update_fields=['nicheng'])

        return Response({'code': 200, 'msg': '昵称已更新', 'data': {'nicheng': nicheng}}, status=status.HTTP_200_OK)


class BossBiaoshiView(APIView):
    """
    获取用户的GoEasy标识 - 极简实现
    POST /yonghu/bossbiaoshi
    需要JWT Token认证
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        # 1. 直接从request.user获取用户
        user = request.user

        # 2. 获取yonghuid（确保用户已登录）
        if not user or not hasattr(user, 'UserUID'):
            return Response({
                'code': 401,
                'msg': '用户未认证或用户信息不完整'
            }, status=401)

        # 3. 生成标识：Boss + yonghuid（与前端约定）
        boss_biaoshi = f"Boss{user.UserUID}"

        # 4. 返回给前端
        return Response({
            'code': 200,
            'msg': '获取成功',
            'data': {
                'boss_biaoshi': boss_biaoshi,
                'yonghuid': user.UserUID,
                'user_type': user.UserType
            }
        })


class YaoqingmaView(APIView):
    """
    获取或生成管事邀请码
    访问路径: /api/yonghu/yaoqingma/
    请求方法: GET
    权限要求: JWT Token认证通过的用户
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        """
        获取当前用户的邀请码
        逻辑:
          1. 通过JWT获取当前用户 (request.user)
          2. 反向查询UserGuanshi扩展表
          3. 验证管事状态(zhuangtai=1)
          4. 已有邀请码直接返回，没有则生成并保存
        """
        # 获取当前认证用户
        current_user = request.user

        # 1. 使用反向查询获取管事扩展信息 (性能最优方式)
        try:
            guanshi_profile = current_user.GuanshiProfile
        except UserGuanshi.DoesNotExist:
            # 用户不是管事，没有扩展表记录
            return Response({
                'code': 403,
                'message': '当前用户不是管事身份，无法生成邀请码',
                'data': None
            }, status=status.HTTP_403_FORBIDDEN)

        # 2. 验证管事状态 (zhuangtai 必须等于 1)
        if guanshi_profile.zhuangtai != 1:
            return Response({
                'code': 403,
                'message': '管事账号状态异常，无法生成邀请码',
                'data': None
            }, status=status.HTTP_403_FORBIDDEN)

        # 3. 检查是否已存在邀请码
        existing_yaoqingma = guanshi_profile.yaoqingma

        # 如果存在且非空，直接返回
        if existing_yaoqingma and existing_yaoqingma.strip() != '':
            return Response({
                'code': 200,
                'message': '成功获取现有邀请码',
                'data': {
                    'yaoqingma': existing_yaoqingma,
                    'is_new': False  # 标记是否为新生成的
                }
            })

        # 4. 生成新的邀请码 (使用用户ID字符串)
        # 注意: 这里使用yonghuid，不是主键id，如你要求的"123456"这种形式
        yonghuid_str = str(current_user.UserUID)
        new_yaoqingma = CreateInvitationCode(yonghuid_str)

        # 5. 验证生成的邀请码格式
        if not VerifyInvitationCode(new_yaoqingma):
            return Response({
                'code': 500,
                'message': '邀请码生成失败，格式验证错误',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # 6. 保存到数据库 (使用事务确保数据一致性)
        try:
            with transaction.atomic():
                guanshi_profile.yaoqingma = new_yaoqingma
                guanshi_profile.save()
        except Exception as e:
            # 这里可以记录日志
            return Response({
                'code': 500,
                'message': f'保存邀请码到数据库失败: {str(e)}',
                'data': None
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # 7. 返回新生成的邀请码
        return Response({
            'code': 200,
            'message': '成功生成新的邀请码',
            'data': {
                'yaoqingma': new_yaoqingma,
                'is_new': True  # 标记是新生成的
            }
        })


class DashouZiliaoHuoquView(APIView):
    """
    打手资料获取接口
    URL: /yonghu/dszjxxhq
    方法: POST
    认证: JWT Token
    返回: 打手扩展表信息
    """

    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            # 使用select_related高效查询，避免N+1问题
            dashou = UserDashou.query.select_related('user').get(user=request.user)

            # 构建返回数据
            data = {
                'nicheng': dashou.nicheng if dashou.nicheng else '',
                'jieshao': dashou.jieshao if dashou.jieshao else '',
                'dianhua': dashou.dianhua if dashou.dianhua else '',
                'wechat': dashou.wechat if dashou.wechat else '',
                'uid': request.user.UserUID,  # 从主表获取UID
                'avatar': request.user.Avatar if request.user.Avatar else '',  # 头像相对URL
            }

            return Response({
                'code': 200,
                'msg': 'success',
                'data': data
            }, status=status.HTTP_200_OK)

        except UserDashou.DoesNotExist:
            logger.warning(f'打手扩展表不存在: user_id={request.user.id}')
            return Response({
                'code': 404,
                'msg': '打手信息不存在'
            }, status=status.HTTP_404_NOT_FOUND)

        except Exception as e:
            logger.error(f'获取打手资料异常: {str(e)}', exc_info=True)
            return Response({
                'code': 500,
                'msg': '服务器内部错误'
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class DashouGengxinView(APIView):
    """
    打手信息更新接口
    URL: /yonghu/dsgxxx
    方法: POST
    认证: JWT Token
    参数: nicheng(昵称), dianhua(电话), wechat(微信), jieshao(介绍)
    """

    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            # 获取请求数据
            nicheng = request.data.get('nicheng', '').strip()
            dianhua = request.data.get('dianhua', '').strip()
            wechat = request.data.get('wechat', '').strip()
            jieshao = request.data.get('jieshao', '').strip()

            # 数据验证
            validation_errors = self.validate_data(nicheng, dianhua, wechat, jieshao)
            if validation_errors:
                return Response({
                    'code': 400,
                    'msg': validation_errors
                }, status=status.HTTP_400_BAD_REQUEST)

            # 获取打手扩展表
            try:
                dashou = UserDashou.query.get(user=request.user)
            except UserDashou.DoesNotExist:
                return Response({
                    'code': 404,
                    'msg': '打手信息不存在'
                }, status=status.HTTP_404_NOT_FOUND)

            # 更新数据
            update_fields = []

            if nicheng is not None:
                dashou.nicheng = nicheng if nicheng != '' else None
                update_fields.append('nicheng')

            if dianhua is not None:
                dashou.dianhua = dianhua if dianhua != '' else None
                update_fields.append('dianhua')

            if wechat is not None:
                dashou.wechat = wechat if wechat != '' else None
                update_fields.append('wechat')

            if jieshao is not None:
                dashou.jieshao = jieshao if jieshao != '' else None
                update_fields.append('jieshao')

            # 保存更新（只更新修改的字段）
            if update_fields:
                dashou.save(update_fields=update_fields)

            return Response({
                'code': 200,
                'msg': '更新成功',
                'data': {
                    'nicheng': dashou.nicheng or '',
                    'dianhua': dashou.dianhua or '',
                    'wechat': dashou.wechat or '',
                    'jieshao': dashou.jieshao or ''
                }
            }, status=status.HTTP_200_OK)

        except Exception as e:
            logger.error(f'更新打手信息异常: {str(e)}', exc_info=True)
            return Response({
                'code': 500,
                'msg': '服务器内部错误'
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    def validate_data(self, nicheng, dianhua, wechat, jieshao):
        """数据验证方法"""
        errors = []

        # 1. 昵称验证（必填）
        if not nicheng:
            errors.append('昵称不能为空')
        elif len(nicheng) > 20:
            errors.append('昵称不能超过20个字符')

        # 2. 手机号验证（非必填，但填写时必须正确）
        if dianhua:
            # 手机号正则：1开头，11位数字
            phone_pattern = r'^1[3-9]\d{9}$'
            if not re.match(phone_pattern, dianhua):
                errors.append('手机号格式不正确')

        # 3. 微信号验证（非必填）
        if wechat and len(wechat) > 30:
            errors.append('微信号不能超过30个字符')

        # 4. 个人介绍验证
        if jieshao and len(jieshao) > 200:
            errors.append('个人介绍不能超过200个字符')

        return '; '.join(errors) if errors else None


class DashouJianquanView(APIView):
    """打手消息权限鉴权"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        # ========== 1. 通过 JWT 获取当前登录用户 ==========
        user = request.user  # User 实例

        # ========== 2. 检查是否为已注册打手 ==========
        try:
            dashou = user.DashouProfile  # OneToOneField 反向查询
        except UserDashou.DoesNotExist:
            return Response({
                'code': 0,
                'allow': 0,
                'msg': '您尚未注册打手身份，请先注册'
            })

        # ========== 3. 条件一：押金 >= 5 元 ==========
        if dashou.yajin and dashou.yajin >= 5:
            return Response({
                'code': 0,
                'allow': 1,
                'msg': '鉴权通过（押金满足条件）'
            })

        # ========== 4. 条件二：存在未过期会员 ==========
        # 高效查询：利用 yonghu_id + huiyuan_zhuangtai 复合索引
        huiyuan_list = Huiyuangoumai.query.filter(
            yonghu_id=user.UserUID,
            huiyuan_zhuangtai=1  # 只查状态为"可用"的，减少内存遍历
        ).only('id', 'daoqi_time', 'huiyuan_zhuangtai')

        for hy in huiyuan_list:
            if not hy.jiance_shifou_daoqi():  # 未过期返回 False
                return Response({
                    'code': 0,
                    'allow': 1,
                    'msg': '鉴权通过（会员未过期）'
                })

        # ========== 5. 条件三：存在订单状态为 2 或 8 的订单 ==========
        # 利用 jiedan_dashou_id + zhuangtai 复合索引，只查 1 条
        has_valid_order = Order.query.filter(
            PlayerID=user.UserUID,
            Status__in=[2, 8]
        ).only('id').exists()  # exists() 高性能判断，只发 SELECT 1 LIMIT 1

        if has_valid_order:
            return Response({
                'code': 0,
                'allow': 1,
                'msg': '鉴权通过（存在有效订单）'
            })

        # ========== 6. 以上均不满足 → 拒绝 ==========
        return Response({
            'code': 0,
            'allow': 0,
            'msg': '消息权限不足，请充值会员或联系管理员'
        })


class LaobanJianquanView(APIView):
    """老板消息权限鉴权"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        # ========== 1. 通过 JWT 获取当前登录用户 ==========
        user = request.user  # User 实例

        # ========== 2. 高效查询平台订单扩展表 ==========
        # BossID 为模型字段名（db_column=laoban_id）；订单状态用 Order.Status
        has_valid_order = PlatformOrderExt.query.filter(
            BossID=user.UserUID
        ).filter(
            Order__Status__in=[1, 2, 3, 4, 5, 6, 7, 8]
        ).only('id').exists()

        if has_valid_order:
            return Response({
                'code': 0,
                'allow': 1,
                'msg': '鉴权通过'
            })

        return Response({
            'code': 0,
            'allow': 0,
            'msg': '您暂无订单记录，消息功能暂不可用'
        })


def _mask_phone(phone):
    p = (phone or '').strip()
    if len(p) < 7:
        return p
    return p[:3] + '****' + p[-4:]


def _phone_auth_completed(user):
    if bool(getattr(user, 'PhoneVerified', False)):
        return True
    phone = (getattr(user, 'Phone', None) or '').strip()
    return len(phone) >= 11


class GetUserPhoneView(APIView):
    """
    点单提交页：查询当前用户手机号认证状态。
    POST /yonghu/hqshouji
    返回：phone / phone_masked / phone_verified / need_auth
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        user = request.user
        uid = getattr(user, 'UserUID', None)
        if uid:
            try:
                user = User.query.get(UserUID=uid)
            except User.DoesNotExist:
                pass
        verified = _phone_auth_completed(user)
        phone = (getattr(user, 'Phone', None) or '').strip()
        return Response({
            'code': 0,
            'msg': 'ok',
            'data': {
                'phone': phone if verified else '',
                'phone_masked': _mask_phone(phone) if verified else '',
                'phone_verified': verified,
                'need_auth': not verified,
            },
        })


class BindUserPhoneView(APIView):
    """
    点单提交页：一键获取手机号并完成认证（无需头像）。
    POST /yonghu/rzwlsjh
    参数：shoujihao_code（微信 getPhoneNumber 返回的 code）
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        code = (
            request.data.get('shoujihao_code')
            or request.data.get('code')
            or ''
        )
        if isinstance(code, str):
            code = code.strip()
        else:
            code = str(code or '').strip()
        if not code:
            return Response({
                'code': 6,
                'msg': '未收到手机号授权，请重新点击获取手机号',
                'data': None,
            })

        user = request.user
        uid = getattr(user, 'UserUID', None)
        if uid:
            try:
                user = User.query.get(UserUID=uid)
            except User.DoesNotExist:
                pass

        # 复用资料页的微信换号逻辑
        binder = UserInfoUpdateView()
        phone = binder._get_phone_safe(code, request)
        if not phone:
            return Response({
                'code': 6,
                'msg': '手机号获取失败，请重新授权',
                'data': None,
            })

        user.Phone = phone
        user.PhoneVerified = True
        user.save(update_fields=['Phone', 'PhoneVerified'])

        return Response({
            'code': 0,
            'msg': '认证成功',
            'data': {
                'phone': phone,
                'phone_masked': _mask_phone(phone),
                'phone_verified': True,
                'need_auth': False,
            },
        })
