"""财务子模块统计（订单/会员/其他充值），供 /jituan/houtai/*；旧 /houtai/* 不变。"""
from datetime import date, datetime
from decimal import Decimal

from django.db.models import Case, Count, DecimalField, Q, Sum, Value, When
from django.db.models.functions import TruncDate, TruncMonth, TruncYear

from jituan.constants import DATA_SCOPE_ALL
from jituan.models import ClubHuiyuanPrice
from jituan.services.club_context import (
    filter_queryset_by_club,
    resolve_club_id_from_request,
    resolve_club_scope,
)
from jituan.services.club_penalty import filter_gsfenhong_qs, filter_penalty_qs
from orders.models import Order, Penalty
from products.czjilu_types import CZJILU_LEIXING_SHANGJIA, exclude_penalty_from_cz_qs
from products.models import Czjilu, Gsfenhong, Huiyuan, ShangpinLeixing
from rank.models import Bankuai


def _meta(request):
    return {
        'club_id': resolve_club_id_from_request(request),
        'scope': resolve_club_scope(request),
    }


def _parse_time_range(granularity, year, month, summary_date):
    now = date.today()
    if granularity == 'day':
        if not year or not month:
            return None, '按日统计需要年份和月份'
        start_date = date(int(year), int(month), 1)
        end_date = date(int(year), int(month) + 1, 1) if int(month) < 12 else date(int(year) + 1, 1, 1)
        trunc_func = TruncDate('CreateTime')
        if not summary_date:
            summary_date = now.strftime('%Y-%m-%d')
        summary_q = datetime.strptime(summary_date, '%Y-%m-%d').date()
    elif granularity == 'month':
        if not year:
            return None, '按月统计需要年份'
        y = int(year)
        start_date = date(y, 1, 1)
        end_date = date(y + 1, 1, 1)
        trunc_func = TruncMonth('CreateTime')
        if not summary_date:
            summary_date = now.strftime('%Y-%m')
        summary_q = datetime.strptime(summary_date + '-01', '%Y-%m-%d').date()
    elif granularity == 'year':
        y = int(year or now.year)
        start_date = date(y, 1, 1)
        end_date = date(y + 1, 1, 1)
        trunc_func = TruncYear('CreateTime')
        if not summary_date:
            summary_date = str(y)
        summary_q = date(y, 1, 1)
    else:
        return None, '无效的颗粒度'

    start_dt = datetime.combine(start_date, datetime.min.time())
    end_dt = datetime.combine(end_date, datetime.min.time())
    return {
        'start_dt': start_dt,
        'end_dt': end_dt,
        'trunc_func': trunc_func,
        'summary_q': summary_q,
        'granularity': granularity,
        'year': int(year) if year is not None else None,
        'month': int(month) if month is not None else None,
    }, None


def _summary_date_filter(granularity, summary_q, field_prefix='CreateTime'):
    if granularity == 'day':
        return {f'{field_prefix}__date': summary_q}
    if granularity == 'month':
        return {f'{field_prefix}__year': summary_q.year, f'{field_prefix}__month': summary_q.month}
    return {f'{field_prefix}__year': summary_q.year}


def _penalty_trunc_func(granularity):
    if granularity == 'day':
        return TruncDate('UpdateTime')
    if granularity == 'month':
        return TruncMonth('UpdateTime')
    return TruncYear('UpdateTime')


def _penalty_fenhong_aggregates():
    """罚款分红只统计已入账（BonusCredited），与 process_fadan_fenhong 一致。"""
    zero = Value(Decimal('0.00'), output_field=DecimalField(max_digits=12, decimal_places=2))
    return {
        'fenhong_amount': Sum(
            Case(
                When(BonusCredited=True, then='ApplicantBonusAmount'),
                default=zero,
                output_field=DecimalField(max_digits=12, decimal_places=2),
            )
        ),
        'fenhong_count': Count(
            'id',
            filter=Q(BonusCredited=True, ApplicantBonusAmount__gt=0),
        ),
    }


def _czjilu_finance_qs(request, start_dt, end_dt, cz_types):
    """充值财务 czjilu：已支付；type4 排除罚款误标。"""
    qs = filter_queryset_by_club(
        Czjilu.query.filter(
            CreateTime__gte=start_dt,
            CreateTime__lt=end_dt,
            leixing__in=cz_types,
            zhuangtai=3,
        ),
        request,
    )
    if CZJILU_LEIXING_SHANGJIA in cz_types:
        qs = exclude_penalty_from_cz_qs(qs)
    return qs


def _czjilu_summary_qs(request, lx, granularity, summary_q):
    filter_kwargs = {'leixing': lx, 'zhuangtai': 3}
    filter_kwargs.update(_summary_date_filter(granularity, summary_q))
    qs = filter_queryset_by_club(Czjilu.query.filter(**filter_kwargs), request)
    if lx == CZJILU_LEIXING_SHANGJIA:
        qs = exclude_penalty_from_cz_qs(qs)
    return qs


def build_order_types_payload():
    types = ShangpinLeixing.query.filter(shenhezhuangtai=1).values('id', 'jieshao')
    return list(types)


def build_order_finance_payload(
    request,
    granularity,
    year,
    month,
    type_id=0,
    order_source=0,
    summary_date=None,
):
    parsed, err = _parse_time_range(granularity, year, month, summary_date)
    if err:
        return None, err

    start_dt = parsed['start_dt']
    end_dt = parsed['end_dt']
    trunc_func = parsed['trunc_func']
    summary_q = parsed['summary_q']
    granularity = parsed['granularity']

    filters = {
        'CreateTime__gte': start_dt,
        'CreateTime__lt': end_dt,
        'Status__in': [1, 2, 3, 4, 5, 6, 7, 8],
    }
    if int(type_id or 0) != 0:
        filters['ProductTypeID'] = type_id
    if int(order_source or 0) in [1, 2]:
        filters['Platform'] = int(order_source)

    order_base = filter_queryset_by_club(
        Order.query.filter(**filters), request, club_field='ClubID',
    )
    qs = order_base.annotate(period=trunc_func).values('period').annotate(
        order_count=Count('id'),
        order_amount=Sum('Amount'),
        success_count=Count('id', filter=Q(Status=3)),
        success_amount=Sum('Amount', filter=Q(Status=3)),
        refund_count=Count('id', filter=Q(Status=5)),
        PlayerCommission_sum=Sum('PlayerCommission', filter=Q(Status=3)),
        dianpu_fenhong_sum=Sum('pingtai_kuozhan__ShopIncome', filter=Q(Status=3, Platform=1)),
        platform_success_amount=Sum('Amount', filter=Q(Status=3, Platform=1)),
        merchant_success_amount=Sum('Amount', filter=Q(Status=3, Platform=2)),
        platform_dashou=Sum('PlayerCommission', filter=Q(Status=3, Platform=1)),
        merchant_dashou=Sum('PlayerCommission', filter=Q(Status=3, Platform=2)),
    ).order_by('period')

    time_series = []
    for item in qs:
        period_str = (
            item['period'].strftime('%Y-%m-%d') if granularity == 'day'
            else item['period'].strftime('%Y-%m') if granularity == 'month'
            else item['period'].strftime('%Y')
        )
        platform_profit = (item['platform_success_amount'] or 0) - (item['platform_dashou'] or 0) - (item['dianpu_fenhong_sum'] or 0)
        merchant_profit = (item['merchant_success_amount'] or 0) - (item['merchant_dashou'] or 0)
        time_series.append({
            'date': period_str,
            'order_count': item['order_count'],
            'order_amount': float(item['order_amount'] or 0),
            'success_count': item['success_count'],
            'success_amount': float(item['success_amount'] or 0),
            'refund_count': item['refund_count'],
            'dashou_fencheng': float(item['PlayerCommission_sum'] or 0),
            'dianpu_fenhong': float(item['dianpu_fenhong_sum'] or 0),
            'profit': round(float(platform_profit + merchant_profit), 2),
        })

    summary_qs = filter_queryset_by_club(Order.query.filter(**filters), request, club_field='ClubID')
    # 合并 13 次独立 count/aggregate → 1 次 aggregate
    _summary_agg = summary_qs.aggregate(
        total_order_count=Count('id'),
        total_order_amount=Sum('Amount'),
        total_success_count=Count('id', filter=Q(Status=3)),
        total_success_amount=Sum('Amount', filter=Q(Status=3)),
        total_refund_count=Count('id', filter=Q(Status=5)),
        total_dashou=Sum('PlayerCommission', filter=Q(Status=3)),
        total_dianpu_fenhong=Sum('pingtai_kuozhan__ShopIncome', filter=Q(Status=3, Platform=1)),
        platform_amount=Sum('Amount', filter=Q(Status=3, Platform=1)),
        merchant_amount=Sum('Amount', filter=Q(Status=3, Platform=2)),
        platform_dashou_sum=Sum('PlayerCommission', filter=Q(Status=3, Platform=1)),
        merchant_dashou_sum=Sum('PlayerCommission', filter=Q(Status=3, Platform=2)),
    )
    total_order_count = _summary_agg['total_order_count'] or 0
    total_order_amount = _summary_agg['total_order_amount'] or 0
    total_success_count = _summary_agg['total_success_count'] or 0
    total_success_amount = _summary_agg['total_success_amount'] or 0
    total_refund_count = _summary_agg['total_refund_count'] or 0
    total_dashou = _summary_agg['total_dashou'] or 0
    total_dianpu_fenhong = _summary_agg['total_dianpu_fenhong'] or 0
    platform_amount = _summary_agg['platform_amount'] or 0
    merchant_amount = _summary_agg['merchant_amount'] or 0
    platform_dashou_sum = _summary_agg['platform_dashou_sum'] or 0
    merchant_dashou_sum = _summary_agg['merchant_dashou_sum'] or 0
    profit_platform = platform_amount - platform_dashou_sum - total_dianpu_fenhong
    profit_merchant = merchant_amount - merchant_dashou_sum
    total_profit = profit_platform + profit_merchant

    data = {
        **_meta(request),
        'time_series': time_series,
        'summary': {
            'total_order_count': total_order_count,
            'total_order_amount': round(float(total_order_amount), 2),
            'total_success_count': total_success_count,
            'total_success_amount': round(float(total_success_amount), 2),
            'total_refund_count': total_refund_count,
            'total_PlayerCommission': round(float(total_dashou), 2),
            'total_dianpu_fenhong': round(float(total_dianpu_fenhong), 2),
            'total_profit': round(float(total_profit), 2),
        },
    }
    return data, None


def build_chongzhi_finance_payload(request, granularity, year, month, types=None, summary_date=None):
    if types is None:
        types = [2, 3, 4, 5, 6]
    if isinstance(types, list) and len(types) == 0:
        types = [2, 3, 4, 5, 6]

    parsed, err = _parse_time_range(granularity, year, month, summary_date)
    if err:
        return None, err

    start_dt = parsed['start_dt']
    end_dt = parsed['end_dt']
    trunc_func = parsed['trunc_func']
    summary_q = parsed['summary_q']
    granularity = parsed['granularity']

    time_series_map = {}

    def ensure_date(ds):
        if ds not in time_series_map:
            time_series_map[ds] = {}
        return time_series_map[ds]

    cz_types = [t for t in types if t in [2, 3, 4, 5]]
    if cz_types:
        cz_qs = _czjilu_finance_qs(request, start_dt, end_dt, cz_types).annotate(
            period=trunc_func,
        ).values('period', 'leixing').annotate(
            total_count=Count('id'),
            total_amount=Sum('jine'),
        ).order_by('period', 'leixing')

        for item in cz_qs:
            period_str = (
                item['period'].strftime('%Y-%m-%d') if granularity == 'day'
                else item['period'].strftime('%Y-%m') if granularity == 'month'
                else item['period'].strftime('%Y')
            )
            lx = item['leixing']
            amount = float(item['total_amount'] or 0)
            count = item['total_count']
            profit = amount if lx == 3 else 0.0
            day_data = ensure_date(period_str)
            day_data[str(lx)] = {
                'count': count,
                'amount': amount,
                'profit': profit,
                'fenhong_count': 0,
                'fenhong_amount': 0.0,
            }

    if 6 in types:
        penalty_trunc = _penalty_trunc_func(granularity)
        penalty_qs = filter_penalty_qs(
            Penalty.query.filter(
                UpdateTime__gte=start_dt,
                UpdateTime__lt=end_dt,
                Status=2,
            ),
            request,
        ).annotate(period=penalty_trunc).values('period').annotate(
            penalty_count=Count('id'),
            penalty_amount=Sum('FineAmount'),
            **_penalty_fenhong_aggregates(),
        ).order_by('period')

        for item in penalty_qs:
            period_str = (
                item['period'].strftime('%Y-%m-%d') if granularity == 'day'
                else item['period'].strftime('%Y-%m') if granularity == 'month'
                else item['period'].strftime('%Y')
            )
            day_data = ensure_date(period_str)
            penalty_amt = float(item['penalty_amount'] or 0)
            fenhong_amt = float(item['fenhong_amount'] or 0)
            day_data['6'] = {
                'count': item['penalty_count'],
                'amount': penalty_amt,
                'profit': round(penalty_amt - fenhong_amt, 2),
                'fenhong_count': item['fenhong_count'],
                'fenhong_amount': fenhong_amt,
            }

    time_series = []
    for period_str in sorted(time_series_map.keys()):
        entry = {'date': period_str, 'types': {}}
        for type_code in sorted(time_series_map[period_str].keys()):
            entry['types'][type_code] = time_series_map[period_str][type_code]
        time_series.append(entry)

    summary_result = {'by_type': {}, 'total_count': 0, 'total_amount': 0.0, 'total_profit': 0.0}

    for lx in cz_types:
        agg = _czjilu_summary_qs(request, lx, granularity, summary_q).aggregate(
            count=Count('id'), amount=Sum('jine'),
        )
        count = agg['count'] or 0
        amount = float(agg['amount'] or 0)
        profit = amount if lx == 3 else 0.0
        summary_result['by_type'][str(lx)] = {
            'count': count,
            'amount': round(amount, 2),
            'profit': round(profit, 2),
            'fenhong_count': 0,
            'fenhong_amount': 0.0,
        }
        summary_result['total_count'] += count
        summary_result['total_amount'] += amount
        summary_result['total_profit'] += profit

    if 6 in types:
        penalty_filter = {'Status': 2}
        penalty_filter.update(_summary_date_filter(granularity, summary_q, field_prefix='UpdateTime'))
        agg = filter_penalty_qs(Penalty.query.filter(**penalty_filter), request).aggregate(
            count=Count('id'),
            amount=Sum('FineAmount'),
            **_penalty_fenhong_aggregates(),
        )
        penalty_count = agg['count'] or 0
        penalty_amount = float(agg['amount'] or 0)
        fh_amount = float(agg['fenhong_amount'] or 0)
        fh_count = agg['fenhong_count'] or 0
        profit = round(penalty_amount - fh_amount, 2)
        summary_result['by_type']['6'] = {
            'count': penalty_count,
            'amount': round(penalty_amount, 2),
            'profit': profit,
            'fenhong_count': fh_count,
            'fenhong_amount': round(fh_amount, 2),
        }
        summary_result['total_count'] += penalty_count
        summary_result['total_amount'] += penalty_amount
        summary_result['total_profit'] += profit

    summary_result['total_amount'] = round(summary_result['total_amount'], 2)
    summary_result['total_profit'] = round(summary_result['total_profit'], 2)

    return {
        **_meta(request),
        'time_series': time_series,
        'summary': summary_result,
    }, None


def build_huiyuan_bankuai_payload(request):
    club_id = resolve_club_id_from_request(request)
    price_map = {}
    if resolve_club_scope(request) != DATA_SCOPE_ALL:
        for row in ClubHuiyuanPrice.query.filter(club_id=club_id, is_enabled=True):
            price_map[row.huiyuan_id] = {
                'jiage': float(row.jiage or 0),
                'guanshifc': float(row.guanshifc or 0),
                'zuzhangfc': float(row.zuzhangfc or 0),
            }

    # 批量预取所有 bankuai 和 huiyuan，避免循环内 N+1
    _bankuai_qs = Bankuai.query.all().only('bankuai_id', 'mingcheng')
    _members_by_bankuai = {}
    for m in Huiyuan.query.values('huiyuan_id', 'jieshao', 'bankuai_id'):
        _members_by_bankuai.setdefault(m['bankuai_id'], []).append(m)

    bankuai_list = []
    for bk in _bankuai_qs:
        members = []
        for m in _members_by_bankuai.get(bk.bankuai_id, []):
            item = dict(m)
            if m['huiyuan_id'] in price_map:
                item['club_price'] = price_map[m['huiyuan_id']]
            members.append(item)
        bankuai_list.append({
            'bankuai_id': bk.bankuai_id,
            'mingcheng': bk.mingcheng,
            'members': members,
        })

    return {**_meta(request), 'bankuai_list': bankuai_list}


def build_huiyuan_stats_payload(
    request,
    huiyuan_id,
    granularity,
    year,
    month,
    include_guanshi=False,
    include_zuzhang=False,
    summary_date=None,
):
    if not huiyuan_id:
        return None, '缺少会员ID'

    parsed, err = _parse_time_range(granularity, year, month, summary_date)
    if err:
        return None, err

    start_dt = parsed['start_dt']
    end_dt = parsed['end_dt']
    trunc_func = parsed['trunc_func']
    summary_q = parsed['summary_q']
    granularity = parsed['granularity']

    chongzhi_filter = {
        'huiyuan_id': huiyuan_id,
        'leixing': 1,
        'zhuangtai': 3,
        'CreateTime__gte': start_dt,
        'CreateTime__lt': end_dt,
    }
    fenhong_filter = {
        'huiyuan_id': huiyuan_id,
        'CreateTime__gte': start_dt,
        'CreateTime__lt': end_dt,
    }

    chongzhi_qs = filter_queryset_by_club(
        Czjilu.query.filter(**chongzhi_filter), request,
    ).annotate(period=trunc_func).values('period').annotate(
        chongzhi_count=Count('id'),
        chongzhi_amount=Sum('jine'),
    ).order_by('period')

    fenhong_qs = None
    if include_guanshi or include_zuzhang:
        fenhong_agg = {}
        if include_guanshi:
            fenhong_agg['guanshi_amount'] = Sum('fenhong')
        if include_zuzhang:
            fenhong_agg['zuzhang_amount'] = Sum('zuzhang_fenhong')
        fenhong_qs = filter_gsfenhong_qs(
            Gsfenhong.query.filter(**fenhong_filter), request,
        ).annotate(period=trunc_func).values('period').annotate(**fenhong_agg).order_by('period')

    data_map = {}
    for item in chongzhi_qs:
        period_str = (
            item['period'].strftime('%Y-%m-%d') if granularity == 'day'
            else item['period'].strftime('%Y-%m') if granularity == 'month'
            else item['period'].strftime('%Y')
        )
        data_map[period_str] = {
            'chongzhi_count': item['chongzhi_count'],
            'chongzhi_amount': float(item['chongzhi_amount'] or 0),
        }

    if fenhong_qs:
        for item in fenhong_qs:
            period_str = (
                item['period'].strftime('%Y-%m-%d') if granularity == 'day'
                else item['period'].strftime('%Y-%m') if granularity == 'month'
                else item['period'].strftime('%Y')
            )
            if period_str not in data_map:
                data_map[period_str] = {'chongzhi_count': 0, 'chongzhi_amount': 0.0}
            if include_guanshi:
                data_map[period_str]['guanshi_amount'] = float(item.get('guanshi_amount') or 0)
            if include_zuzhang:
                data_map[period_str]['zuzhang_amount'] = float(item.get('zuzhang_amount') or 0)

    time_series = []
    for period_str, vals in data_map.items():
        guanshi = vals.get('guanshi_amount', 0) if include_guanshi else 0
        zuzhang = vals.get('zuzhang_amount', 0) if include_zuzhang else 0
        vals['shouyi'] = round(vals['chongzhi_amount'] - guanshi - zuzhang, 2)
        vals['date'] = period_str
        time_series.append(vals)
    time_series.sort(key=lambda x: x['date'])

    summary_chongzhi_filter = dict(chongzhi_filter)
    summary_chongzhi_filter.pop('CreateTime__gte', None)
    summary_chongzhi_filter.pop('CreateTime__lt', None)
    summary_chongzhi_filter.update(_summary_date_filter(granularity, summary_q))
    summary_chongzhi = filter_queryset_by_club(
        Czjilu.query.filter(**summary_chongzhi_filter), request,
    ).aggregate(total_count=Count('id'), total_amount=Sum('jine'))
    total_chongzhi_count = summary_chongzhi['total_count'] or 0
    total_chongzhi_amount = float(summary_chongzhi['total_amount'] or 0)

    total_guanshi = 0.0
    total_zuzhang = 0.0
    if include_guanshi or include_zuzhang:
        summary_fenhong_filter = dict(fenhong_filter)
        summary_fenhong_filter.pop('CreateTime__gte', None)
        summary_fenhong_filter.pop('CreateTime__lt', None)
        summary_fenhong_filter.update(_summary_date_filter(granularity, summary_q))
        fenhong_agg = {}
        if include_guanshi:
            fenhong_agg['guanshi_sum'] = Sum('fenhong')
        if include_zuzhang:
            fenhong_agg['zuzhang_sum'] = Sum('zuzhang_fenhong')
        summary_fenhong = filter_gsfenhong_qs(
            Gsfenhong.query.filter(**summary_fenhong_filter), request,
        ).aggregate(**fenhong_agg)
        total_guanshi = float(summary_fenhong.get('guanshi_sum') or 0)
        total_zuzhang = float(summary_fenhong.get('zuzhang_sum') or 0)

    total_shouyi = round(total_chongzhi_amount - total_guanshi - total_zuzhang, 2)

    return {
        **_meta(request),
        'time_series': time_series,
        'summary': {
            'total_chongzhi_count': total_chongzhi_count,
            'total_chongzhi_amount': total_chongzhi_amount,
            'total_guanshi_fenhong': total_guanshi,
            'total_zuzhang_fenhong': total_zuzhang,
            'total_shouyi': total_shouyi,
        },
    }, None
