"""充值订单 czjilu 履约（押金/积分），与 member_recharge 同一套幂等原则。"""
from __future__ import annotations

import logging
from decimal import Decimal

from django.db import transaction
from django.db.models import F

from backend.utils import update_guanshi_daily_by_action
from jituan.constants import CLUB_ID_DEFAULT
from jituan.services.club_penalty import resolve_gsfenhong_club_id
from orders.models import CommissionRate
from products.models import Czjilu, Gsfenhong
from products.czjilu_types import is_czjilu_fulfillable_status
from users.models import UserDashou, UserGuanshi

logger = logging.getLogger(__name__)

INTEGRAL_PRICE = Decimal('5.00')
MAX_INTEGRAL = 10
INTEGRAL_INCREMENT = 5


class CzjiluFulfillError(Exception):
    def __init__(self, message, code='error'):
        super().__init__(message)
        self.code = code


@transaction.atomic
def fulfill_zhuanpan(dingdan_id, paid_amount_yuan=None):
    """转盘购票履约：Czjilu 标已付 + 抽奖机会改为待抽。绝不创建抢单池 Order。"""
    from products.czjilu_types import CZJILU_LEIXING_ZHUANPAN, CZJILU_PAID_STATUS
    from products.models import ZhuanpanJihui

    try:
        order = Czjilu.query.select_for_update().get(dingdan_id=dingdan_id)
    except Czjilu.DoesNotExist:
        raise CzjiluFulfillError('订单不存在', 'not_found')

    if order.leixing != CZJILU_LEIXING_ZHUANPAN:
        raise CzjiluFulfillError('订单类型不是转盘购票', 'type_error')

    if order.zhuangtai == CZJILU_PAID_STATUS:
        return {'already_done': True, 'dingdan_id': dingdan_id, 'zhuangtai': CZJILU_PAID_STATUS}

    if not is_czjilu_fulfillable_status(order.zhuangtai):
        return {'already_done': True, 'dingdan_id': dingdan_id, 'zhuangtai': order.zhuangtai}

    amount = Decimal(str(order.jine))
    if paid_amount_yuan is not None:
        paid = Decimal(str(paid_amount_yuan))
        if abs(paid - amount) > Decimal('0.01'):
            raise CzjiluFulfillError(f'支付金额{paid}与订单{amount}不一致', 'amount_mismatch')

    jihui = ZhuanpanJihui.query.select_for_update().filter(
        czjilu_dingdan_id=dingdan_id
    ).first()
    if not jihui:
        raise CzjiluFulfillError('转盘机会单不存在', 'not_found')

    order.zhuangtai = CZJILU_PAID_STATUS
    order.save(update_fields=['zhuangtai'])

    if int(jihui.zhuangtai) == 9:
        jihui.zhuangtai = 1  # 已付待抽
        jihui.save(update_fields=['zhuangtai', 'UpdateTime'])

    try:
        from jituan.services.szjilu_accounting import repair_wechat_income_if_missing
        repair_wechat_income_if_missing(
            amount,
            getattr(order, 'club_id', None) or CLUB_ID_DEFAULT,
            biz_ref=f'cz:{dingdan_id}',
        )
    except Exception as exc:
        logger.error('转盘购票补记收支失败 dingdan=%s: %s', dingdan_id, exc, exc_info=True)

    logger.info('转盘购票履约完成 dingdan=%s jihui=%s', dingdan_id, jihui.id)
    return {
        'already_done': False,
        'dingdan_id': dingdan_id,
        'zhuangtai': CZJILU_PAID_STATUS,
        'jihui_id': jihui.id,
    }


@transaction.atomic
def fulfill_yajin_recharge(dingdan_id, paid_amount_yuan=None):
    """押金充值幂等履约：仅待履约状态(9/用户关单2)时入账；已付(3)直接跳过防多加。"""
    order = Czjilu.query.filter(dingdan_id=dingdan_id).select_for_update().first()
    if not order:
        raise CzjiluFulfillError('订单不存在', 'not_found')
    if order.leixing != 2:
        raise CzjiluFulfillError('订单类型不是押金', 'type_error')
    if not is_czjilu_fulfillable_status(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) or CLUB_ID_DEFAULT,
                biz_ref=f'cz:{dingdan_id}',
            )
        except Exception as exc:
            logger.error('押金补记收支失败 dingdan=%s: %s', dingdan_id, exc, exc_info=True)
        return {'already_done': True, 'dingdan_id': dingdan_id, 'zhuangtai': order.zhuangtai}

    amount = Decimal(str(order.jine))
    if paid_amount_yuan is not None:
        paid = Decimal(str(paid_amount_yuan))
        if abs(paid - amount) > Decimal('0.01'):
            raise CzjiluFulfillError(f'支付金额{paid}与订单{amount}不一致', 'amount_mismatch')

    try:
        dashou = UserDashou.objects.select_for_update().get(user__UserUID=order.yonghuid)
    except UserDashou.DoesNotExist:
        raise CzjiluFulfillError('打手不存在，无法充值押金', 'dashou_not_found')

    dashou.yajin += amount
    dashou.save(update_fields=['yajin'])

    order.zhuangtai = 3
    order.save(update_fields=['zhuangtai'])

    club_id = getattr(order, 'club_id', None) or CLUB_ID_DEFAULT
    try:
        from jituan.services.szjilu_accounting import repair_wechat_income_if_missing
        repair_wechat_income_if_missing(amount, club_id, biz_ref=f'cz:{dingdan_id}')
    except Exception as exc:
        logger.error('押金收支统计失败 dingdan=%s: %s', dingdan_id, exc, exc_info=True)

    try:
        _apply_yajin_fenhong(order, dashou, amount)
    except Exception as exc:
        logger.error('押金分红失败但押金已到账 dingdan=%s: %s', dingdan_id, exc, exc_info=True)

    logger.info('押金履约完成 dingdan=%s amount=%s', dingdan_id, amount)
    return {'already_done': False, 'dingdan_id': dingdan_id, 'zhuangtai': 3, 'yajin': dashou.yajin}


def _apply_yajin_fenhong(order, dashou, amount):
    if Gsfenhong.query.filter(dingdan_id=order.dingdan_id).exists():
        return
    guanshi_id = dashou.yaoqingren
    if not guanshi_id:
        return
    lilu_obj = CommissionRate.query.filter(Platform='12').first()
    rate = lilu_obj.Rate if lilu_obj and lilu_obj.Rate is not None else Decimal('0')
    if rate <= 0:
        return
    fenhong_jine = (amount * rate).quantize(Decimal('0.01'))
    if fenhong_jine <= 0:
        return
    guanshi = UserGuanshi.objects.select_for_update().get(user__UserUID=guanshi_id)
    from jituan.services.fund_freeze import credit_guanshi_fenhong
    dingdan_id = str(order.dingdan_id or '')
    result = credit_guanshi_fenhong(
        user_id=str(guanshi_id),
        amount=fenhong_jine,
        biz_id=f'{dingdan_id}:guanshi:yajin',
        profile=guanshi,
        czjilu_id=dingdan_id,
        freeze_reason=f'押金分红 {dingdan_id}',
    )
    if not result.get('idempotent'):
        UserGuanshi.objects.filter(pk=guanshi.pk).update(
            chongzhifenrun=F('chongzhifenrun') + fenhong_jine,
        )
    user_main = dashou.user
    Gsfenhong.query.create(
        dingdan_id=order.dingdan_id,
        guanshi=guanshi_id,
        dashouid=order.yonghuid,
        shuoming='押金分红',
        fenhong=fenhong_jine,
        avatar=user_main.Avatar,
        nicheng=dashou.nicheng or '未知打手',
        fenhong_leixing=2,
        club_id=resolve_gsfenhong_club_id(
            dingdan_id=order.dingdan_id, dashouid=order.yonghuid, czjilu=order,
        ),
    )
    try:
        update_guanshi_daily_by_action(yonghuid=guanshi_id, action=4, amount=fenhong_jine)
    except Exception as exc:
        logger.error('押金分红日统计失败: %s', exc)


@transaction.atomic
def fulfill_jifen_recharge(dingdan_id, paid_amount_yuan=None):
    """积分补充幂等履约：仅待履约状态(9/2)时入账。"""
    order = Czjilu.query.filter(dingdan_id=dingdan_id).select_for_update().first()
    if not order:
        raise CzjiluFulfillError('订单不存在', 'not_found')
    if order.leixing != 3:
        raise CzjiluFulfillError('订单类型不是积分', 'type_error')
    if not is_czjilu_fulfillable_status(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) or CLUB_ID_DEFAULT,
                biz_ref=f'cz:{dingdan_id}',
            )
        except Exception as exc:
            logger.error('积分补记收支失败 dingdan=%s: %s', dingdan_id, exc, exc_info=True)
        return {'already_done': True, 'dingdan_id': dingdan_id, 'zhuangtai': order.zhuangtai}

    amount = Decimal(str(order.jine))
    if paid_amount_yuan is not None:
        paid = Decimal(str(paid_amount_yuan))
        if abs(paid - amount) > Decimal('0.01'):
            raise CzjiluFulfillError(f'支付金额{paid}与订单{amount}不一致', 'amount_mismatch')
        if abs(paid - INTEGRAL_PRICE) > Decimal('0.01'):
            raise CzjiluFulfillError('积分订单金额必须为5元', 'amount_mismatch')

    try:
        dashou = UserDashou.objects.select_for_update().get(user__UserUID=order.yonghuid)
    except UserDashou.DoesNotExist:
        raise CzjiluFulfillError('打手不存在，无法补充积分', 'dashou_not_found')

    if dashou.jifen < MAX_INTEGRAL:
        dashou.jifen = min(dashou.jifen + INTEGRAL_INCREMENT, MAX_INTEGRAL)
        dashou.save(update_fields=['jifen'])

    order.zhuangtai = 3
    order.save(update_fields=['zhuangtai'])

    club_id = getattr(order, 'club_id', None) or CLUB_ID_DEFAULT
    try:
        from jituan.services.szjilu_accounting import repair_wechat_income_if_missing
        repair_wechat_income_if_missing(amount, club_id, biz_ref=f'cz:{dingdan_id}')
    except Exception as exc:
        logger.error('积分收支统计失败 dingdan=%s: %s', dingdan_id, exc, exc_info=True)

    logger.info('积分履约完成 dingdan=%s jifen=%s', dingdan_id, dashou.jifen)
    return {'already_done': False, 'dingdan_id': dingdan_id, 'zhuangtai': 3, 'jifen': dashou.jifen}


@transaction.atomic
def fulfill_fakuan(dingdan_id, paid_amount_yuan=None):
    """罚款支付幂等履约：仅 zhuangtai==9 时入账。"""
    import re
    from orders.models import Penalty
    from users.fadan_fenhong_utils import process_fadan_fenhong

    order = Czjilu.query.filter(dingdan_id=dingdan_id).select_for_update().first()
    if not order:
        raise CzjiluFulfillError('订单不存在', 'not_found')
    if order.leixing != 6:
        raise CzjiluFulfillError('订单类型不是罚款', 'type_error')
    if not is_czjilu_fulfillable_status(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) or CLUB_ID_DEFAULT,
                biz_ref=f'cz:{dingdan_id}',
            )
        except Exception as exc:
            logger.error('罚款补记收支失败 dingdan=%s: %s', dingdan_id, exc, exc_info=True)
        return {'already_done': True, 'dingdan_id': dingdan_id, 'zhuangtai': order.zhuangtai}

    amount = Decimal(str(order.jine))
    if paid_amount_yuan is not None:
        paid = Decimal(str(paid_amount_yuan))
        if abs(paid - amount) > Decimal('0.01'):
            raise CzjiluFulfillError(f'支付金额{paid}与订单{amount}不一致', 'amount_mismatch')

    order.zhuangtai = 3
    order.save(update_fields=['zhuangtai'])

    # 更新罚单状态为已缴纳（2）并触发分红
    try:
        fadan = None
        m = re.search(r'#fadan:(\d+)', order.shuoming or '')
        if m:
            fadan_id_str = m.group(1)
            fadan = Penalty.query.filter(
                id=int(fadan_id_str),
                PenalizedUserID=order.yonghuid,
                Status__in=[1, 3],
            ).first()
            if not fadan:
                logger.warning(
                    '罚款订单 %s shuoming 标记罚单 %s 未找到(被处罚者=%s, 状态需为1/3)',
                    dingdan_id, fadan_id_str, order.yonghuid,
                )
        else:
            logger.warning(
                '罚款订单 %s shuoming 无 #fadan:xxx 标记，回退金额匹配: %s',
                dingdan_id, order.shuoming,
            )
        if not fadan:
            fadan = Penalty.query.filter(
                PenalizedUserID=order.yonghuid,
                FineAmount=order.jine,
                Status__in=[1, 3],
            ).order_by('-CreateTime').first()
            if fadan:
                logger.warning(
                    '罚款订单 %s 回退金额匹配到罚单 %s（金额=%s）',
                    dingdan_id, fadan.id, order.jine,
                )
        if fadan:
            fadan.Status = 2
            fadan.save(update_fields=['Status'])
            try:
                from jituan.services.deal_stats import on_penalty_paid
                on_penalty_paid(fadan)
            except Exception as exc:
                logger.warning('成交罚款指标记账跳过 fadan=%s: %s', fadan.id, exc)
            success, msg = process_fadan_fenhong(fadan.id)
            if not success:
                logger.error('罚单 %s 分红处理失败: %s', fadan.id, msg)
            else:
                logger.info('罚单 %s 分红处理完成: %s', fadan.id, msg)
        else:
            logger.error(
                '罚款订单 %s 未找到对应罚单，罚单状态未更新(被处罚者=%s, 金额=%s)',
                dingdan_id, order.yonghuid, order.jine,
            )
    except Exception as exc:
        logger.error('罚款罚单状态更新失败 dingdan=%s: %s', dingdan_id, exc, exc_info=True)

    try:
        from jituan.services.szjilu_accounting import repair_wechat_income_if_missing
        repair_wechat_income_if_missing(amount, getattr(order, 'club_id', None) or CLUB_ID_DEFAULT, biz_ref=f'cz:{dingdan_id}')
    except Exception as exc:
        logger.error('罚款收支统计失败 dingdan=%s: %s', dingdan_id, exc, exc_info=True)

    logger.info('罚款履约完成 dingdan=%s amount=%s', dingdan_id, amount)
    return {'already_done': False, 'dingdan_id': dingdan_id, 'zhuangtai': 3}


@transaction.atomic
def fulfill_kaohe(dingdan_id, paid_amount_yuan=None):
    """考核支付幂等履约：仅待履约状态(9/2)时入账。"""
    from rank.models import KaohePayTemp
    from rank.utils import create_shenhe_jilu_from_temp
    from users.business_models import User

    order = Czjilu.query.filter(dingdan_id=dingdan_id).select_for_update().first()
    if not order:
        raise CzjiluFulfillError('订单不存在', 'not_found')
    if order.leixing != 5:
        raise CzjiluFulfillError('订单类型不是考核', 'type_error')
    if not is_czjilu_fulfillable_status(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) or CLUB_ID_DEFAULT,
                biz_ref=f'cz:{dingdan_id}',
            )
        except Exception as exc:
            logger.error('考核补记收支失败 dingdan=%s: %s', dingdan_id, exc, exc_info=True)
        return {'already_done': True, 'dingdan_id': dingdan_id, 'zhuangtai': order.zhuangtai}

    amount = Decimal(str(order.jine))
    if paid_amount_yuan is not None:
        paid = Decimal(str(paid_amount_yuan))
        if abs(paid - amount) > Decimal('0.01'):
            raise CzjiluFulfillError(f'支付金额{paid}与订单{amount}不一致', 'amount_mismatch')

    order.zhuangtai = 3
    order.save(update_fields=['zhuangtai'])

    # 从临时表取业务参数并创建审核记录
    try:
        temp = KaohePayTemp.query.get(dingdan_id=dingdan_id, yonghuid=order.yonghuid)
        user = User.query.get(UserUID=order.yonghuid)
        success, msg, record = create_shenhe_jilu_from_temp(user, temp)
        if not success:
            logger.error('创建审核记录失败 dingdan=%s: %s', dingdan_id, msg)
            order.zhuangtai = 10  # 支付成功但业务处理失败
            order.save(update_fields=['zhuangtai'])
        else:
            logger.info('考核审核记录创建成功 dingdan=%s jilu_id=%s', dingdan_id, getattr(record, 'jilu_id', None))
    except KaohePayTemp.DoesNotExist:
        logger.error('考核临时表无记录 dingdan=%s', dingdan_id)
        order.zhuangtai = 10
        order.save(update_fields=['zhuangtai'])
    except Exception as exc:
        logger.error('考核审核记录创建异常 dingdan=%s: %s', dingdan_id, exc, exc_info=True)
        order.zhuangtai = 10
        order.save(update_fields=['zhuangtai'])

    try:
        from jituan.services.szjilu_accounting import repair_wechat_income_if_missing
        repair_wechat_income_if_missing(amount, getattr(order, 'club_id', None) or CLUB_ID_DEFAULT, biz_ref=f'cz:{dingdan_id}')
    except Exception as exc:
        logger.error('考核收支统计失败 dingdan=%s: %s', dingdan_id, exc, exc_info=True)

    logger.info('考核履约完成 dingdan=%s amount=%s', dingdan_id, amount)
    return {'already_done': False, 'dingdan_id': dingdan_id, 'zhuangtai': order.zhuangtai}


@transaction.atomic
def fulfill_shangjia(dingdan_id, paid_amount_yuan=None):
    """商家充值幂等履约：仅待履约状态(9/2)时入账；已付(3)跳过防多加。"""
    from users.models import UserShangjia

    order = Czjilu.query.filter(dingdan_id=dingdan_id).select_for_update().first()
    if not order:
        raise CzjiluFulfillError('订单不存在', 'not_found')
    if order.leixing != 4:
        raise CzjiluFulfillError('订单类型不是商家充值', 'type_error')
    if not is_czjilu_fulfillable_status(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) or CLUB_ID_DEFAULT,
                biz_ref=f'cz:{dingdan_id}',
            )
        except Exception as exc:
            logger.error('商家充值补记收支失败 dingdan=%s: %s', dingdan_id, exc, exc_info=True)
        return {'already_done': True, 'dingdan_id': dingdan_id, 'zhuangtai': order.zhuangtai}

    amount = Decimal(str(order.jine))
    if paid_amount_yuan is not None:
        paid = Decimal(str(paid_amount_yuan))
        if abs(paid - amount) > Decimal('0.01'):
            raise CzjiluFulfillError(f'支付金额{paid}与订单{amount}不一致', 'amount_mismatch')

    try:
        shangjia = UserShangjia.objects.select_for_update().get(user__UserUID=order.yonghuid)
    except UserShangjia.DoesNotExist:
        raise CzjiluFulfillError('商家不存在，无法充值', 'shangjia_not_found')

    shangjia.yue += amount
    shangjia.save(update_fields=['yue'])

    order.zhuangtai = 3
    order.save(update_fields=['zhuangtai'])

    try:
        from jituan.services.szjilu_accounting import repair_wechat_income_if_missing
        repair_wechat_income_if_missing(amount, getattr(order, 'club_id', None) or CLUB_ID_DEFAULT, biz_ref=f'cz:{dingdan_id}')
    except Exception as exc:
        logger.error('商家充值收支统计失败 dingdan=%s: %s', dingdan_id, exc, exc_info=True)

    logger.info('商家充值履约完成 dingdan=%s amount=%s yue=%s', dingdan_id, amount, shangjia.yue)
    return {'already_done': False, 'dingdan_id': dingdan_id, 'zhuangtai': 3, 'yue': shangjia.yue}


def confirm_czjilu_paid(dingdan_id, yonghuid, expected_leixing):
    """轮询确认：付呗/微信/支付宝官方查单成功后幂等履约（防多加）。"""
    try:
        order = Czjilu.query.get(dingdan_id=dingdan_id, yonghuid=yonghuid)
    except Czjilu.DoesNotExist:
        raise CzjiluFulfillError('订单不存在或不属于当前用户', 'not_found')

    if order.leixing != expected_leixing:
        raise CzjiluFulfillError('订单类型不匹配', 'type_error')

    if order.zhuangtai == 3:
        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) or CLUB_ID_DEFAULT,
                biz_ref=f'cz:{dingdan_id}',
            )
        except Exception as exc:
            logger.error('充值单补记收支失败 dingdan=%s: %s', dingdan_id, exc, exc_info=True)
        return {'already_done': True, 'zhuangtai': 3}

    if not is_czjilu_fulfillable_status(order.zhuangtai):
        raise CzjiluFulfillError(f'订单状态异常: {order.zhuangtai}', 'status_error')

    paid = Decimal(str(order.jine))
    club_id = getattr(order, 'club_id', None)

    from jituan.services.pay_confirm import confirm_remote_payment
    remote = confirm_remote_payment(dingdan_id, club_id, prefer_amount=paid)
    if not remote.get('ok'):
        if remote.get('pending'):
            raise CzjiluFulfillError('支付结果尚未同步，请稍后再试', 'not_paid')
        raise CzjiluFulfillError(remote.get('msg') or '支付未成功', 'not_paid')
    if remote.get('amount') is not None:
        paid = Decimal(str(remote['amount']))

    # 查单确认支付成功，执行履约（fulfill_* 内行锁+仅 9/2→3，防多加）
    if expected_leixing == 1:
        from jituan.services.member_recharge import fulfill_member_recharge
        return fulfill_member_recharge(dingdan_id, paid_amount_yuan=paid, source='confirm')
    if expected_leixing == 2:
        return fulfill_yajin_recharge(dingdan_id, paid_amount_yuan=paid)
    if expected_leixing == 3:
        return fulfill_jifen_recharge(dingdan_id, paid_amount_yuan=paid)
    if expected_leixing == 4:
        return fulfill_shangjia(dingdan_id, paid_amount_yuan=paid)
    if expected_leixing == 5:
        return fulfill_kaohe(dingdan_id, paid_amount_yuan=paid)
    if expected_leixing == 6:
        return fulfill_fakuan(dingdan_id, paid_amount_yuan=paid)
    if expected_leixing == 7:
        return fulfill_zhuanpan(dingdan_id, paid_amount_yuan=paid)
    raise CzjiluFulfillError('不支持的充值类型', 'type_error')


def repair_czjilu_payment(dingdan_id):
    """客服补单：官方查单确认已付后，走同一套幂等履约（绝不多加）。"""
    try:
        order = Czjilu.query.get(dingdan_id=dingdan_id)
    except Czjilu.DoesNotExist:
        raise CzjiluFulfillError('订单不存在', 'not_found')
    return confirm_czjilu_paid(dingdan_id, order.yonghuid, expected_leixing=order.leixing)
