"""资金冻结：解冻条件评估（实时算窗口指标，并回写角色 risk_* 快照）。"""
from __future__ import annotations

import logging
from datetime import timedelta
from decimal import Decimal, ROUND_HALF_UP
from typing import Any, Dict, Optional, Tuple

from django.utils import timezone

logger = logging.getLogger(__name__)


def _dec(v, default='0') -> Decimal:
    try:
        return Decimal(str(v if v is not None else default)).quantize(
            Decimal('0.000001'), rounding=ROUND_HALF_UP
        )
    except Exception:
        return Decimal(default)


def _int(v, default=0) -> int:
    try:
        return int(v)
    except (TypeError, ValueError):
        return default


def _flag(cfg: dict, enable_key: str, legacy_active: bool) -> bool:
    """新配置用 enable_*；旧配置无 enable 时按 legacy_active 推断。"""
    if enable_key in cfg:
        return bool(cfg.get(enable_key))
    return legacy_active


def compute_risk_metrics(
    club_id: str,
    user_id: str,
    role: str,
    condition_json: Optional[dict] = None,
) -> Dict[str, Any]:
    """按窗口统计完成单量 / 罚款率 / 投诉率 / 有效邀请。失败时返回安全默认。"""
    cfg = condition_json or {}
    fine_days = max(1, _int(cfg.get('fine_rate_window_days'), 30))
    complaint_days = max(1, _int(cfg.get('complaint_rate_window_days'), 30))
    order_days = max(fine_days, complaint_days, _int(cfg.get('order_window_days'), 30))
    now = timezone.now()
    since_order = now - timedelta(days=order_days)
    since_fine = now - timedelta(days=fine_days)
    since_complaint = now - timedelta(days=complaint_days)

    completed = 0
    fine_count = 0
    complaint_count = 0
    valid_invites = 0

    try:
        from orders.models import Order, Penalty

        if role == 'dashou':
            completed = Order.query.filter(
                ClubID=club_id,
                PlayerID=user_id,
                Status=3,
                CreateTime__gte=since_order,
            ).count()
            fine_count = Penalty.query.filter(
                PenalizedUserID=user_id,
                Identity=1,
                Status__in=[1, 2, 3, 5],
                CreateTime__gte=since_fine,
            ).count()
            # 投诉：窗口内该打手完成单关联的工单数
            try:
                from gongdan.models import Gongdan
                order_ids = list(
                    Order.query.filter(
                        ClubID=club_id,
                        PlayerID=user_id,
                        Status=3,
                        CreateTime__gte=since_complaint,
                    ).values_list('OrderID', flat=True)[:800]
                )
                if order_ids:
                    complaint_count = Gongdan.query.filter(
                        order_id__in=order_ids,
                    ).count()
            except Exception as e:
                logger.warning('complaint metrics skip: %s', e)
            valid_invites = 0

        elif role == 'guanshi':
            from users.models import User, UserGuanshi, UserDashou
            u = User.query.filter(UserUID=user_id).first()
            gs = UserGuanshi.query.filter(user=u).first() if u else None
            if gs is not None:
                valid_invites = int(getattr(gs, 'yaogingshuliang', 0) or 0)
                # 管事「完成单」近似：窗口内其邀请打手的成交单量
                dashou_uids = list(
                    UserDashou.query.filter(yaoqingren=user_id).values_list(
                        'user__UserUID', flat=True
                    )[:500]
                )
                if dashou_uids:
                    completed = Order.query.filter(
                        ClubID=club_id,
                        PlayerID__in=dashou_uids,
                        Status=3,
                        CreateTime__gte=since_order,
                    ).count()
            fine_count = Penalty.query.filter(
                PenalizedUserID=user_id,
                Identity=2,
                Status__in=[1, 2, 3, 5],
                CreateTime__gte=since_fine,
            ).count()
            try:
                from gongdan.models import Gongdan
                complaint_count = Gongdan.query.filter(
                    guanshi_id=user_id,
                    CreateTime__gte=since_complaint,
                ).count()
            except Exception:
                pass

        elif role == 'zuzhang':
            from users.models import User, UserGuanshi, UserZuzhang
            u = User.query.filter(UserUID=user_id).first()
            zz = UserZuzhang.query.filter(user=u).first() if u else None
            if zz is not None:
                valid_invites = UserGuanshi.query.filter(yaoqingren=user_id).count()
            fine_count = Penalty.query.filter(
                PenalizedUserID=user_id,
                Identity=4,
                Status__in=[1, 2, 3, 5],
                CreateTime__gte=since_fine,
            ).count()
            completed = valid_invites  # 组长无直接结单，用邀请管事数作「完成」近似时可被 min_orders 覆盖

        else:  # shenheguan
            fine_count = Penalty.query.filter(
                PenalizedUserID=user_id,
                Status__in=[1, 2, 3, 5],
                CreateTime__gte=since_fine,
            ).count()
    except Exception as e:
        logger.exception('compute_risk_metrics failed user=%s role=%s: %s', user_id, role, e)

    denom = max(completed, 1)
    fine_rate = _dec(fine_count) / _dec(denom)
    complaint_rate = _dec(complaint_count) / _dec(denom)

    return {
        'completed_orders': completed,
        'fine_count': fine_count,
        'complaint_count': complaint_count,
        'fine_rate': str(fine_rate),
        'complaint_rate': str(complaint_rate),
        'valid_invites': valid_invites,
        'order_window_days': order_days,
        'fine_rate_window_days': fine_days,
        'complaint_rate_window_days': complaint_days,
    }


def write_risk_snapshot(profile, metrics: dict) -> None:
    if profile is None:
        return
    try:
        profile.__class__.query.filter(pk=profile.pk).update(
            risk_fine_rate=_dec(metrics.get('fine_rate')),
            risk_complaint_rate=_dec(metrics.get('complaint_rate')),
            risk_completed_orders=_int(metrics.get('completed_orders')),
            risk_valid_invites=_int(metrics.get('valid_invites')),
            risk_stat_at=timezone.now(),
        )
    except Exception as e:
        logger.warning('write_risk_snapshot failed: %s', e)


def evaluate_unfreeze_conditions(
    club_id: str,
    user_id: str,
    role: str,
    condition_json: Optional[dict],
    profile=None,
) -> Tuple[bool, Dict[str, Any]]:
    """
    返回 (是否达标, 指标快照)。
    未勾选的条件不参与；全部未勾选视为「无条件限制」→ 达标。
    """
    cfg = condition_json or {}
    metrics = compute_risk_metrics(club_id, user_id, role, cfg)
    write_risk_snapshot(profile, metrics)

    checks = []
    reasons = []

    # 最低完成单量
    min_orders = _int(cfg.get('min_completed_orders'), 0)
    if _flag(cfg, 'enable_min_completed_orders', min_orders > 0):
        ok = metrics['completed_orders'] >= min_orders
        checks.append(ok)
        if not ok:
            reasons.append(
                f"完成单量 {metrics['completed_orders']}/{min_orders}"
            )

    # 最高罚款率
    max_fine = _dec(cfg.get('max_fine_rate'), '1')
    if _flag(cfg, 'enable_max_fine_rate', 0 <= max_fine < 1):
        ok = _dec(metrics['fine_rate']) <= max_fine
        checks.append(ok)
        if not ok:
            reasons.append(
                f"罚款率 {metrics['fine_rate']} > {max_fine}"
            )

    # 最高投诉率
    max_comp = _dec(cfg.get('max_complaint_rate'), '1')
    if _flag(cfg, 'enable_max_complaint_rate', 0 <= max_comp < 1):
        ok = _dec(metrics['complaint_rate']) <= max_comp
        checks.append(ok)
        if not ok:
            reasons.append(
                f"投诉率 {metrics['complaint_rate']} > {max_comp}"
            )

    # 最低有效邀请
    min_inv = _int(cfg.get('min_valid_invites'), 0)
    if _flag(cfg, 'enable_min_valid_invites', min_inv > 0):
        ok = metrics['valid_invites'] >= min_inv
        checks.append(ok)
        if not ok:
            reasons.append(
                f"有效邀请 {metrics['valid_invites']}/{min_inv}"
            )

    if not checks:
        passed = True
    else:
        passed = all(checks)

    snap = {
        **metrics,
        'passed': passed,
        'fail_reasons': reasons,
    }
    return passed, snap
