"""orders.views.merchant_penalty - auto-generated by split script."""
# ==================== 标准库 ====================
import hmac
import os
import json
import time
import random
import string
import traceback
import threading
import requests
import hashlib
import xml.etree.ElementTree as ET
import xmltodict
import logging
from decimal import Decimal

from django.conf import settings
from django.db import models, transaction
from gvsdsdk.fluent import db, func, FQ
from django.db.models import F, Q, OuterRef, Subquery
from django.core.cache import cache
from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpResponse
from django.utils import timezone

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 JSONParser
from rest_framework.throttling import AnonRateThrottle
from rest_framework_simplejwt.authentication import JWTAuthentication

from tencentcloud.common import credential
from tencentcloud.common.profile.client_profile import ClientProfile
from tencentcloud.common.profile.http_profile import HttpProfile
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
from tencentcloud.sts.v20180813 import sts_client, models

from utils.weixin_broadcast import WeixinBroadcastSender
from utils.money import yuan_to_fen
from utils.chat_utils import (
    _send_group_message, _subscribe_users_to_group, establish_order_chat,
    prepare_order_group_chat, resolve_pair_group_id,
    parse_pair_from_group_id, resolve_pair_from_order, check_pair_chat_permission,
    query_pair_orders, order_to_chat_dict,
)
from utils.fadan_utils import check_fadan_qiangdan_eligible

from orders.utils import (
    calc_shangjia_order_fencheng,
    update_daily_payout,
    settle_shangjia_order_guanshi_fenhong
)
from shop.utils import calculate_pingtai_and_dianpu_shouyi, validate_shangpin_and_dianpu, update_dianpu_daily_stat
from products.utils import update_shangpin_daily_stat
from backend.utils import update_shangjia_daily, update_dashou_daily_by_action, pick_leixing_id, datetime_aliases, fmt_datetime
from merchant_ops.constants import (
    ACTION_PENALTY_APPLY,
    ACTION_PENALTY_CANCEL,
    ACTION_PENALTY_MODIFY,
    ACTION_PENALTY_RESUBMIT,
    ACTION_REFUND_CANCEL,
    ACTION_REFUND_MODIFY,
)
from merchant_ops.services.action_log import (
    record_order_action_from_request, leixing_id_from_order,
)
from rank.services import record_dashou_biaoxian
from rank.utils import check_dashou_biaoqian_required

from orders.notice_tasks import dingdan_guangbo

from ..models import (
    Order, MerchantOrderExt, PlatformOrderExt, PlayerDeliveryImage,
    PenaltyRecord, PenaltyEvidenceImage, Penalty, PenaltyAppealImage, PenaltyBonus, PenaltyBonusRate,
    CommissionRate, PlayerRating, RefundRecord,
)
from jituan.services.club_config import get_commission_rate, get_commission_rate_object
from jituan.services.club_context import resolve_club_id_from_request
from jituan.services.club_user import get_payment_openid
from jituan.services.club_penalty import resolve_penalty_club_id, resolve_penalty_record_club_id
from users.fadan_fenhong_utils import lock_penalty_bonus
from users.fadan_fenhong_utils import lock_penalty_bonus
from products.models import Shangpin, ShangpinLeixing, Huiyuangoumai
from users.models import UserDashou, UserShangjia, UserBoss
from users.business_models import User
from rank.models import DashouBiaoxian, Chenghao, DingdanBiaoqian, YonghuChenghao
from config.models import (
    ShangjiaLianjie
)



logger = logging.getLogger(__name__)

# ===================== 视图：商家罚款申请 =====================
# 文件路径：dingdan/views.py 或单独文件，根据项目结构放置


class ShangjiaFakuanApplyView(APIView):
    """
    商家罚款申请接口
    路径：POST /dingdan/sjfksq
    权限：JWT认证的商家用户
    参数：
        dingdan_id        - 订单ID (必填)
        chufa_liyou       - 罚款原因 (必填)
        fakuanjine        - 罚款金额，元 (必填)
        yingxiang_qiangdan - 是否影响抢单 1是 0否 (必填)
    说明：
        1. 自动从订单中获取被打手ID，防止商家篡改目标。
        2. 同一订单同一打手不允许重复罚款。
        3. 跨平台订单若为我方派单，则通知对方平台处罚；否则本地生成罚单。
    """

    permission_classes = [IsAuthenticated]
    parser_classes = [JSONParser]

    def post(self, request):
        # ========== 1. 身份验证 ==========
        current_user = request.user
        try:
            shangjia_id = current_user.UserUID  # 商家的用户ID
        except AttributeError:
            return Response({'code': 1, 'msg': '用户认证失败'}, status=status.HTTP_401_UNAUTHORIZED)

        # ========== 2. 参数提取与校验 ==========
        dingdan_id = str(request.data.get('OrderID') or request.data.get('dingdan_id') or '').strip()
        chufa_liyou = request.data.get('chufa_liyou', '').strip()
        fakuanjine = request.data.get('fakuanjine', 0)
        yingxiang_qiangdan = int(request.data.get('yingxiang_qiangdan', 1))
        evidence_urls = request.data.get('evidence_urls') or []

        if not dingdan_id or not chufa_liyou or not fakuanjine:
            return Response({'code': 2, 'msg': '参数不完整'}, status=status.HTTP_400_BAD_REQUEST)

        if not isinstance(evidence_urls, list):
            return Response({'code': 2, 'msg': '证据图片格式错误'}, status=status.HTTP_400_BAD_REQUEST)
        evidence_urls = [str(u).strip() for u in evidence_urls if str(u).strip()]
        if len(evidence_urls) > 9:
            return Response({'code': 2, 'msg': '证据图片最多9张'}, status=status.HTTP_400_BAD_REQUEST)
        if len(evidence_urls) < 1:
            return Response({'code': 2, 'msg': '请至少上传1张证据图片'}, status=status.HTTP_400_BAD_REQUEST)

        try:
            fakuanjine = float(fakuanjine)
            if fakuanjine <= 0:
                raise ValueError
        except (ValueError, TypeError):
            return Response({'code': 2, 'msg': '罚款金额格式错误'}, status=status.HTTP_400_BAD_REQUEST)

        # ========== 3. 获取订单，校验归属商家 ==========
        try:
            # 关联查询商家扩展表，确保订单属于当前商家
            shangjia_kuozhan = MerchantOrderExt.query.select_related('Order').get(
                Order__OrderID=dingdan_id,
                MerchantID=shangjia_id
            )
            order = shangjia_kuozhan.Order
        except MerchantOrderExt.DoesNotExist:
            return Response({'code': 3, 'msg': '订单不存在或您无权操作'}, status=status.HTTP_403_FORBIDDEN)

        # ========== 4. 获取被打手ID ==========
        dashou_id = order.PlayerID
        if not dashou_id:
            return Response({'code': 4, 'msg': '该订单暂无接单打手，无法罚款'}, status=status.HTTP_400_BAD_REQUEST)

        # ========== 5. 重复罚款检查 ==========
        from orders.services.team_recruit import get_active_recruit
        _active_team = get_active_recruit(dingdan_id)
        if _active_team:
            if Penalty.query.filter(RelatedOrderID=dingdan_id).exclude(Status=4).exists():
                return Response({'code': 5, 'msg': '该订单已有罚单，不能重复申请'}, status=status.HTTP_400_BAD_REQUEST)
        elif Penalty.query.filter(PenalizedUserID=dashou_id, RelatedOrderID=dingdan_id).exists():
            return Response({'code': 5, 'msg': '该打手在此订单已被罚款，不能重复申请'}, status=status.HTTP_400_BAD_REQUEST)

        # ========== 6. 本地创建罚单 ==========
        staff_kefu_id = None
        staff_member = getattr(request, '_staff_member', None)
        if staff_member is not None:
            staff_kefu_id = getattr(staff_member, 'staff_user_id', None) or None

        from utils.penalty_status import PENALTY_PLATFORM_AUDIT

        with transaction.atomic():
            # 组队均摊：有效组队则每人一张罚单
            from orders.services.team_recruit import maybe_split_penalties
            club_for_p = resolve_penalty_club_id(
                order=order,
                penalized_user_id=dashou_id,
                request=request,
                applicant_id=shangjia_id,
                user=current_user,
            )
            split = maybe_split_penalties(
                order_id=dingdan_id,
                total_amount=fakuanjine,
                reason=chufa_liyou,
                applicant_id=shangjia_id,
                applicant_identity=5,
                affects_grabbing=yingxiang_qiangdan,
                club_id=club_for_p,
            )
            if split:
                # 均摊单默认待缴纳；若业务需审核，统一改状态
                from orders.models import Penalty as PModel
                PModel.objects.filter(id__in=[p.id for p in split]).update(Status=PENALTY_PLATFORM_AUDIT)
                fadan = split[0]
            else:
                fadan = Penalty.query.create(
                    PenalizedUserID=dashou_id,
                    ApplicantID=shangjia_id,  # 申请人为当前商家
                    Identity=1,                    # 被打者身份：1=打手
                    Reason=chufa_liyou,
                    FineAmount=fakuanjine,
                    RelatedOrderID=dingdan_id,
                    Status=PENALTY_PLATFORM_AUDIT,  # 平台审核中，通过后变为待缴纳
                    AffectsGrabbing=yingxiang_qiangdan,
                    ApplicantIdentity=5,        # 申请人是商家
                    ClubID=club_for_p,
                    ShopStaffUserID=staff_kefu_id,
                )
            PenaltyAppealImage.objects.bulk_create([
                PenaltyAppealImage(
                    Penalty=fadan,
                    PenalizedUserID=dashou_id,
                    ImageURL=url,
                    Purpose=1,
                )
                for url in evidence_urls
            ]) if not split else None
            if split and evidence_urls:
                for p in split:
                    PenaltyAppealImage.objects.bulk_create([
                        PenaltyAppealImage(
                            Penalty=p,
                            PenalizedUserID=p.PenalizedUserID,
                            ImageURL=url,
                            Purpose=1,
                        )
                        for url in evidence_urls
                    ])
            if split:
                for p in split:
                    lock_penalty_bonus(p)
            else:
                lock_penalty_bonus(fadan)
        logger.info(f"商家{shangjia_id}对订单{dingdan_id}打手{dashou_id}提交罚款申请(平台审核)，金额{fakuanjine}")
        record_order_action_from_request(
            request,
            ACTION_PENALTY_APPLY,
            dingdan_id,
            merchant_id=shangjia_id,
            amount=fakuanjine,
            leixing_id=leixing_id_from_order(order),
            remark=chufa_liyou[:200] if chufa_liyou else '',
        )
        return Response({'code': 0, 'msg': '罚款申请已提交，等待平台审核', 'data': {'fadan_id': fadan.id}})


class ShangjiaFakuaiXiugaiView(APIView):
    """
    商家端统一修改接口：罚款修改/撤销/再次申请，订单退款撤销/修改理由
    URL: /dingdan/sjfkxgycx
    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        user = request.user
        # 验证商家身份
        try:
            shangjia = user.ShopProfile
        except UserShangjia.DoesNotExist:
            return Response({'code': 403, 'msg': '您不是商家'}, status=status.HTTP_403_FORBIDDEN)
        if shangjia.zhuangtai != 1:
            return Response({'code': 403, 'msg': '商家状态异常'}, status=status.HTTP_403_FORBIDDEN)

        operation = request.data.get('operation')
        dingdan_id = request.data.get('dingdan_id') or request.data.get('OrderID')
        if not dingdan_id:
            return Response({'code': 400, 'msg': '缺少订单ID'}, status=status.HTTP_400_BAD_REQUEST)

        # 验证订单属于该商家
        try:
            dingdan = Order.query.select_related('shangjia_kuozhan').get(OrderID=dingdan_id)
        except Order.DoesNotExist:
            return Response({'code': 404, 'msg': '订单不存在'}, status=status.HTTP_404_NOT_FOUND)
        if not hasattr(dingdan, 'shangjia_kuozhan') or dingdan.shangjia_kuozhan.MerchantID != user.UserUID:
            return Response({'code': 403, 'msg': '无权操作此订单'}, status=status.HTTP_403_FORBIDDEN)

        # 处理订单相关操作
        if operation == 'cancel_refund':
            resp = self._cancel_refund(dingdan, request.data.get('liyou', ''))
            if getattr(resp, 'data', {}).get('code') == 0:
                record_order_action_from_request(
                    request, ACTION_REFUND_CANCEL, dingdan_id,
                    merchant_id=user.UserUID,
                    amount=getattr(dingdan, 'Amount', None),
                    leixing_id=leixing_id_from_order(dingdan),
                    remark=str(request.data.get('liyou') or '')[:200],
                )
            return resp
        elif operation == 'modify_refund_reason':
            resp = self._modify_refund_reason(dingdan, request.data.get('liyou', ''))
            if getattr(resp, 'data', {}).get('code') == 0:
                record_order_action_from_request(
                    request, ACTION_REFUND_MODIFY, dingdan_id,
                    merchant_id=user.UserUID,
                    amount=getattr(dingdan, 'Amount', None),
                    leixing_id=leixing_id_from_order(dingdan),
                    remark=str(request.data.get('liyou') or '')[:200],
                )
            return resp

        # 处理罚款相关操作
        fadan_id = request.data.get('fadan_id')
        if not fadan_id:
            return Response({'code': 400, 'msg': '缺少罚单ID'}, status=status.HTTP_400_BAD_REQUEST)
        try:
            fadan = Penalty.query.get(id=fadan_id)
        except Penalty.DoesNotExist:
            return Response({'code': 404, 'msg': '罚单不存在'}, status=status.HTTP_404_NOT_FOUND)
        if fadan.RelatedOrderID != dingdan_id:
            return Response({'code': 403, 'msg': '罚单与订单不匹配'}, status=status.HTTP_403_FORBIDDEN)

        if operation == 'modify_penalty':
            resp = self._modify_penalty(fadan, request.data.get('liyou', ''), request.data.get('jine'), user.UserUID)
            if getattr(resp, 'data', {}).get('code') == 0:
                record_order_action_from_request(
                    request, ACTION_PENALTY_MODIFY, dingdan_id,
                    merchant_id=user.UserUID,
                    amount=request.data.get('jine') or getattr(fadan, 'FineAmount', None),
                    leixing_id=leixing_id_from_order(dingdan),
                    remark=str(request.data.get('liyou') or '')[:200],
                )
            return resp
        elif operation == 'cancel_penalty':
            resp = self._cancel_penalty(fadan, request.data.get('liyou', ''), user.UserUID)
            if getattr(resp, 'data', {}).get('code') == 0:
                record_order_action_from_request(
                    request, ACTION_PENALTY_CANCEL, dingdan_id,
                    merchant_id=user.UserUID,
                    amount=getattr(fadan, 'FineAmount', None),
                    leixing_id=leixing_id_from_order(dingdan),
                    remark=str(request.data.get('liyou') or '')[:200],
                )
            return resp
        elif operation == 'resubmit_penalty':
            resp = self._resubmit_penalty(fadan, request.data.get('liyou', ''), request.data.get('jine'), user.UserUID)
            if getattr(resp, 'data', {}).get('code') == 0:
                record_order_action_from_request(
                    request, ACTION_PENALTY_RESUBMIT, dingdan_id,
                    merchant_id=user.UserUID,
                    amount=request.data.get('jine') or getattr(fadan, 'FineAmount', None),
                    leixing_id=leixing_id_from_order(dingdan),
                    remark=str(request.data.get('liyou') or '')[:200],
                )
            return resp
        elif operation == 'manage_penalty_evidence':
            return self._manage_penalty_evidence(fadan, request.data)
        else:
            return Response({'code': 400, 'msg': '无效的操作类型'}, status=status.HTTP_400_BAD_REQUEST)

    def _cancel_refund(self, dingdan, liyou):
        """撤销退款申请（状态4 → 状态8，清空退款理由）"""
        if dingdan.Status != 4:
            return Response({'code': 400, 'msg': '只有退款中的订单可以撤销退款申请'})
        with transaction.atomic():
            dingdan.Status = 8
            dingdan.RefundReason = ''   # 清空退款理由
            dingdan.save()
            # 记录撤销理由可存入日志（如需要可另建日志表，此处简单处理）
            logger.info(f"商家撤销退款订单 {dingdan.OrderID}，理由: {liyou}")
        return Response({'code': 0, 'msg': '已撤销退款申请'})

    def _modify_refund_reason(self, dingdan, liyou):
        """修改退款理由（仅状态4时）"""
        if dingdan.Status != 4:
            return Response({'code': 400, 'msg': '只有退款中的订单可以修改退款理由'})
        if not liyou.strip():
            return Response({'code': 400, 'msg': '退款理由不能为空'})
        dingdan.RefundReason = liyou[:500]
        dingdan.save()
        return Response({'code': 0, 'msg': '退款理由修改成功'})

    def _team_split_siblings(self, fadan, *, include_rejected=False):
        """同订单组队均摊罚单；非均摊则只返回自身。"""
        from orders.services.team_recruit import list_team_split_penalties
        reason = fadan.Reason or ''
        if reason.startswith('【组队均摊】') and fadan.RelatedOrderID:
            sibs = list_team_split_penalties(
                fadan.RelatedOrderID, include_rejected=include_rejected,
            )
            if sibs:
                return sibs
        return [fadan]

    def _modify_penalty(self, fadan, liyou, jine_str, processor_id=None):
        """修改罚款（待缴纳 / 申诉中 / 平台审核中）；组队均摊联动改总额并重分。"""
        from utils.penalty_status import PENALTY_PENDING_PAY, PENALTY_APPEALING, PENALTY_PLATFORM_AUDIT
        from orders.services.team_recruit import _d, MONEY
        from decimal import ROUND_DOWN
        if fadan.Status not in (PENALTY_PENDING_PAY, PENALTY_APPEALING, PENALTY_PLATFORM_AUDIT):
            return Response({'code': 400, 'msg': '当前罚单状态不可修改'})
        if not liyou.strip():
            return Response({'code': 400, 'msg': '罚款理由不能为空'})
        try:
            jine = Decimal(jine_str)
            if jine <= 0:
                raise ValueError
        except Exception:
            return Response({'code': 400, 'msg': '罚款金额必须为正数'})
        siblings = self._team_split_siblings(fadan, include_rejected=False)
        for s in siblings:
            if s.Status not in (PENALTY_PENDING_PAY, PENALTY_APPEALING, PENALTY_PLATFORM_AUDIT):
                return Response({'code': 400, 'msg': '组队均摊罚单状态不一致，无法修改'})
        reason_text = liyou[:500]
        if (fadan.Reason or '').startswith('【组队均摊】') and not reason_text.startswith('【组队均摊】'):
            reason_text = f'【组队均摊】{reason_text}'
        with transaction.atomic():
            k = len(siblings)
            if k > 1:
                total = _d(jine)
                base = (total / Decimal(k)).quantize(MONEY, rounding=ROUND_DOWN)
                amounts = [base] * k
                amounts[0] = _d(total - base * (k - 1))
                # 队长优先（与创建时一致）
                siblings = sorted(
                    siblings,
                    key=lambda p: 0 if p.id == fadan.id else 1,
                )
                for i, s in enumerate(siblings):
                    s.Reason = reason_text
                    s.FineAmount = amounts[i]
                    if processor_id:
                        s.ProcessorID = processor_id
                        s.ProcessorIdentity = 5
                    s.save()
                    lock_penalty_bonus(s)
            else:
                fadan.Reason = reason_text
                fadan.FineAmount = jine
                if processor_id:
                    fadan.ProcessorID = processor_id
                    fadan.ProcessorIdentity = 5
                fadan.save()
                lock_penalty_bonus(fadan)
        return Response({'code': 0, 'msg': '罚款修改成功'})

    def _cancel_penalty(self, fadan, liyou, processor_id=None):
        """撤销罚款；组队均摊一并驳回，避免只撤一张仍挡抢单。"""
        from utils.penalty_status import PENALTY_PENDING_PAY, PENALTY_APPEALING, PENALTY_PLATFORM_AUDIT, PENALTY_REJECTED
        if fadan.Status not in (PENALTY_PENDING_PAY, PENALTY_APPEALING, PENALTY_PLATFORM_AUDIT):
            return Response({'code': 400, 'msg': '当前罚单状态不可撤销'})
        if not liyou.strip():
            return Response({'code': 400, 'msg': '驳回理由不能为空'})
        siblings = self._team_split_siblings(fadan, include_rejected=False)
        with transaction.atomic():
            for s in siblings:
                if s.Status not in (PENALTY_PENDING_PAY, PENALTY_APPEALING, PENALTY_PLATFORM_AUDIT):
                    continue
                s.Status = PENALTY_REJECTED
                s.RejectReason = liyou[:500]
                if processor_id:
                    s.ProcessorID = processor_id
                    s.ProcessorIdentity = 5
                s.save()
        return Response({'code': 0, 'msg': '罚款已撤销（驳回）'})

    def _resubmit_penalty(self, fadan, liyou, jine_str, processor_id=None):
        """再次申请罚款；组队均摊联动总额重分。"""
        from utils.penalty_status import PENALTY_REJECTED, PENALTY_PLATFORM_AUDIT
        from orders.services.team_recruit import _d, MONEY
        from decimal import ROUND_DOWN
        if fadan.Status != PENALTY_REJECTED:
            return Response({'code': 400, 'msg': '只有已驳回的罚单可再次申请'})
        if not liyou.strip():
            return Response({'code': 400, 'msg': '罚款理由不能为空'})
        try:
            jine = Decimal(jine_str)
            if jine <= 0:
                raise ValueError
        except Exception:
            return Response({'code': 400, 'msg': '罚款金额必须为正数'})
        siblings = self._team_split_siblings(fadan, include_rejected=True)
        siblings = [s for s in siblings if s.Status == PENALTY_REJECTED] or [fadan]
        reason_text = liyou[:500]
        if (fadan.Reason or '').startswith('【组队均摊】') and not reason_text.startswith('【组队均摊】'):
            reason_text = f'【组队均摊】{reason_text}'
        with transaction.atomic():
            k = len(siblings)
            total = _d(jine)
            if k > 1:
                base = (total / Decimal(k)).quantize(MONEY, rounding=ROUND_DOWN)
                amounts = [base] * k
                amounts[0] = _d(total - base * (k - 1))
            else:
                amounts = [total]
            for i, s in enumerate(siblings):
                s.Status = PENALTY_PLATFORM_AUDIT
                s.Reason = reason_text
                s.FineAmount = amounts[i] if i < len(amounts) else amounts[-1]
                s.RejectReason = ''
                if processor_id:
                    s.ProcessorID = processor_id
                    s.ProcessorIdentity = 5
                s.save()
                lock_penalty_bonus(s)
            logger.info(f"商家重新申请组队罚单 order={fadan.RelatedOrderID} 总额={jine} n={k}")
        return Response({'code': 0, 'msg': '已重新申请罚款'})

    def _manage_penalty_evidence(self, fadan, data):
        """管理罚单证据图片（Purpose=1），与申诉图片严格隔离"""
        from utils.penalty_status import PENALTY_PENDING_PAY, PENALTY_APPEALING, PENALTY_PLATFORM_AUDIT
        if fadan.Status not in (PENALTY_PENDING_PAY, PENALTY_APPEALING, PENALTY_PLATFORM_AUDIT):
            return Response({'code': 400, 'msg': '当前罚单状态不可修改证据图片'})
        add_urls = data.get('add_urls') or []
        remove_ids = data.get('remove_ids') or []
        if not add_urls and not remove_ids:
            return Response({'code': 400, 'msg': '未提供图片变更'})
        with transaction.atomic():
            if remove_ids:
                PenaltyAppealImage.query.filter(
                    Penalty=fadan, Purpose=1, id__in=remove_ids
                ).delete()
            existing_count = PenaltyAppealImage.query.filter(Penalty=fadan, Purpose=1).count()
            if existing_count + len(add_urls) > 9:
                return Response({'code': 400, 'msg': '证据图片最多9张'})
            _valid_add_urls = [str(u).strip() for u in add_urls if str(u).strip()]
            if _valid_add_urls:
                PenaltyAppealImage.objects.bulk_create([
                    PenaltyAppealImage(
                        Penalty=fadan,
                        PenalizedUserID=fadan.PenalizedUserID,
                        ImageURL=url,
                        Purpose=1,
                    )
                    for url in _valid_add_urls
                ])
            logger.info(
                f"罚单{fadan.id}证据图变更：新增{len(add_urls)}张，删除{len(remove_ids)}张"
            )
        return Response({'code': 0, 'msg': '证据图片已更新'})
