"""users.views.pay - 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__)

class FaKuanPayView(APIView):
    """
    罚款微信支付下单接口（完全照抄 YajinGoumai）
    路径：POST /yonghu/fkjn
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            fadan_id = request.data.get('fadan_id')
            if not fadan_id:
                return Response({'code': 400, 'message': '参数不完整'}, status=status.HTTP_400_BAD_REQUEST)

            try:
                fadan = Penalty.query.get(id=fadan_id, PenalizedUserID=request.user.UserUID)
            except Penalty.DoesNotExist:
                return Response({'code': 404, 'message': '罚单不存在或不属于您'}, status=status.HTTP_404_NOT_FOUND)

            if fadan.Status not in [1, 3]:
                return Response({'code': 400, 'message': '罚单当前状态不可支付'}, status=status.HTTP_400_BAD_REQUEST)

            jine = float(fadan.FineAmount)

            from jituan.services.club_write import resolve_club_id_for_write
            from jituan.services.alipay_pay import alipay_pay_method_block_reason
            pay_method = (request.data.get('pay_method') or request.data.get('payMethod') or 'wechat').lower()
            pay_club = resolve_club_id_for_write(request, request.user)
            _alipay_block = alipay_pay_method_block_reason(pay_method, pay_club)
            if _alipay_block:
                return Response({
                    'code': 400,
                    'message': _alipay_block,
                    'msg': _alipay_block,
                }, status=status.HTTP_400_BAD_REQUEST)

            timestamp_ms = int(time.time() * 1000)
            timestamp_ns = int(time.time_ns() // 1000) % 1000000
            random_num = random.randint(0, 99)
            dingdanid = f"FK{timestamp_ms:011d}{timestamp_ns:06d}{random_num:02d}"

            from jituan.services.club_penalty import penalty_cz_club_id
            from jituan.services.club_user import get_payment_openid
            from products.czjilu_types import CZJILU_LEIXING_FAKUAN, penalty_cz_shuoming

            chongzhi_jilu = Czjilu.query.create(
                dingdan_id=dingdanid,
                zhuangtai=9,
                yonghuid=request.user.UserUID,
                jine=jine,
                leixing=CZJILU_LEIXING_FAKUAN,
                shuoming=penalty_cz_shuoming(fadan.id),
                club_id=penalty_cz_club_id(fadan),
            )

            # 生成支付参数（支持支付宝/微信）
            # 支付必须用「当前小程序/打手写入俱乐部」的 AppID+openid；
            # 与罚单 ClubID（申请人俱乐部）混用会导致部分人 openid/appid 不一致而下单失败。
            if pay_method == 'alipay':
                try:
                    from jituan.services.alipay_pay import generate_alipay_czjilu_pay_params
                    pay_params = generate_alipay_czjilu_pay_params(
                        dingdanid=dingdanid,
                        jine=jine,
                        subject=settings.FAKUAN_PAY_DESCRIPTION,
                        club_id=pay_club,
                    )
                except Exception as e:
                    chongzhi_jilu.delete()
                    logger.error(f"生成支付宝支付参数失败: {str(e)}")
                    return Response({
                        'code': 500,
                        'message': f'支付参数生成失败: {str(e)}',
                        'msg': f'支付参数生成失败: {str(e)}',
                    }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            else:
                pay_openid, pay_club = get_payment_openid(request, club_id=pay_club)
                if not pay_openid:
                    chongzhi_jilu.delete()
                    return Response({
                        'code': 400,
                        'message': '当前小程序未绑定微信openid，请退出后重新登录再支付',
                        'msg': '当前小程序未绑定微信openid，请退出后重新登录再支付',
                    }, status=status.HTTP_400_BAD_REQUEST)
                try:
                    pay_params = self.generate_wechat_pay_params(
                        dingdanid=dingdanid,
                        jine=jine,
                        openid=pay_openid,
                        pay_type='fakuan',
                        club_id=pay_club,
                    )
                except Exception as e:
                    chongzhi_jilu.delete()
                    logger.error(f"生成微信支付参数失败: {str(e)}")
                    return Response({
                        'code': 500,
                        'message': f'支付参数生成失败: {str(e)}',
                        'msg': f'支付参数生成失败: {str(e)}',
                    }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

            return Response({
                'code': 200,
                'message': '支付参数生成成功',
                'msg': '支付参数生成成功',
                'payParams': pay_params,
                'dingdanid': dingdanid
            }, status=status.HTTP_200_OK)

        except Exception as e:
            logger.error(f"罚款支付下单接口异常: {str(e)}", exc_info=True)
            return Response({
                'code': 500,
                'message': '服务器内部错误，请稍后重试',
                'msg': '服务器内部错误，请稍后重试',
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    def generate_wechat_pay_params(self, dingdanid, jine, openid, pay_type, club_id=None):
        from jituan.services.club_write import resolve_club_id_for_write
        from jituan.services.wechat_pay import (
            get_wechat_v2_config,
            log_wechat_v2_pay_diag,
            parse_wechat_v2_unifiedorder_response,
        )
        pay_club = club_id or resolve_club_id_for_write(self.request, self.request.user)
        pay_cfg = get_wechat_v2_config(pay_club)
        uid = getattr(self.request.user, 'UserUID', '')
        log_wechat_v2_pay_diag(
            pay_cfg, scene='fakuan', openid=openid, order_hint=f'{dingdanid} uid={uid}',
        )
        APPID = pay_cfg['appid']
        MCHID = pay_cfg['mch_id']
        KEY = pay_cfg['key']
        if not APPID or not MCHID or not KEY:
            raise Exception(
                f'俱乐部{pay_club}微信支付配置不完整(appid/mch_id/密钥)，请联系管理员'
            )

        if pay_type == 'fakuan':
            PAY_DESCRIPTION = settings.FAKUAN_PAY_DESCRIPTION
            NOTIFY_URL = settings.FAKUAN_PAY_NOTIFY_URL   # 回调地址在这里
        else:
            raise Exception('未知的支付类型')

        from jituan.constants import FUBEI_BIZ_CZJILU
        from jituan.services.mini_pay_router import maybe_create_fubei_jsapi_params
        fb_params = maybe_create_fubei_jsapi_params(
            club_id=pay_club,
            out_trade_no=dingdanid,
            amount_yuan=jine,
            body=PAY_DESCRIPTION,
            openid=openid,
            notify_url=NOTIFY_URL,
            biz_type=FUBEI_BIZ_CZJILU,
        )
        if fb_params is not None:
            return fb_params

        nonce_str = ''.join(random.choices(string.ascii_letters + string.digits, k=32))
        total_fee = yuan_to_fen(jine)
        trade_type = 'JSAPI'
        user_ip = self.get_client_ip()

        # 第一次签名
        sign_data = {
            'appid': APPID,
            'mch_id': MCHID,
            'nonce_str': nonce_str,
            'body': PAY_DESCRIPTION,
            'out_trade_no': dingdanid,
            'total_fee': total_fee,
            'spbill_create_ip': user_ip,
            'notify_url': NOTIFY_URL,    # 关键：把回调地址传入
            'trade_type': trade_type,
            'openid': openid
        }

        sorted_sign_data = sorted(sign_data.items(), key=lambda x: x[0])
        sign_string = '&'.join([f"{key}={value}" for key, value in sorted_sign_data if value])
        sign_string += f'&key={KEY}'
        sign = hashlib.md5(sign_string.encode('utf-8')).hexdigest().upper()

        # 构建 XML，这里必须包含 notify_url
        xml_data = f"""<xml>
<appid>{APPID}</appid>
<body>{PAY_DESCRIPTION}</body>
<mch_id>{MCHID}</mch_id>
<nonce_str>{nonce_str}</nonce_str>
<notify_url>{NOTIFY_URL}</notify_url>
<openid>{openid}</openid>
<out_trade_no>{dingdanid}</out_trade_no>
<spbill_create_ip>{user_ip}</spbill_create_ip>
<total_fee>{total_fee}</total_fee>
<trade_type>{trade_type}</trade_type>
<sign>{sign}</sign>
</xml>"""

        response = requests.post(
            'https://api.mch.weixin.qq.com/pay/unifiedorder',
            data=xml_data.encode('utf-8'),
            headers={'Content-Type': 'application/xml'},
            timeout=10
        )

        logger.info(f"罚款微信支付响应: {response.text}")

        parsed = parse_wechat_v2_unifiedorder_response(
            response.content, log_prefix=f'FAKUAN_UNIFIED club={pay_club}',
        )
        prepay_id = parsed['prepay_id']
        timestamp = str(int(time.time()))
        nonce_str = ''.join(random.choices(string.ascii_letters + string.digits, k=32))
        package = f'prepay_id={prepay_id}'
        sign_type = 'MD5'

        pay_sign_data = {
            'appId': APPID,
            'timeStamp': timestamp,
            'nonceStr': nonce_str,
            'package': package,
            'signType': sign_type
        }

        sorted_pay_sign_data = sorted(pay_sign_data.items(), key=lambda x: x[0])
        pay_sign_string = '&'.join([f"{key}={value}" for key, value in sorted_pay_sign_data])
        pay_sign_string += f'&key={KEY}'
        pay_sign = hashlib.md5(pay_sign_string.encode('utf-8')).hexdigest().upper()

        return {
            'appId': APPID,
            'timeStamp': timestamp,
            'nonceStr': nonce_str,
            'package': package,
            'signType': sign_type,
            'paySign': pay_sign
        }

    def get_client_ip(self):
        request = self.request
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR')
        return ip if ip else '127.0.0.1'


class FaKuanHuitiaoView(View):

    """
    罚款支付回调接口（严格借鉴 YajinHuitiao 接口，参数一个不少）
    路径：POST /yonghu/fk_huitiao
    """
    def post(self, request):
        try:
            # 1. 解析微信回调XML数据
            request_body = request.body
            logger.info(f"收到微信支付回调，原始数据: {request_body.decode('utf-8')}")

            root = ET.fromstring(request_body)

            # 获取关键字段
            return_code = root.find('return_code').text
            result_code = root.find('result_code').text
            out_trade_no = root.find('out_trade_no').text  # 订单ID
            transaction_id = root.find('transaction_id').text  # 微信交易号
            total_fee = int(root.find('total_fee').text)  # 支付金额（分）

            logger.info(f"回调数据解析: 订单{out_trade_no}, 状态{return_code}/{result_code}, 金额{total_fee}分")

            # 2. 验证基本返回状态
            if return_code != 'SUCCESS' or result_code != 'SUCCESS':
                logger.warning(f"微信支付回调状态异常: {return_code}/{result_code}")
                return self.wechat_response('FAIL', '支付失败')

            # 3. 验证签名（确保是微信发来的）
            from jituan.services.wechat_pay import club_id_from_czjilu, verify_wechat_v2_signature
            notify_club = club_id_from_czjilu(out_trade_no)
            if not verify_wechat_v2_signature(request_body, notify_club):
                logger.error(f"签名验证失败: {out_trade_no}")
                return self.wechat_response('FAIL', '签名验证失败')

            # 4. 查询订单
            try:
                order = Czjilu.query.get(dingdan_id=out_trade_no)
                logger.info(f"找到订单: {order.dingdan_id}, 状态: {order.zhuangtai}, 金额: {order.jine}")
            except Czjilu.DoesNotExist:
                logger.error(f"订单不存在: {out_trade_no}")
                return self.wechat_response('FAIL', '订单不存在')

            from products.czjilu_types import is_penalty_czjilu
            if not is_penalty_czjilu(order):
                logger.error(f"非罚款支付订单误入罚款回调: {out_trade_no}, leixing={order.leixing}")
                return self.wechat_response('FAIL', '订单类型错误')

            # 5. 检查订单状态是否为未支付（9）
            if order.zhuangtai != 9:
                logger.warning(f"订单状态不是未支付: {order.dingdan_id}, 当前状态: {order.zhuangtai}")
                try:
                    from jituan.services.szjilu_accounting import repair_wechat_income_if_missing
                    repair_wechat_income_if_missing(
                        Decimal(str(order.jine)),
                        getattr(order, 'club_id', None),
                        biz_ref=f'cz:{out_trade_no}',
                    )
                except Exception as exc:
                    logger.error('罚款回调补记收支失败 %s: %s', out_trade_no, exc, exc_info=True)
                return self.wechat_response('SUCCESS', 'OK')

            # 6. 验证金额（微信返回的是分，需要转换）
            wechat_amount_yuan = total_fee / 100  # 转换为元
            order_amount_yuan = float(order.jine)  # 订单金额（元）

            # 允许1分钱的误差（浮点数比较）
            if abs(wechat_amount_yuan - order_amount_yuan) > 0.01:
                logger.error(f"金额不一致: 订单金额{order_amount_yuan}元, 支付金额{wechat_amount_yuan}元")
                return self.wechat_response('FAIL', '金额不一致')

            # 7. 调用统一履约函数（事务 + 行锁 + 幂等 + 罚单状态 + 分红 + 收支记录）
            from jituan.services.czjilu_fulfill import fulfill_fakuan, CzjiluFulfillError
            try:
                fulfill_fakuan(out_trade_no, paid_amount_yuan=wechat_amount_yuan)
            except CzjiluFulfillError as exc:
                logger.error('罚款回调履约失败 %s: %s', out_trade_no, exc)
                return self.wechat_response('FAIL', str(exc))

            # 8. 返回成功响应给微信
            logger.info(f"罚款支付回调处理完成: {out_trade_no}")
            return self.wechat_response('SUCCESS', 'OK')

        except ET.ParseError as e:
            logger.error(f"XML解析失败: {str(e)}")
            return self.wechat_response('FAIL', '数据格式错误')
        except Exception as e:
            logger.error(f"罚款支付回调处理异常: {str(e)}", exc_info=True)
            # 返回FAIL让微信重试
            return self.wechat_response('FAIL', '处理异常')

    def verify_signature(self, xml_data):
        """验证微信支付签名（完全照搬 YajinHuitiao 的 verify_signature）"""
        try:
            root = ET.fromstring(xml_data)
            sign = root.find('sign').text

            if not sign:
                logger.error("签名字段不存在")
                return False

            # 获取所有参数并排除sign
            params = {}
            for child in root:
                if child.tag != 'sign' and child.text is not None:
                    params[child.tag] = child.text

            # 按照参数名ASCII码从小到大排序
            sorted_params = sorted(params.items(), key=lambda x: x[0])

            # 拼接字符串
            sign_string = '&'.join([f"{key}={value}" for key, value in sorted_params if value])
            sign_string += f'&key={settings.WEIXIN_SHANGHUMIYAO}'

            # 计算签名
            calculated_sign = hashlib.md5(sign_string.encode('utf-8')).hexdigest().upper()

            is_valid = sign == calculated_sign
            if not is_valid:
                logger.error(f"签名验证失败: 计算签名{calculated_sign}, 收到签名{sign}")

            return is_valid

        except Exception as e:
            logger.error(f"验证签名异常: {str(e)}")
            return False

    def wechat_response(self, return_code, return_msg):
        """返回微信支付回调响应（完全照搬 YajinHuitiao 的 wechat_response）"""
        xml_response = f"""<xml>
<return_code><![CDATA[{return_code}]]></return_code>
<return_msg><![CDATA[{return_msg}]]></return_msg>
</xml>"""

        logger.info(f"返回微信响应: {return_code} - {return_msg}")
        return HttpResponse(xml_response, content_type='application/xml')


class FaKuanPayPollView(APIView):
    """罚款支付轮询接口（借鉴 YajinLunxun）"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            dingdanid = request.data.get('dingdanid')
            if not dingdanid:
                return Response({'code': 400, 'message': '订单ID不能为空'})

            order = Czjilu.query.get(dingdan_id=dingdanid, yonghuid=request.user.UserUID)
            # 订单未支付/已关单待补履约时，尝试补偿履约（回调延迟/失败时通过轮询兜底）
            from products.czjilu_types import is_czjilu_fulfillable_status
            if is_czjilu_fulfillable_status(order.zhuangtai):
                from jituan.services.czjilu_fulfill import confirm_czjilu_paid, CzjiluFulfillError
                try:
                    confirm_czjilu_paid(dingdanid, request.user.UserUID, expected_leixing=6)
                    order = Czjilu.query.get(dingdan_id=dingdanid, yonghuid=request.user.UserUID)
                except CzjiluFulfillError as exc:
                    logger.info(f"罚款确认未就绪 {dingdanid}: {exc}")
                    return Response({'code': 400, 'message': '订单尚未支付'})

            if order.zhuangtai == 3:
                return Response({'code': 200, 'message': '支付成功'})
            else:
                return Response({'code': 400, 'message': '订单尚未支付'})
        except Czjilu.DoesNotExist:
            return Response({'code': 400, 'message': '订单不存在'})
        except Exception as e:
            logger.error(f"轮询异常: {str(e)}")
            return Response({'code': 500, 'message': '服务器内部错误'})


class FaKuanPayFailView(APIView):
    """罚款支付失败处理接口（借鉴 YajinShibai）"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            dingdanid = request.data.get('dingdanid')
            if not dingdanid:
                return Response({'code': 400, 'message': '订单ID不能为空'})

            order = Czjilu.query.get(dingdan_id=dingdanid, yonghuid=request.user.UserUID)
            if order.zhuangtai == 9:  # 未支付：关单不删
                from products.czjilu_types import close_unpaid_czjilu
                close_unpaid_czjilu(order, reason='用户取消支付')
                logger.info(f"用户{request.user.UserUID}关闭未支付罚款订单(不删): {dingdanid}")
                return Response({'code': 200, 'message': '订单已取消'})
            else:
                return Response({'code': 400, 'message': '订单状态不是未支付，无法取消'})
        except Czjilu.DoesNotExist:
            return Response({'code': 400, 'message': '订单不存在'})
        except Exception as e:
            logger.error(f"支付失败处理异常: {str(e)}")
            return Response({'code': 500, 'message': '服务器内部错误'})


class KaohePayView(APIView):
    """
    考核支付下单接口
    路径：POST /yonghu/khzf
    参数：
        tag_id: 标签ID
        game_nick: 游戏昵称
        remark: 备注
        reapply: 是否再次申请 (true/false)
        jilu_id: 再次申请时的原记录ID（reapply=true时必传）
        shenheguan_id: 指定考核官ID（可选）
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            user = request.user
            tag_id = request.data.get('tag_id')
            game_nick = request.data.get('game_nick', '').strip()
            remark = request.data.get('remark', '').strip()
            reapply = request.data.get('reapply', False)
            jilu_id = request.data.get('jilu_id') if reapply else None
            shenheguan_id = request.data.get('shenheguan_id', '').strip()

            logger.info(f"用户 {user.UserUID} 发起考核支付申请，标签ID: {tag_id}, 再次申请: {reapply}")

            if not tag_id or not game_nick:
                logger.warning(f"用户 {user.UserUID} 参数不完整: tag_id={tag_id}, game_nick={game_nick}")
                return Response({'code': 400, 'message': '标签ID和游戏昵称不能为空'})

            # 1. 检查是否有未支付的考核订单
            """unpaid_order = Czjilu.query.filter(
                UserUID=user.UserUID,
                leixing=5,
                zhuangtai=9
            ).exists()
            if unpaid_order:
                logger.warning(f"用户 {user.UserUID} 存在未支付的考核订单")
                return Response({'code': 400, 'message': '您有未支付的考核订单，请先完成支付或取消'})"""

            # 2. 验证标签
            try:
                chenghao = Chenghao.query.get(id=tag_id)
                logger.info(f"标签验证通过: {chenghao.mingcheng}")
            except Chenghao.DoesNotExist:
                logger.error(f"标签不存在: tag_id={tag_id}")
                return Response({'code': 404, 'message': '标签不存在'})

            # 3. 检查用户是否已拥有该标签
            if YonghuChenghao.query.filter(yonghu=user, chenghao=chenghao).exists():
                logger.warning(f"用户 {user.UserUID} 已拥有标签 {chenghao.mingcheng}")
                return Response({'code': 400, 'message': '您已拥有该标签，无需重复考核'})

            # 4. 检查是否有未完成的申请
            if ShenheJilu.query.filter(
                    shenqingren_id=user.UserUID,
                    chenghao=chenghao,
                    zhuangtai__in=[0, 3]  # 审核中、待审核
            ).exists():
                logger.warning(f"用户 {user.UserUID} 有未完成的考核申请")
                return Response({'code': 400, 'message': '您有未完成的考核申请，请等待结果'})

            # 5. 计算考核次数和费用
            if reapply and jilu_id:
                last = ShenheJilu.query.filter(
                    jilu_id=jilu_id,
                    shenqingren_id=user.UserUID,
                    zhuangtai=2
                ).first()
                if not last:
                    logger.warning(f"用户 {user.UserUID} 再次申请失败：原记录不存在或状态不是未通过，jilu_id={jilu_id}")
                    return Response({'code': 400, 'message': '原记录不存在或状态不是未通过'})
                new_cishu = last.cishu + 1
                logger.info(f"再次申请，原记录ID: {jilu_id}，新次数: {new_cishu}")
            else:
                max_cishu = ShenheJilu.query.filter(
                    shenqingren_id=user.UserUID,
                    chenghao=chenghao,
                    zhuangtai__in=[1, 2]
                ).aggregate(max=models.Max('cishu'))['max'] or 0
                new_cishu = max_cishu + 1
                logger.info(f"首次申请，计算次数: {new_cishu}")

            # 6. 获取本次费用
            fee = get_tag_fee(chenghao, new_cishu)
            logger.info(f"本次考核费用: {fee}元，次数: {new_cishu}")

            if fee <= 0:
                logger.warning(f"费用为0，应使用免费申请接口，tag_id={tag_id}, cishu={new_cishu}")
                return Response({'code': 400, 'message': '本次费用为0，请直接使用免费申请接口'})

            from jituan.services.club_write import resolve_club_id_for_write
            from jituan.services.alipay_pay import alipay_pay_method_block_reason
            pay_method = (request.data.get('pay_method') or request.data.get('payMethod') or 'wechat').lower()
            pay_club = resolve_club_id_for_write(request, user)
            _alipay_block = alipay_pay_method_block_reason(pay_method, pay_club)
            if _alipay_block:
                return Response({'code': 400, 'message': _alipay_block})

            # 7. 生成订单ID
            timestamp_ms = int(time.time() * 1000)
            timestamp_ns = int(time.time_ns() // 1000) % 1000000
            random_num = random.randint(0, 99)
            dingdanid = f"KH{timestamp_ms:011d}{timestamp_ns:06d}{random_num:02d}"
            logger.info(f"生成订单ID: {dingdanid}")

            # 8. 创建支付订单（Czjilu）
            order = Czjilu.query.create(
                dingdan_id=dingdanid,
                zhuangtai=9,  # 未支付
                yonghuid=user.UserUID,
                jine=float(fee),
                leixing=5,  # 5-考核支付
                shuoming=settings.KAOHE_PAY_DESCRIPTION,
                club_id=pay_club,
            )
            logger.info(f"创建支付订单成功: {dingdanid}")

            # 9. 保存考核支付临时上下文（用于回调时取业务参数）
            try:
                KaohePayTemp.query.create(
                    dingdan_id=dingdanid,
                    yonghuid=user.UserUID,
                    tag_id=tag_id,
                    game_nick=game_nick,
                    remark=remark,
                    reapply=reapply,
                    jilu_id=jilu_id if reapply else None,
                    shenheguan_id=shenheguan_id,
                    cishu=new_cishu,
                    fee=fee,
                )
                logger.info(f"保存临时上下文成功: {dingdanid}")
            except Exception as e:
                logger.error(f"保存临时上下文失败: {str(e)}", exc_info=True)
                # 临时表保存失败应该回滚订单
                order.delete()
                return Response({'code': 500, 'message': '系统异常，请稍后重试'})

            # 10. 构造 attach 参数（只传订单ID，确保不超127字节）
            attach_str = json.dumps({'oid': dingdanid}, ensure_ascii=False)
            logger.info(f"attach内容: {attach_str}，长度: {len(attach_str.encode('utf-8'))}字节")

            # 11. 生成支付参数（支持支付宝/微信）
            if pay_method == 'alipay':
                try:
                    from jituan.services.alipay_pay import generate_alipay_czjilu_pay_params
                    pay_params = generate_alipay_czjilu_pay_params(
                        dingdanid=dingdanid,
                        jine=float(fee),
                        subject=settings.KAOHE_PAY_DESCRIPTION,
                        club_id=pay_club,
                    )
                    logger.info(f"支付宝支付参数生成成功: {dingdanid}")
                except Exception as e:
                    order.delete()
                    try:
                        KaohePayTemp.query.filter(dingdan_id=dingdanid).delete()
                    except Exception as del_e:
                        logger.error(f"删除临时表记录失败: {str(del_e)}")
                    logger.error(f"生成支付宝支付参数失败: {str(e)}", exc_info=True)
                    return Response({'code': 500, 'message': f'支付参数生成失败: {str(e)}'})
            else:
                try:
                    pay_params = self.generate_wechat_pay_params(
                        dingdanid=dingdanid,
                        jine=float(fee),
                        openid=user.OpenID,
                        attach=attach_str,
                        pay_type='kaohe'
                    )
                    logger.info(f"微信支付参数生成成功: {dingdanid}")
                except Exception as e:
                    order.delete()  # 下单失败，删除订单记录
                    # 同时删除临时表记录
                    try:
                        KaohePayTemp.query.filter(dingdan_id=dingdanid).delete()
                    except Exception as del_e:
                        logger.error(f"删除临时表记录失败: {str(del_e)}")
                    logger.error(f"生成微信支付参数失败: {str(e)}", exc_info=True)
                    return Response({'code': 500, 'message': f'支付参数生成失败: {str(e)}'})

            return Response({
                'code': 200,
                'message': '支付参数生成成功',
                'payParams': pay_params,
                'dingdanid': dingdanid
            })

        except Exception as e:
            logger.error(f"考核支付下单异常: {str(e)}", exc_info=True)
            return Response({'code': 500, 'message': '服务器内部错误'})

    def generate_wechat_pay_params(self, dingdanid, jine, openid, attach, pay_type):
        """生成微信JSAPI支付参数"""
        from jituan.services.club_write import resolve_club_id_for_write
        from jituan.services.wechat_pay import get_wechat_v2_config
        pay_cfg = get_wechat_v2_config(resolve_club_id_for_write(self.request, self.request.user))
        APPID = pay_cfg['appid']
        MCHID = pay_cfg['mch_id']
        KEY = pay_cfg['key']

        if pay_type == 'kaohe':
            PAY_DESCRIPTION = settings.KAOHE_PAY_DESCRIPTION
            NOTIFY_URL = settings.KAOHE_PAY_NOTIFY_URL
        else:
            raise Exception('未知支付类型')

        from jituan.constants import FUBEI_BIZ_CZJILU
        from jituan.services.mini_pay_router import maybe_create_fubei_jsapi_params
        _club = resolve_club_id_for_write(self.request, self.request.user)
        fb_params = maybe_create_fubei_jsapi_params(
            club_id=_club,
            out_trade_no=dingdanid,
            amount_yuan=jine,
            body=PAY_DESCRIPTION,
            openid=openid,
            notify_url=NOTIFY_URL,
            biz_type=FUBEI_BIZ_CZJILU,
        )
        if fb_params is not None:
            return fb_params

        nonce_str = ''.join(random.choices(string.ascii_letters + string.digits, k=32))
        total_fee = yuan_to_fen(jine)
        trade_type = 'JSAPI'
        user_ip = self.get_client_ip()

        logger.info(f"微信支付下单参数: out_trade_no={dingdanid}, total_fee={total_fee}, openid={openid}, ip={user_ip}")

        # 第一次签名（统一下单）
        sign_data = {
            'appid': APPID,
            'mch_id': MCHID,
            'nonce_str': nonce_str,
            'body': PAY_DESCRIPTION,
            'out_trade_no': dingdanid,
            'total_fee': total_fee,
            'spbill_create_ip': user_ip,
            'notify_url': NOTIFY_URL,
            'trade_type': trade_type,
            'openid': openid,
            'attach': attach,
        }

        sorted_sign_data = sorted(sign_data.items(), key=lambda x: x[0])
        sign_string = '&'.join([f"{key}={value}" for key, value in sorted_sign_data if value])
        sign_string += f'&key={KEY}'
        sign = hashlib.md5(sign_string.encode('utf-8')).hexdigest().upper()

        # 构建XML请求体
        xml_data = f"""<xml>
<appid>{APPID}</appid>
<body>{PAY_DESCRIPTION}</body>
<mch_id>{MCHID}</mch_id>
<nonce_str>{nonce_str}</nonce_str>
<notify_url>{NOTIFY_URL}</notify_url>
<openid>{openid}</openid>
<out_trade_no>{dingdanid}</out_trade_no>
<spbill_create_ip>{user_ip}</spbill_create_ip>
<total_fee>{total_fee}</total_fee>
<trade_type>{trade_type}</trade_type>
<attach><![CDATA[{attach}]]></attach>
<sign>{sign}</sign>
</xml>"""

        logger.info(f"微信支付请求XML: {xml_data[:500]}...")  # 只打印前500字符

        response = requests.post(
            'https://api.mch.weixin.qq.com/pay/unifiedorder',
            data=xml_data.encode('utf-8'),
            headers={'Content-Type': 'application/xml'},
            timeout=10
        )

        logger.info(f"微信支付响应: {response.text}")

        root = ET.fromstring(response.content)

        # 安全获取return_code
        return_code_node = root.find('return_code')
        if return_code_node is None or return_code_node.text is None:
            logger.error(f"微信返回缺少return_code节点，原始内容: {response.text}")
            raise Exception('微信支付接口异常，缺少return_code')
        return_code = return_code_node.text

        # 安全获取result_code
        result_code_node = root.find('result_code')
        if result_code_node is None or result_code_node.text is None:
            logger.error(f"微信返回缺少result_code节点，原始内容: {response.text}")
            raise Exception('微信支付接口异常，缺少result_code')
        result_code = result_code_node.text

        if return_code == 'SUCCESS' and result_code == 'SUCCESS':
            prepay_id_node = root.find('prepay_id')
            if prepay_id_node is None or prepay_id_node.text is None:
                logger.error(f"统一下单成功但未返回prepay_id，原始内容: {response.text}")
                raise Exception('微信支付接口异常，缺少prepay_id')
            prepay_id = prepay_id_node.text

            timestamp = str(int(time.time()))
            nonce_str = ''.join(random.choices(string.ascii_letters + string.digits, k=32))
            package = f'prepay_id={prepay_id}'
            sign_type = 'MD5'

            # 第二次签名（前端调起支付用）
            pay_sign_data = {
                'appId': APPID,
                'timeStamp': timestamp,
                'nonceStr': nonce_str,
                'package': package,
                'signType': sign_type,
            }
            sorted_pay_sign_data = sorted(pay_sign_data.items(), key=lambda x: x[0])
            pay_sign_string = '&'.join([f"{key}={value}" for key, value in sorted_pay_sign_data])
            pay_sign_string += f'&key={KEY}'
            pay_sign = hashlib.md5(pay_sign_string.encode('utf-8')).hexdigest().upper()

            logger.info(f"微信支付预下单成功，prepay_id={prepay_id}")

            return {
                'appId': APPID,
                'timeStamp': timestamp,
                'nonceStr': nonce_str,
                'package': package,
                'signType': sign_type,
                'paySign': pay_sign,
            }
        else:
            # 安全获取错误信息
            err_code_node = root.find('err_code')
            err_code = err_code_node.text if err_code_node is not None else '未知错误码'
            err_code_des_node = root.find('err_code_des')
            err_code_des = err_code_des_node.text if err_code_des_node is not None else '未知错误描述'
            error_details = f"return_code: {return_code}, result_code: {result_code}, err_code: {err_code}, err_code_des: {err_code_des}"
            logger.error(f"微信支付下单失败: {error_details}")
            raise Exception(f"微信支付下单失败: {error_details}")

    def get_client_ip(self):
        """获取客户端IP"""
        request = self.request
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR', '127.0.0.1')
        return ip if ip else '127.0.0.1'


class KaohePayCallbackView(View):
    """
    考核支付回调接口
    路径：/yonghu/kh_huitiao
    """

    def post(self, request):
        try:
            request_body = request.body
            logger.info(f"收到考核支付回调: {request_body.decode('utf-8')}")

            root = ET.fromstring(request_body)

            # 安全获取return_code
            return_code_node = root.find('return_code')
            if return_code_node is None or return_code_node.text is None:
                logger.error(f"微信回调XML缺少return_code节点，原始内容: {request_body.decode('utf-8')}")
                return self.wechat_response('FAIL', '数据格式错误')
            return_code = return_code_node.text

            # 安全获取result_code
            result_code_node = root.find('result_code')
            if result_code_node is None or result_code_node.text is None:
                logger.error(f"微信回调XML缺少result_code节点，原始内容: {request_body.decode('utf-8')}")
                return self.wechat_response('FAIL', '数据格式错误')
            result_code = result_code_node.text

            # 安全获取out_trade_no
            out_trade_no_node = root.find('out_trade_no')
            if out_trade_no_node is None or out_trade_no_node.text is None:
                logger.error(f"微信回调XML缺少out_trade_no节点，原始内容: {request_body.decode('utf-8')}")
                return self.wechat_response('FAIL', '数据格式错误')
            out_trade_no = out_trade_no_node.text

            # 安全获取transaction_id
            transaction_id_node = root.find('transaction_id')
            if transaction_id_node is None or transaction_id_node.text is None:
                logger.error(f"微信回调XML缺少transaction_id节点，原始内容: {request_body.decode('utf-8')}")
                return self.wechat_response('FAIL', '数据格式错误')
            transaction_id = transaction_id_node.text

            # 安全获取total_fee
            total_fee_node = root.find('total_fee')
            if total_fee_node is None or total_fee_node.text is None:
                logger.error(f"微信回调XML缺少total_fee节点，原始内容: {request_body.decode('utf-8')}")
                return self.wechat_response('FAIL', '数据格式错误')
            total_fee = int(total_fee_node.text)

            logger.info(
                f"回调参数解析成功: out_trade_no={out_trade_no}, transaction_id={transaction_id}, total_fee={total_fee}")

            if return_code != 'SUCCESS' or result_code != 'SUCCESS':
                logger.warning(f"支付状态异常: return_code={return_code}, result_code={result_code}")
                return self.wechat_response('FAIL', '支付失败')

            # 验证签名
            from jituan.services.wechat_pay import club_id_from_czjilu, verify_wechat_v2_signature
            notify_club = club_id_from_czjilu(out_trade_no)
            if not verify_wechat_v2_signature(request_body, notify_club):
                logger.error(f"签名验证失败，订单号: {out_trade_no}")
                return self.wechat_response('FAIL', '签名验证失败')
            logger.info(f"签名验证成功，订单号: {out_trade_no}")

            # 查找订单
            try:
                order = Czjilu.query.get(dingdan_id=out_trade_no, leixing=5)
                logger.info(f"找到订单: {out_trade_no}, 当前状态: {order.zhuangtai}")
            except Czjilu.DoesNotExist:
                logger.error(f"考核订单不存在: {out_trade_no}")
                return self.wechat_response('FAIL', '订单不存在')

            if order.zhuangtai != 9:
                logger.warning(f"订单状态非未支付: {order.zhuangtai}, 订单号: {out_trade_no}")
                if order.zhuangtai == 3:
                    logger.info(f"订单已支付，补试收支记账: {out_trade_no}")
                    try:
                        from jituan.services.szjilu_accounting import repair_wechat_income_if_missing
                        repair_wechat_income_if_missing(
                            Decimal(str(order.jine)),
                            getattr(order, 'club_id', None),
                            biz_ref=f'cz:{out_trade_no}',
                        )
                    except Exception as exc:
                        logger.error('考核回调补记收支失败 %s: %s', out_trade_no, exc, exc_info=True)
                    return self.wechat_response('SUCCESS', 'OK')
                return self.wechat_response('FAIL', '订单状态异常')

            # 验证金额
            wechat_yuan = total_fee / 100
            if abs(wechat_yuan - float(order.jine)) > 0.01:
                logger.error(f"金额不一致: 订单金额={order.jine}, 回调金额={wechat_yuan}, 订单号: {out_trade_no}")
                return self.wechat_response('FAIL', '金额不一致')
            logger.info(f"金额验证通过: {wechat_yuan}元")

            # 调用统一履约函数（事务 + 行锁 + 幂等 + 审核记录 + 收支记录）
            from jituan.services.czjilu_fulfill import fulfill_kaohe, CzjiluFulfillError
            try:
                fulfill_kaohe(out_trade_no, paid_amount_yuan=wechat_yuan)
            except CzjiluFulfillError as exc:
                logger.error('考核回调履约失败 %s: %s', out_trade_no, exc)
                return self.wechat_response('FAIL', str(exc))

            logger.info(f"考核支付回调处理完成，订单号: {out_trade_no}")
            return self.wechat_response('SUCCESS', 'OK')

        except ET.ParseError as e:
            logger.error(f"XML解析失败: {str(e)}, 原始数据: {request.body.decode('utf-8')}")
            return self.wechat_response('FAIL', '数据格式错误')
        except Exception as e:
            logger.error(f"考核支付回调异常: {str(e)}", exc_info=True)
            return self.wechat_response('FAIL', '处理异常')

    def verify_signature(self, xml_data):
        """验证微信支付签名"""
        try:
            root = ET.fromstring(xml_data)
            sign_node = root.find('sign')
            if sign_node is None or sign_node.text is None:
                logger.error("微信回调XML缺少sign节点")
                return False
            sign = sign_node.text

            params = {}
            for child in root:
                if child.tag != 'sign' and child.text is not None:
                    params[child.tag] = child.text

            sorted_params = sorted(params.items(), key=lambda x: x[0])
            sign_string = '&'.join([f"{key}={value}" for key, value in sorted_params if value])
            sign_string += f'&key={settings.WEIXIN_SHANGHUMIYAO}'
            calculated_sign = hashlib.md5(sign_string.encode('utf-8')).hexdigest().upper()

            result = sign == calculated_sign
            if not result:
                logger.error(f"签名验证失败: 原签名={sign}, 计算签名={calculated_sign}")
            return result
        except Exception as e:
            logger.error(f"签名验证异常: {str(e)}", exc_info=True)
            return False

    def wechat_response(self, return_code, return_msg):
        """构造微信回调响应XML"""
        xml = f"""<xml>
<return_code><![CDATA[{return_code}]]></return_code>
<return_msg><![CDATA[{return_msg}]]></return_msg>
</xml>"""
        logger.info(f"返回微信响应: {xml}")
        return HttpResponse(xml, content_type='application/xml')


class KaohePayPollView(APIView):
    """考核支付轮询"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        dingdanid = request.data.get('dingdanid')
        if not dingdanid:
            return Response({'code': 400, 'message': '订单ID不能为空'})
        try:
            order = Czjilu.query.get(dingdan_id=dingdanid, yonghuid=request.user.UserUID, leixing=5)
            # 订单未支付/已关单待补履约时，尝试补偿履约
            from products.czjilu_types import is_czjilu_fulfillable_status
            if is_czjilu_fulfillable_status(order.zhuangtai):
                from jituan.services.czjilu_fulfill import confirm_czjilu_paid, CzjiluFulfillError
                try:
                    confirm_czjilu_paid(dingdanid, request.user.UserUID, expected_leixing=5)
                    order = Czjilu.query.get(dingdan_id=dingdanid, yonghuid=request.user.UserUID, leixing=5)
                except CzjiluFulfillError as exc:
                    logger.info(f"考核确认未就绪 {dingdanid}: {exc}")
                    return Response({'code': 400, 'message': '订单尚未支付'})

            if order.zhuangtai == 3:
                return Response({'code': 200, 'message': '支付成功'})
            return Response({'code': 400, 'message': '订单尚未支付'})
        except Czjilu.DoesNotExist:
            return Response({'code': 400, 'message': '订单不存在'})


class KaohePayFailView(APIView):
    """考核支付失败处理"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        dingdanid = request.data.get('dingdanid')
        if not dingdanid:
            return Response({'code': 400, 'message': '订单ID不能为空'})
        try:
            order = Czjilu.query.get(dingdan_id=dingdanid, yonghuid=request.user.UserUID, leixing=5)
            if order.zhuangtai == 9:
                from products.czjilu_types import close_unpaid_czjilu
                close_unpaid_czjilu(order, reason='用户取消支付')
                return Response({'code': 200, 'message': '订单已取消'})
            return Response({'code': 400, 'message': '订单状态无法取消'})
        except Czjilu.DoesNotExist:
            return Response({'code': 400, 'message': '订单不存在'})            
