"""成交指标 + 可控自动结算（俱乐部开关，默认全关）。

对齐设计文档：成交率与自动结算_设计方案.md
"""
from __future__ import annotations

import logging
from datetime import datetime, timedelta
from decimal import Decimal

from django.conf import settings
from django.db import transaction
from django.db.models import F
from django.utils import timezone

logger = logging.getLogger(__name__)


def _db_datetime(dt):
    """写入 DateTimeField 用：与 USE_TZ 对齐，避免 MySQL 拒收 aware datetime。"""
    if dt is None:
        return None
    if settings.USE_TZ:
        if timezone.is_naive(dt):
            return timezone.make_aware(dt, timezone.get_current_timezone())
        return dt
    if timezone.is_aware(dt):
        return timezone.make_naive(dt, timezone.get_current_timezone())
    return dt


def _default_stats_since():
    return _db_datetime(datetime(2020, 1, 1))


def get_or_create_deal_config(club_id: str):
    from jituan.models import ClubOrderDealConfig
    cid = (club_id or '').strip()
    if not cid:
        return None
    row = ClubOrderDealConfig.query.filter(club_id=cid).first()
    if not row:
        # 新建俱乐部默认开启成交指标，便于抢单池立刻展示成交率/罚款率/均时
        row = ClubOrderDealConfig(
            club_id=cid,
            stats_enabled=True,
            stats_since=_default_stats_since(),
        )
        row.save()
    return row


def order_auto_settle_payload(order) -> dict:
    """
    小程序列表/详情展示用。
    仅 Status=8 且 AutoSettleEligible 且有 AutoExpireAt 时返回字段；否则空 dict。
    前端有字段才展示，避免关开关/老单误报。
    """
    from backend.utils import fmt_datetime

    if not order:
        return {}
    try:
        status = int(getattr(order, 'Status', 0) or 0)
    except (TypeError, ValueError):
        status = 0
    if status != 8:
        return {}
    if not bool(getattr(order, 'AutoSettleEligible', False)):
        return {}
    expire_at = getattr(order, 'AutoExpireAt', None)
    if not expire_at:
        return {}
    now = _db_datetime(timezone.now())
    expire_at = _db_datetime(expire_at)
    remain = int((expire_at - now).total_seconds())
    try:
        expire_ts = int(expire_at.timestamp())
    except Exception:
        expire_ts = 0
    return {
        'auto_settle_eligible': True,
        'auto_expire_at': fmt_datetime(expire_at),
        # 前端用时间戳做秒级时钟倒计时，避免解析本地时区偏差
        'auto_expire_at_ts': expire_ts,
        'auto_settle_remain_seconds': max(0, remain),
    }


def config_to_dict(row) -> dict:
    if not row:
        return {
            'club_id': '',
            'auto_settle_enabled': False,
            'auto_settle_enabled_at': '',
            'hours_platform': 48,
            'hours_merchant': 48,
            'stats_enabled': False,
            'stats_since': '',
        }
    return {
        'club_id': row.club_id,
        'auto_settle_enabled': bool(row.auto_settle_enabled),
        'auto_settle_enabled_at': row.auto_settle_enabled_at.isoformat() if row.auto_settle_enabled_at else '',
        'hours_platform': int(row.hours_platform or 48),
        'hours_merchant': int(row.hours_merchant or 48),
        'stats_enabled': bool(row.stats_enabled),
        'stats_since': row.stats_since.isoformat() if row.stats_since else '',
    }


def save_deal_config(club_id: str, data: dict, updated_by: str = '') -> dict:
    """保存配置。关自动结算时清空本俱乐部所有 eligible。"""
    row = get_or_create_deal_config(club_id)
    if not row:
        raise ValueError('俱乐部不能为空')

    was_auto = bool(row.auto_settle_enabled)
    was_stats = bool(row.stats_enabled)
    now = _db_datetime(timezone.now())

    if 'hours_platform' in data and data['hours_platform'] is not None:
        row.hours_platform = max(1, min(int(data['hours_platform']), 720))
    if 'hours_merchant' in data and data['hours_merchant'] is not None:
        row.hours_merchant = max(1, min(int(data['hours_merchant']), 720))

    if 'auto_settle_enabled' in data and data['auto_settle_enabled'] is not None:
        enabled = bool(data['auto_settle_enabled'])
        row.auto_settle_enabled = enabled
        if enabled and not was_auto:
            row.auto_settle_enabled_at = now
        if not enabled:
            row.auto_settle_enabled_at = None
            _clear_club_auto_settle_eligible(row.club_id)

    if 'stats_enabled' in data and data['stats_enabled'] is not None:
        enabled = bool(data['stats_enabled'])
        row.stats_enabled = enabled
        if enabled and not was_stats:
            # 开启时默认从较早起点计，便于旧单刷新也能出成交率
            row.stats_since = _default_stats_since()
        if not enabled:
            # 停止写入；保留 since 供只读理解历史起点；再次开启会刷新 since
            pass
        if enabled and was_stats and data.get('reset_stats_since'):
            row.stats_since = now

    # 若刚打开统计且 since 空
    if row.stats_enabled and not row.stats_since:
        row.stats_since = _default_stats_since()

    # 历史脏数据：aware datetime 在 USE_TZ=False 下无法落库，保存前统一清洗
    row.auto_settle_enabled_at = _db_datetime(row.auto_settle_enabled_at)
    row.stats_since = _db_datetime(row.stats_since)

    row.updated_by = (updated_by or '')[:32]
    row.save()
    # 保存后触发一次历史回填，保证抢单池马上能刷到成交率/罚款率
    try:
        from jituan.models import DealStatEvent
        from jituan.services.deal_stats import ensure_club_stats_bootstrapped
        DealStatEvent.objects.filter(event_key=f'{row.club_id}:stats_bootstrap_v1').delete()
        ensure_club_stats_bootstrapped(row.club_id)
    except Exception as e:
        logger.warning('保存成交配置后回填失败 club=%s: %s', row.club_id, e)
    return config_to_dict(row)


def _clear_club_auto_settle_eligible(club_id: str):
    from orders.models import Order
    Order.objects.filter(ClubID=club_id, AutoSettleEligible=True).update(
        AutoSettleEligible=False,
        AutoExpireAt=None,
        PendingDispatch=False,
        AutoTaskID='',
    )


def apply_enter_status_8(instance):
    """
    订单进入结算中(8)：写 SettlementTime；若俱乐部自动结算已开且本次开启后进入则打 eligible。
    由 signal 调用。instance 为即将 save 的 Order。
    """
    now = _db_datetime(timezone.now())
    instance.SettlementTime = now
    instance.PendingDispatch = False
    instance.AutoTaskID = ''

    club_id = (getattr(instance, 'ClubID', None) or '').strip()
    cfg = get_or_create_deal_config(club_id) if club_id else None
    enabled_at = _db_datetime(getattr(cfg, 'auto_settle_enabled_at', None)) if cfg else None
    if (
        cfg
        and cfg.auto_settle_enabled
        and enabled_at
        and now >= enabled_at
    ):
        hours = int(cfg.hours_merchant if instance.Platform == 2 else cfg.hours_platform) or 48
        instance.AutoSettleEligible = True
        instance.AutoExpireAt = now + timedelta(hours=hours)
    else:
        instance.AutoSettleEligible = False
        instance.AutoExpireAt = None


def apply_leave_status_8(instance):
    instance.SettlementTime = None
    instance.AutoSettleEligible = False
    instance.AutoExpireAt = None
    instance.PendingDispatch = False
    instance.AutoTaskID = ''


@transaction.atomic
def mark_order_completed_unified(order, *, source='auto_settle', operator=''):
    """
    与客服强制结单等价的核心结算（打手入账 + 商家成交 + 管事分红）。
    自动结算路径额外要求 AutoSettleEligible。
    """
    from orders.models import Order
    from users.business_models import User
    from orders.utils import settle_shangjia_order_guanshi_fenhong
    from jituan.services.fund_freeze import credit_dashou_order_settle

    locked = Order.objects.select_for_update().filter(OrderID=order.OrderID).first()
    if not locked:
        return False, '订单不存在'
    if locked.Status != 8:
        return False, f'状态不是结算中({locked.Status})'
    if source == 'auto_settle':
        # 三重闸：资格位 + 到期时间 + 俱乐部开关仍开（关开关后立刻停，不靠等下一轮扫）
        if not locked.AutoSettleEligible:
            return False, '非自动结算资格单'
        now = _db_datetime(timezone.now())
        expire_at = _db_datetime(locked.AutoExpireAt)
        if expire_at and expire_at > now:
            return False, '未到自动结算时间'
        club_id = (getattr(locked, 'ClubID', None) or '').strip()
        cfg = get_or_create_deal_config(club_id) if club_id else None
        if not cfg or not cfg.auto_settle_enabled:
            return False, '俱乐部已关闭自动结算'
        # 只认「本次打开之后」进结算中的单；开启前的旧 8 状态单不会带 eligible
        enabled_at = _db_datetime(cfg.auto_settle_enabled_at)
        settle_at = _db_datetime(getattr(locked, 'SettlementTime', None))
        if enabled_at and settle_at and settle_at < enabled_at:
            return False, '订单进入结算中早于本次开启，不自动结算'

    dashou_id = locked.PlayerID
    dashou_fencheng = locked.PlayerCommission
    if not dashou_id:
        return False, '无接单打手'
    if not dashou_fencheng or dashou_fencheng <= 0:
        return False, '打手分成无效'

    try:
        dashou_user = User.query.get(UserUID=dashou_id)
        dashou_profile = dashou_user.DashouProfile
    except Exception:
        return False, '打手不存在'

    # 组队分账优先；无有效组队则全额给队长（原逻辑）
    # 若本应组队分账却失败/异常：禁止回退全额队长（避免队友白做或重复入账）
    try:
        from orders.services.team_recruit import settle_team_or_legacy, get_active_recruit, team_should_split
        from orders.models import TeamSettleLedger, TeamRecruit
        rec = get_active_recruit(locked.OrderID)
        if rec and rec.status == TeamRecruit.STATUS_RECRUITING:
            # 进8锁失败的兜底：结单前再强制锁一次
            from orders.services.team_recruit import lock_recruit
            lock_recruit(rec.id, actor_uid='system', reason='settle_force_lock')
        team_done = settle_team_or_legacy(locked, set_idle_leader=True)
    except Exception as e:
        logger.error('组队分账异常 %s: %s', locked.OrderID, e)
        from orders.models import TeamSettleLedger
        if TeamSettleLedger.query.filter(order_id=locked.OrderID).exists():
            return False, f'组队分账异常且已有入账记录: {e}'
        try:
            from orders.services.team_recruit import team_should_split, get_active_recruit
            if get_active_recruit(locked.OrderID) or team_should_split(locked):
                return False, f'组队分账失败: {e}'
        except Exception:
            pass
        team_done = False

    if not team_done:
        try:
            from orders.services.team_recruit import get_active_recruit
            if get_active_recruit(locked.OrderID):
                return False, '存在有效组队但分账未完成，已阻止全额打给队长'
        except Exception:
            pass
        credit_dashou_order_settle(
            order=locked,
            dashou_profile=dashou_profile,
            user_id=dashou_id,
            amount=dashou_fencheng,
        )

    if locked.Platform == 2:
        try:
            ext = getattr(locked, 'shangjia_kuozhan', None)
            mid = getattr(ext, 'MerchantID', None) if ext else None
            if mid:
                shangjia_user = User.query.get(UserUID=mid)
                profile = shangjia_user.ShopProfile
                type(profile).objects.filter(pk=profile.pk).update(chengjiao=F('chengjiao') + 1)
                try:
                    from backend.utils import update_shangjia_daily
                    update_shangjia_daily(
                        yonghuid=mid,
                        amount=Decimal(str(locked.Amount or 0)),
                        action=2,
                        order_id=locked.OrderID,
                    )
                except Exception as e:
                    logger.warning('商家日统计跳过 %s: %s', locked.OrderID, e)
        except Exception as e:
            logger.warning('商家成交累加跳过 %s: %s', locked.OrderID, e)
        settle_shangjia_order_guanshi_fenhong(locked, dashou_id)

    locked.Status = 3
    locked.AutoSettleEligible = False
    locked.AutoExpireAt = None
    # pre_save 会清 SettlementTime；必须写入 update_fields 才能落库
    update_fields = ['Status', 'AutoSettleEligible', 'AutoExpireAt', 'SettlementTime', 'UpdateTime']
    if operator:
        locked.AssignedCS = (operator or '')[:20]
        update_fields.append('AssignedCS')
    locked.save(update_fields=update_fields)
    # 成交指标由 post_save signal 幂等记账，此处不重复调用
    return True, 'ok'


def scan_due_auto_settle(limit: int = 100) -> dict:
    """只处理 eligible 到期单；无关自动结算的俱乐部空跑；关开关后立刻无单可扫。"""
    from jituan.models import ClubOrderDealConfig
    from orders.models import Order

    clubs = list(
        ClubOrderDealConfig.query.filter(auto_settle_enabled=True).values_list('club_id', flat=True)
    )
    if not clubs:
        return {'scanned': 0, 'settled': 0, 'failed': 0, 'clubs': 0}

    now = _db_datetime(timezone.now())
    qs = (
        Order.objects.filter(
            ClubID__in=list(clubs),
            Status=8,
            AutoSettleEligible=True,
            AutoExpireAt__isnull=False,
            AutoExpireAt__lte=now,
        )
        .order_by('AutoExpireAt')[:limit]
    )
    settled = 0
    failed = 0
    for order in qs:
        # 每单再读一次开关：避免扫到一半被关掉仍继续结
        cid = (getattr(order, 'ClubID', None) or '').strip()
        cfg = ClubOrderDealConfig.query.filter(club_id=cid, auto_settle_enabled=True).first()
        if not cfg:
            failed += 1
            logger.info('自动结算跳过 %s: 俱乐部已关开关', order.OrderID)
            continue
        ok, msg = mark_order_completed_unified(order, source='auto_settle', operator='auto')
        if ok:
            settled += 1
            logger.info('自动结算成功 %s', order.OrderID)
        else:
            failed += 1
            logger.warning('自动结算跳过 %s: %s', order.OrderID, msg)
    return {'scanned': settled + failed, 'settled': settled, 'failed': failed, 'clubs': len(clubs)}
