# yonghu/tixian_shenhe_collect_b.py
"""
自动提现审核 — 用户侧接口：
  ① TixianZddkshApplyView  → POST /yonghu/zddksh   提交审核（扣款）
  ② process_audit_collect  → POST /yonghu/tixiansq  收款（P0：一单到底，先查微信再决定是否新建）
"""
import decimal
import json
import logging
import random
import string
import time

import requests
from django.conf import settings
from django.db import transaction
from django.utils import timezone
from rest_framework import permissions
from rest_framework.response import Response
from rest_framework.views import APIView

from .models import TixianAutoRecord, TixianShenheJilu
from .tixian_shenhe_services import (
    RECONCILE_ALLOW_NEW,
    RECONCILE_COMPLETED,
    RECONCILE_PENDING,
    RECONCILE_WAIT_CONFIRM,
    check_collect_quota_limits,
    create_audit_application,
    get_audit_for_collect,
    handle_post_transfer_failure,
    reconcile_shenhe_wechat_bills,
    release_collect_quota_for_record,
    reserve_collect_quota_limits,
    resolve_collect_context,
    validate_collect_eligibility,
)

logger = logging.getLogger('yonghu.tixian_shenhe')


def generate_tixian_id():
    """生成唯一微信商户打款单号：TX + 时间戳(13位) + 4位随机数"""
    timestamp = str(int(time.time() * 1000))
    rand = ''.join(random.choices(string.digits, k=4))
    return f'TX{timestamp}{rand}'


def _build_collect_success_response(payload):
    return Response({
        'code': 0,
        'msg': '请确认收款',
        'data': payload,
    })


def _is_wx_balance_insufficient(err_code='', err_msg=''):
    blob = f'{err_code} {err_msg}'.upper()
    return any(k in blob for k in ('NOT_ENOUGH', 'INSUFFICIENT', '余额不足', '资金不足'))


def _verify_collect_quota_or_response(user_main, audit, shenhe_danhao):
    """收款限额校验；不通过时直接返回 Response"""
    ok, msg, limit_kind = check_collect_quota_limits(
        user_main,
        audit.leixing,
        audit.shenqing_jine,
        audit.shijidaozhang,
        shenhe_danhao,
    )
    if not ok:
        limit_code = 33 if limit_kind == 'platform' else 400
        logger.warning(
            '收款限额校验未通过 yonghuid=%s shenhe_danhao=%s leixing=%s kind=%s msg=%s',
            user_main.yonghuid, shenhe_danhao, audit.leixing, limit_kind, msg,
        )
        return Response({'code': limit_code, 'msg': msg})
    return None


def _mark_auto_record_failed(tixian_id, fail_reason):
    """微信明确失败时标记打款记录终态，避免后续一直被当成「处理中」"""
    TixianAutoRecord.objects.filter(tixian_id=tixian_id, zhuangtai=0).update(
        zhuangtai=2,
        fail_reason=(fail_reason or '打款失败')[:500],
        update_time=timezone.now(),
    )


def _build_wx_transfer_body(wx_cfg, tixian_id, openid, yonghuid, leixing, shijidaozhang):
    transfer_scene_id = wx_cfg['TRANSFER_SCENE_ID']
    role_map = {1: '打手', 2: '管事', 3: '组长', 4: '考核官', 5: '打手', 6: '商家'}
    desc_map = {1: '佣金提现', 2: '分红提现', 3: '分红提现', 4: '分佣提现', 5: '押金提现', 6: '余额提现'}
    transfer_scene_report_infos = []
    if transfer_scene_id == '1005':
        transfer_scene_report_infos = [
            {'info_type': '岗位类型', 'info_content': role_map.get(leixing, '用户')},
            {'info_type': '报酬说明', 'info_content': desc_map.get(leixing, '提现')},
        ]
    elif transfer_scene_id == '1009':
        transfer_scene_report_infos = [
            {'info_type': '采购商品名称', 'info_content': '平台服务'},
        ]
    return {
        'appid': wx_cfg['APPID'],
        'out_bill_no': tixian_id,
        'transfer_scene_id': transfer_scene_id,
        'openid': openid,
        'transfer_amount': int(shijidaozhang * 100),
        'transfer_remark': f'平台提现-{yonghuid}',
        'transfer_scene_report_infos': transfer_scene_report_infos,
    }


def _call_wechat_transfer(body, wx_cfg):
    from utils.wechat_v3 import build_authorization

    url = 'https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/transfer-bills'
    body_json = json.dumps(body, separators=(',', ':'))
    auth = build_authorization('POST', url, body_json)
    headers = {
        'Authorization': auth,
        'Content-Type': 'application/json',
        'Accept': 'application/json',
        'User-Agent': 'Mozilla/5.0',
    }
    resp = requests.post(url, data=body_json, headers=headers, timeout=15)
    try:
        wx_result = resp.json()
    except ValueError:
        wx_result = {}
    return resp, wx_result


class TixianZddkshApplyView(APIView):
    """POST /yonghu/zddksh  提交审核申请（扣款，不创建 TixianAutoRecord）"""
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request):
        from utils.redis_lock import acquire_lock, release_lock

        user_main = request.user
        lock_key = f'tixian_audit_apply:{user_main.yonghuid}'
        if not acquire_lock(lock_key, timeout=10):
            return Response({'code': 429, 'msg': '操作频繁，请稍后重试'})

        try:
            leixing = request.data.get('leixing')
            jine_str = request.data.get('jine')
            if leixing is None or jine_str is None:
                return Response({'code': 1, 'msg': '缺少参数: leixing 或 jine'})

            try:
                leixing = int(leixing)
                jine = decimal.Decimal(str(jine_str))
            except (ValueError, decimal.InvalidOperation):
                return Response({'code': 2, 'msg': '参数格式错误'})

            if leixing not in [1, 2, 3, 4, 5, 6]:
                return Response({'code': 3, 'msg': '提现类型无效'})
            if jine <= decimal.Decimal('0.1'):
                return Response({'code': 4, 'msg': '提现金额必须大于0.1'})

            try:
                data = create_audit_application(user_main, leixing, jine)
            except ValueError as e:
                return Response({'code': 400, 'msg': str(e)})
            except Exception as e:
                logger.error(f'提现审核申请异常: {e}', exc_info=True)
                return Response({'code': 99, 'msg': '提现申请失败，请稍后重试'})

            return Response({'code': 0, 'msg': '提现申请已提交，请等待审核', 'data': data})

        finally:
            release_lock(lock_key)


def process_audit_collect(request):
    """
    POST /yonghu/tixiansq  用户收款（P0 防多提）

    主路径：tixianjilu_id → Tixianjilu → TixianShenheJilu(状态6)
    若 TixianAutoRecord 已有 tixian_id → 只问微信，非终态绝不新建
    仅微信 FAIL/CANCELLED 终态后才允许新建打款记录
    """
    from utils.redis_lock import acquire_lock, release_lock

    user_main = request.user
    yonghuid = user_main.yonghuid
    tixianjilu_id = request.data.get('tixianjilu_id') or request.data.get('id')
    shenhe_danhao = (request.data.get('shenhe_danhao') or '').strip()
    shenhe_jilu_id = request.data.get('shenhe_jilu_id')

    logger.info(
        '收款请求 yonghuid=%s tixianjilu_id=%s shenhe_jilu_id=%s shenhe_danhao=%s',
        yonghuid, tixianjilu_id, shenhe_jilu_id, shenhe_danhao,
    )

    try:
        if not user_main.openid:
            return Response({'code': 10, 'msg': '用户未绑定微信，无法收款'})

        wx_cfg = settings.WECHAT_PAY_V3_CONFIG
        if not wx_cfg.get('APPID') or not wx_cfg.get('MCHID') or not wx_cfg.get('TRANSFER_SCENE_ID'):
            logger.error('微信打款配置不完整 APPID=%s MCHID=%s', wx_cfg.get('APPID'), wx_cfg.get('MCHID'))
            return Response({'code': 11, 'msg': '系统配置异常，请联系客服'})

        ctx, err = resolve_collect_context(
            yonghuid,
            tixianjilu_id=tixianjilu_id,
            shenhe_danhao=shenhe_danhao,
            shenhe_jilu_id=shenhe_jilu_id,
        )
        if not ctx:
            return Response({'code': 400, 'msg': err})

        audit = ctx['audit']
        shenhe_danhao = ctx['shenhe_danhao']
        tixianjilu_id_val = ctx['jilu'].id if ctx.get('jilu') else audit.tixianjilu_id
        leixing = audit.leixing

        # 二次资格校验：不通过仅返回错误，审核单保持待收款(6)，不退款，用户补齐资质后可再点收款
        collect_ok, collect_msg = validate_collect_eligibility(user_main, leixing)
        if not collect_ok:
            logger.warning(
                '收款校验未通过 yonghuid=%s shenhe_danhao=%s leixing=%s msg=%s',
                yonghuid, shenhe_danhao, leixing, collect_msg,
            )
            return Response({'code': 400, 'msg': collect_msg})

        quota_resp = _verify_collect_quota_or_response(user_main, audit, shenhe_danhao)
        if quota_resp:
            return quota_resp

        lock_key = f'tixian_collect:{shenhe_danhao}'
        if not acquire_lock(lock_key, timeout=15):
            return Response({'code': 429, 'msg': '操作频繁，请稍后重试'})

        tixian_id = None
        quota_reserved = False
        try:
            action, payload = reconcile_shenhe_wechat_bills(shenhe_danhao)
            if action == RECONCILE_COMPLETED:
                return Response({'code': 8, 'msg': payload.get('msg', '该提现已完成，请勿重复操作')})
            if action == RECONCILE_WAIT_CONFIRM:
                quota_resp = _verify_collect_quota_or_response(user_main, audit, shenhe_danhao)
                if quota_resp:
                    return quota_resp
                return _build_collect_success_response(payload)
            if action == RECONCILE_PENDING:
                msg = payload.get('msg') if isinstance(payload, dict) else payload
                return Response({'code': 12, 'msg': msg or '有收款处理中，请稍后再试'})

            if action != RECONCILE_ALLOW_NEW:
                return Response({'code': 12, 'msg': '收款处理中，请稍后再试'})

            with transaction.atomic():
                audit, err = get_audit_for_collect(
                    shenhe_danhao, yonghuid, audit.id,
                )
                if not audit:
                    return Response({'code': 400, 'msg': err})

                # 金额一律用审核表已落库数据（申请时扣款+算费），收款不再重算
                jine = audit.shenqing_jine
                shouxufei = audit.shouxufei
                shijidaozhang = audit.shijidaozhang
                feilv = audit.feilv

                ok, msg, limit_kind = reserve_collect_quota_limits(
                    user_main, leixing, jine, shijidaozhang, shenhe_danhao,
                )
                if not ok:
                    limit_code = 33 if limit_kind == 'platform' else 400
                    return Response({'code': limit_code, 'msg': msg})

                quota_reserved = True

                tixian_id = generate_tixian_id()
                TixianAutoRecord.objects.create(
                    tixian_id=tixian_id,
                    yonghuid=yonghuid,
                    leixing=leixing,
                    jine=jine,
                    shouxufei=shouxufei,
                    shijidaozhang=shijidaozhang,
                    feilv=feilv,
                    zhuangtai=0,
                    shenhe_danhao=shenhe_danhao,
                )
                audit.tixian_auto_id = tixian_id
                audit.save(update_fields=['tixian_auto_id', 'update_time'])

            body = _build_wx_transfer_body(
                wx_cfg, tixian_id, user_main.openid, yonghuid, leixing, shijidaozhang,
            )

            try:
                resp, wx_result = _call_wechat_transfer(body, wx_cfg)

                if resp.status_code == 200 and wx_result.get('state') == 'WAIT_USER_CONFIRM':
                    package_info = wx_result.get('package_info')
                    auto_record = TixianAutoRecord.objects.get(tixian_id=tixian_id)
                    auto_record.wechat_package = json.dumps(package_info, ensure_ascii=False)
                    auto_record.save(update_fields=['wechat_package'])

                    return _build_collect_success_response({
                        'tixian_id': tixian_id,
                        'package_info': package_info,
                        'shouxufei': str(shouxufei),
                        'shijidaozhang': str(shijidaozhang),
                        'current_rate': str(feilv),
                        'mch_id': wx_cfg['MCHID'],
                        'shenhe_danhao': shenhe_danhao,
                        'tixianjilu_id': tixianjilu_id_val,
                    })

                err_code = wx_result.get('code', '')
                err_msg = wx_result.get('message', wx_result.get('detail', '微信接口调用失败'))
                logger.error(
                    '微信发起转账未进入待确认: bill=%s code=%s msg=%s HTTP%s',
                    tixian_id, err_code, err_msg, resp.status_code,
                )

                is_balance_issue = _is_wx_balance_insufficient(err_code, err_msg)
                if is_balance_issue:
                    _mark_auto_record_failed(tixian_id, err_msg)
                    if quota_reserved:
                        release_collect_quota_for_record(
                            TixianAutoRecord.objects.get(tixian_id=tixian_id),
                        )
                        quota_reserved = False
                    return Response({
                        'code': 99,
                        'msg': '运营账户资金不足，需等管理员充值后才能提现',
                    })

                post_action, post_payload = handle_post_transfer_failure(
                    tixian_id, shenhe_danhao, err_code, err_msg,
                )
                if post_action == RECONCILE_WAIT_CONFIRM:
                    quota_resp = _verify_collect_quota_or_response(user_main, audit, shenhe_danhao)
                    if quota_resp:
                        return quota_resp
                    return _build_collect_success_response(post_payload)
                if post_action == RECONCILE_COMPLETED:
                    return Response({'code': 8, 'msg': '该提现已完成，请勿重复操作'})
                if post_action == RECONCILE_PENDING:
                    pending_msg = (
                        post_payload.get('msg', post_payload)
                        if isinstance(post_payload, dict) else post_payload
                    )
                    return Response({'code': 12, 'msg': pending_msg or '有收款处理中，请稍后再试'})

                user_msg = err_msg or '微信收款发起失败，请稍后重试'
                _mark_auto_record_failed(tixian_id, user_msg)
                if quota_reserved:
                    release_collect_quota_for_record(
                        TixianAutoRecord.objects.get(tixian_id=tixian_id),
                    )
                    quota_reserved = False
                return Response({'code': 99, 'msg': user_msg})

            except requests.RequestException as e:
                logger.error(
                    '微信打款网络异常(保持待查单): bill=%s err=%s',
                    tixian_id, e, exc_info=True,
                )
                return Response({
                    'code': 12,
                    'msg': '收款请求已提交，系统正在核对状态，请稍后再试，切勿重复点击',
                })

        finally:
            release_lock(lock_key)

    except Exception as e:
        logger.error(
            '收款接口未捕获异常 yonghuid=%s tixianjilu_id=%s shenhe_danhao=%s err=%s',
            yonghuid, tixianjilu_id, shenhe_danhao, e,
            exc_info=True,
        )
        return Response({'code': 99, 'msg': '系统繁忙，请稍后重试'})
