"""客服后台：资金冻结配置 / 明细 / 风险指标。"""
from __future__ import annotations

import json
from decimal import Decimal
from typing import Any, Dict, List, Optional, Tuple

from django.db.models import Q

from jituan.constants import CLUB_ID_DEFAULT
from jituan.models import ClubFundFreezeConfig, FundFreezeLedger, FundUnfreezeEvent
from jituan.services.club_context import filter_user_related_by_club, resolve_club_id_from_request
from jituan.services.fund_freeze import (
    ROLE_DASHOU,
    ROLE_GUANSHI,
    ROLE_SHENHEGUAN,
    ROLE_ZUZHANG,
    apply_unfreeze_step,
    invalidate_freeze_config_cache,
    split_preview,
)

ROLES = (
    (ROLE_DASHOU, '打手'),
    (ROLE_GUANSHI, '管事'),
    (ROLE_ZUZHANG, '组长'),
    (ROLE_SHENHEGUAN, '考核官'),
)

DEFAULT_INSTALLMENT = {
    'steps': [
        {'seq': 1, 'after_days': 7, 'type': 'ratio', 'value': 0.3, 'need_condition': False},
        {'seq': 2, 'after_days': 14, 'type': 'ratio', 'value': 0.3, 'need_condition': True},
        {'seq': 3, 'after_days': 30, 'type': 'remain', 'value': 1, 'need_condition': True},
    ]
}


def _club(request) -> str:
    return (resolve_club_id_from_request(request) or CLUB_ID_DEFAULT).strip() or CLUB_ID_DEFAULT


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


def _cfg_to_dict(row: Optional[ClubFundFreezeConfig], club_id: str, role: str) -> Dict[str, Any]:
    if not row:
        return {
            'club_id': club_id,
            'role': role,
            'enabled': False,
            'freeze_mode': 1,
            'freeze_rate': '0',
            'freeze_fixed': '0',
            'freeze_cap': '0',
            'min_credit_to_freeze': '0',
            'source_scope': ['order_settle'],
            'unfreeze_trigger': 'time_only',
            'schedule_mode': 'all_at_once',
            'installment_json': {'steps': [{'seq': 1, 'after_days': 15, 'type': 'remain', 'value': 1, 'need_condition': False}]},
            'condition_json': {
                'min_completed_orders': 0,
                'max_fine_rate': 1,
                'fine_rate_window_days': 30,
                'max_complaint_rate': 1,
                'complaint_rate_window_days': 30,
                'min_valid_invites': 0,
            },
            'freeze_min_days': 0,
            'remark': '',
            'display_reason': '',
            'exists': False,
        }
    return {
        'club_id': row.club_id,
        'role': row.role,
        'enabled': bool(row.enabled),
        'freeze_mode': int(row.freeze_mode or 1),
        'freeze_rate': _dec(row.freeze_rate),
        'freeze_fixed': _dec(row.freeze_fixed),
        'freeze_cap': _dec(row.freeze_cap),
        'min_credit_to_freeze': _dec(row.min_credit_to_freeze),
        'source_scope': row.source_scope or [],
        'unfreeze_trigger': row.unfreeze_trigger or 'time_only',
        'schedule_mode': row.schedule_mode or 'all_at_once',
        'installment_json': row.installment_json or {},
        'condition_json': row.condition_json or {},
        'freeze_min_days': int(row.freeze_min_days or 0),
        'remark': row.remark or '',
        'display_reason': row.remark or '',
        'updated_by': row.updated_by or '',
        'exists': True,
    }


def build_config_payload(request) -> Dict[str, Any]:
    club_id = _club(request)
    rows = {
        r.role: r
        for r in ClubFundFreezeConfig.query.filter(club_id=club_id)
    }
    items = []
    for role, name in ROLES:
        d = _cfg_to_dict(rows.get(role), club_id, role)
        d['role_name'] = name
        avail, frozen = split_preview(club_id, role, Decimal('100'), 'order_settle')
        d['preview_100'] = {'available': str(avail), 'frozen': str(frozen)}
        items.append(d)
    return {'club_id': club_id, 'roles': items}


def save_config(request, data: dict, operator: str = '') -> Tuple[Optional[dict], Optional[str]]:
    club_id = _club(request)
    role = (data.get('role') or '').strip().lower()
    if role not in {r[0] for r in ROLES}:
        return None, '无效角色'

    enabled = bool(data.get('enabled'))
    freeze_mode = int(data.get('freeze_mode') or 1)
    if freeze_mode not in (1, 2, 3):
        return None, 'freeze_mode 无效'

    try:
        freeze_rate = Decimal(str(data.get('freeze_rate') or 0))
        freeze_fixed = Decimal(str(data.get('freeze_fixed') or 0))
        freeze_cap = Decimal(str(data.get('freeze_cap') or 0))
        min_credit = Decimal(str(data.get('min_credit_to_freeze') or 0))
    except Exception:
        return None, '金额/比例格式错误'

    if freeze_rate < 0 or freeze_rate > 1:
        return None, '冻结比例须在 0~1 之间（如 0.2=20%）'

    source_scope = data.get('source_scope')
    if source_scope is None:
        # 默认同时覆盖订单结算与分红（管事/组长主要靠分红入账）
        source_scope = ['order_settle', 'fenhong', 'withdraw_reject', 'club_migrate']
    if isinstance(source_scope, str):
        try:
            source_scope = json.loads(source_scope)
        except Exception:
            source_scope = [source_scope]

    unfreeze_trigger = (data.get('unfreeze_trigger') or 'time_only').strip()
    if unfreeze_trigger not in (
        'time_only', 'condition_only', 'time_and_condition', 'time_or_condition', 'manual_only',
    ):
        return None, '解冻触发模式无效'

    schedule_mode = (data.get('schedule_mode') or 'all_at_once').strip()
    if schedule_mode not in ('all_at_once', 'installment'):
        return None, '分期模式无效'

    installment_json = data.get('installment_json') or {}
    if isinstance(installment_json, str):
        try:
            installment_json = json.loads(installment_json)
        except Exception:
            return None, 'installment_json 不是合法 JSON'
    if schedule_mode == 'installment':
        steps = installment_json.get('steps') if isinstance(installment_json, dict) else None
        if not steps:
            installment_json = dict(DEFAULT_INSTALLMENT)
            steps = installment_json['steps']
        # 规范化：自由添加的分期重新编号，校验类型
        normalized = []
        for i, s in enumerate(steps):
            if not isinstance(s, dict):
                return None, f'第{i + 1}期格式错误'
            typ = (s.get('type') or 'ratio').strip().lower()
            if typ not in ('ratio', 'fixed', 'remain'):
                return None, f'第{i + 1}期类型无效（ratio/fixed/remain）'
            try:
                after_days = max(0, int(s.get('after_days') or 0))
            except (TypeError, ValueError):
                return None, f'第{i + 1}期天数无效'
            try:
                value = float(s.get('value') or 0)
            except (TypeError, ValueError):
                return None, f'第{i + 1}期数值无效'
            if typ == 'ratio' and (value < 0 or value > 1):
                return None, f'第{i + 1}期比例须在 0~1'
            normalized.append({
                'seq': i + 1,
                'after_days': after_days,
                'type': typ,
                'value': value if typ != 'remain' else 1,
                'need_condition': bool(s.get('need_condition')),
            })
        if not normalized:
            return None, '请至少添加一期分期'
        # 最后一期建议 remain，若全是 ratio 且合计偏离太大仅告警不拦截——尾差由 remain 或末步吃完
        installment_json = {'steps': normalized}

    condition_json = data.get('condition_json') or {}
    if isinstance(condition_json, str):
        try:
            condition_json = json.loads(condition_json)
        except Exception:
            return None, 'condition_json 不是合法 JSON'

    freeze_min_days = int(data.get('freeze_min_days') or 0)
    if freeze_min_days < 0:
        return None, '最短冻结天数不能为负'

    # 用户可见冻结说明：优先 display_reason，兼容 remark
    display_reason = (data.get('display_reason') or data.get('remark') or '')[:255]

    row = ClubFundFreezeConfig.query.filter(club_id=club_id, role=role).first()
    if not row:
        row = ClubFundFreezeConfig(
            club_id=club_id,
            role=role,
        )
    row.enabled = enabled
    row.freeze_mode = freeze_mode
    row.freeze_rate = freeze_rate
    row.freeze_fixed = freeze_fixed
    row.freeze_cap = freeze_cap
    row.min_credit_to_freeze = min_credit
    row.source_scope = source_scope
    row.unfreeze_trigger = unfreeze_trigger
    row.schedule_mode = schedule_mode
    row.installment_json = installment_json
    row.condition_json = condition_json
    row.freeze_min_days = freeze_min_days
    row.remark = display_reason
    row.updated_by = (operator or '')[:32]
    row.save()
    invalidate_freeze_config_cache(club_id, role)
    out = _cfg_to_dict(row, club_id, role)
    out['role_name'] = dict(ROLES).get(role, role)
    return out, None


STATUS_LABEL = {
    FundFreezeLedger.STATUS_FROZEN: '冻结中',
    FundFreezeLedger.STATUS_PARTIAL: '部分解冻',
    FundFreezeLedger.STATUS_DONE: '已解冻',
    FundFreezeLedger.STATUS_VOID: '已作废',
}


def list_ledgers(request, data: dict) -> Dict[str, Any]:
    club_id = _club(request)
    page = max(1, int(data.get('page') or 1))
    page_size = min(100, max(1, int(data.get('page_size') or 20)))
    role = (data.get('role') or '').strip().lower()
    status = data.get('status')
    user_id = (data.get('user_id') or data.get('yonghuid') or '').strip()
    order_id = (data.get('order_id') or '').strip()
    kw = (data.get('keyword') or '').strip()

    qs = FundFreezeLedger.query.filter(club_id=club_id)
    if role:
        qs = qs.filter(role=role)
    if status not in (None, ''):
        try:
            qs = qs.filter(status=int(status))
        except (TypeError, ValueError):
            pass
    if user_id:
        qs = qs.filter(user_id=user_id)
    if order_id:
        qs = qs.filter(order_id=order_id)
    if kw:
        qs = qs.filter(Q(user_id__icontains=kw) | Q(order_id__icontains=kw) | Q(biz_id__icontains=kw))

    total = qs.count()
    rows = qs.order_by('-id')[(page - 1) * page_size: page * page_size]
    items = []
    for r in rows:
        remain = Decimal(str(r.amount_total or 0)) - Decimal(str(r.amount_unfrozen or 0))
        items.append({
            'id': r.id,
            'club_id': r.club_id,
            'user_id': r.user_id,
            'role': r.role,
            'role_name': dict(ROLES).get(r.role, r.role),
            'amount_total': _dec(r.amount_total),
            'amount_unfrozen': _dec(r.amount_unfrozen),
            'amount_remain': _dec(remain),
            'status': r.status,
            'status_label': STATUS_LABEL.get(r.status, str(r.status)),
            'frozen_at': r.frozen_at.isoformat() if r.frozen_at else '',
            'next_eligible_at': r.next_eligible_at.isoformat() if r.next_eligible_at else '',
            'source_type': r.source_type,
            'order_id': r.order_id,
            'czjilu_id': r.czjilu_id,
            'biz_id': r.biz_id,
            'freeze_reason': r.freeze_reason,
            'current_step': r.current_step,
        })
    return {
        'club_id': club_id,
        'list': items,
        'total': total,
        'page': page,
        'page_size': page_size,
    }


def list_unfreeze_events(ledger_id: int, club_id: str) -> List[dict]:
    qs = FundUnfreezeEvent.query.filter(ledger_id=ledger_id, club_id=club_id).order_by('-id')[:50]
    return [{
        'id': e.id,
        'step_seq': e.step_seq,
        'amount': _dec(e.amount),
        'unfrozen_at': e.unfrozen_at.isoformat() if e.unfrozen_at else '',
        'trigger': e.trigger,
        'reason': e.reason,
        'operator_id': e.operator_id,
    } for e in qs]


def manual_unfreeze(request, data: dict, operator: str = '') -> Tuple[Optional[dict], Optional[str]]:
    club_id = _club(request)
    try:
        ledger_id = int(data.get('ledger_id') or data.get('id') or 0)
    except (TypeError, ValueError):
        return None, 'ledger_id 无效'
    if ledger_id <= 0:
        return None, '缺少 ledger_id'

    ledger = FundFreezeLedger.query.filter(id=ledger_id, club_id=club_id).first()
    if not ledger:
        return None, '冻结单不存在或不属于当前俱乐部'

    mode = (data.get('mode') or 'step').strip()  # step | all
    if mode == 'all':
        # 循环解完（最多 20 步防死循环）
        last = None
        for _ in range(20):
            last = apply_unfreeze_step(
                ledger_id,
                trigger='manual',
                operator_id=operator,
                reason=(data.get('reason') or '后台手动全部解冻')[:255],
                force=True,
            )
            if not last.get('ok'):
                return None, last.get('msg') or '解冻失败'
            if last.get('done'):
                break
        return last, None

    result = apply_unfreeze_step(
        ledger_id,
        trigger='manual',
        operator_id=operator,
        reason=(data.get('reason') or '后台手动解冻一步')[:255],
        force=True,
    )
    if not result.get('ok'):
        return None, result.get('msg') or '解冻失败'
    return result, None


def list_risk_users(request, data: dict) -> Dict[str, Any]:
    """从角色扩展表读风险快照 + 冻结池（按俱乐部用户过滤）。"""
    from users.models import UserDashou, UserGuanshi, UserZuzhang, UserShenheguan
    from jituan.services.club_context import filter_user_related_by_club

    club_id = _club(request)
    role = (data.get('role') or ROLE_DASHOU).strip().lower()
    page = max(1, int(data.get('page') or 1))
    page_size = min(100, max(1, int(data.get('page_size') or 20)))
    keyword = (data.get('keyword') or '').strip()
    only_frozen = bool(data.get('only_frozen'))

    mapping = {
        ROLE_DASHOU: UserDashou,
        ROLE_GUANSHI: UserGuanshi,
        ROLE_ZUZHANG: UserZuzhang,
        ROLE_SHENHEGUAN: UserShenheguan,
    }
    model = mapping.get(role)
    if not model:
        return {'club_id': club_id, 'list': [], 'total': 0, 'page': page, 'page_size': page_size}

    qs = model.query.select_related('user').all()
    qs = filter_user_related_by_club(qs, request, user_club_path='user__ClubID')

    if keyword:
        qs = qs.filter(user__UserUID__icontains=keyword)
    if only_frozen:
        qs = qs.filter(dongjie_yue__gt=0)

    total = qs.count()
    rows = qs.order_by('-dongjie_yue', '-id')[(page - 1) * page_size: page * page_size]
    items = []
    for p in rows:
        uid = getattr(p.user, 'UserUID', '') if p.user_id else ''
        if role == ROLE_ZUZHANG:
            available = _dec(getattr(p, 'ketixian_jine', 0))
        else:
            available = _dec(getattr(p, 'yue', 0))
        items.append({
            'user_id': uid,
            'role': role,
            'role_name': dict(ROLES).get(role, role),
            'available': available,
            'dongjie_yue': _dec(getattr(p, 'dongjie_yue', 0)),
            'risk_fine_rate': _dec(getattr(p, 'risk_fine_rate', 0)),
            'risk_complaint_rate': _dec(getattr(p, 'risk_complaint_rate', 0)),
            'risk_completed_orders': int(getattr(p, 'risk_completed_orders', 0) or 0),
            'risk_valid_invites': int(getattr(p, 'risk_valid_invites', 0) or 0),
            'risk_stat_at': p.risk_stat_at.isoformat() if getattr(p, 'risk_stat_at', None) else '',
            'nicheng': getattr(p, 'nicheng', '') or '',
        })
    return {
        'club_id': club_id,
        'role': role,
        'list': items,
        'total': total,
        'page': page,
        'page_size': page_size,
    }
