"""总会员（包含式会员）：购买一个会员，可接多个子会员类型订单。"""
from datetime import timedelta

from django.utils import timezone

from products.models import Huiyuan, Huiyuangoumai


def normalize_huiyuan_id(huiyuan_id):
    if huiyuan_id is None:
        return ''
    return str(huiyuan_id).strip()


def huiyuan_ids_match(a, b):
    """会员 ID 等价比较（忽略首尾空格；001 与 1 视为相同）。"""
    left = normalize_huiyuan_id(a)
    right = normalize_huiyuan_id(b)
    if not left or not right:
        return False
    if left == right:
        return True
    left_core = left.lstrip('0') or '0'
    right_core = right.lstrip('0') or '0'
    return left_core == right_core


def expand_huiyuan_id_variants(huiyuan_id):
    """老小程序用 == 比较会员 ID，补齐 1 / 01 / 001 等写法。"""
    hid = normalize_huiyuan_id(huiyuan_id)
    if not hid:
        return []
    variants = []
    for candidate in (hid, hid.lstrip('0') or '0'):
        if candidate and candidate not in variants:
            variants.append(candidate)
    core = hid.lstrip('0') or '0'
    if core.isdigit():
        for width in (3, 4, 6):
            padded = core.zfill(width)
            if padded not in variants:
                variants.append(padded)
    return variants


def format_legacy_daoqi(dt):
    if not dt:
        return ''
    if timezone.is_aware(dt):
        dt = timezone.localtime(dt)
    return dt.strftime('%Y-%m-%d %H:%M:%S')


def _normalize_datetime_for_compare(dt):
    """统一 naive/aware，避免 daoqi 与 now 比较时报 TypeError。"""
    from django.conf import settings

    if dt is None:
        return timezone.now()
    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 get_bundle_included_ids(club_id, bundle_huiyuan_id):
    from jituan.models import ClubHuiyuanBundleInclude

    if not club_id or not bundle_huiyuan_id:
        return []
    for bid in expand_huiyuan_id_variants(bundle_huiyuan_id):
        ids = list(
            ClubHuiyuanBundleInclude.query.filter(
                club_id=club_id,
                bundle_huiyuan_id=bid,
            ).values_list('included_huiyuan_id', flat=True)
        )
        if ids:
            return ids
    stored_bids = ClubHuiyuanBundleInclude.objects.filter(
        club_id=club_id,
    ).values_list('bundle_huiyuan_id', flat=True).distinct()
    for stored_bid in stored_bids:
        if huiyuan_ids_match(stored_bid, bundle_huiyuan_id):
            return list(
                ClubHuiyuanBundleInclude.query.filter(
                    club_id=club_id,
                    bundle_huiyuan_id=stored_bid,
                ).values_list('included_huiyuan_id', flat=True)
            )
    return []


def _membership_club_candidates(yonghu_id, club_id=None, record_club_id=None):
    """
    会员/总会员子类型解析用的俱乐部候选（有序）。
    Huiyuangoumai/Czjilu.club_id 历史上默认 xq，星之界用户落库为 xq 时不能盲信。
    """
    from jituan.constants import CLUB_ID_DEFAULT

    record_cid = (record_club_id or '').strip()
    request_cid = (club_id or '').strip()
    user_cid = _resolve_user_club_id(yonghu_id, club_id=club_id)

    candidates = []

    def add(cid):
        c = (cid or '').strip()
        if c and c not in candidates:
            candidates.append(c)

    if record_cid and record_cid != CLUB_ID_DEFAULT:
        add(record_cid)
    add(request_cid)
    add(user_cid)
    if record_cid:
        add(record_cid)
    add(CLUB_ID_DEFAULT)
    return candidates


def resolve_bundle_included_ids(bundle_huiyuan_id, club_id=None, yonghu_id=None, record_club_id=None):
    """按候选俱乐部合并总会员包含的子会员（去重，只读）。"""
    if not bundle_huiyuan_id:
        return []
    merged = []
    seen = set()
    for cid in _membership_club_candidates(
        yonghu_id, club_id=club_id, record_club_id=record_club_id,
    ):
        for hid in get_bundle_included_ids(cid, bundle_huiyuan_id):
            norm = normalize_huiyuan_id(hid)
            core = norm.lstrip('0') or '0' if norm else ''
            dedupe_key = core or norm
            if dedupe_key and dedupe_key not in seen:
                seen.add(dedupe_key)
                merged.append(hid)
    return merged


def _included_huiyuan_id_set(included_ids):
    included_set = set()
    for x in included_ids or []:
        for variant in expand_huiyuan_id_variants(x):
            sx = normalize_huiyuan_id(variant)
            if sx:
                included_set.add(sx)
                included_set.add(sx.lstrip('0') or '0')
    return included_set


def _resolve_user_club_id(yonghu_id, club_id=None):
    """解析用户所属俱乐部（体验/正式同一套；总会员子类型依赖此值）。"""
    from jituan.constants import CLUB_ID_DEFAULT
    from jituan.services.club_user import get_user_club_id

    cid = (club_id or '').strip()
    if cid:
        return cid
    try:
        from users.business_models import User
        user = User.query.filter(UserUID=yonghu_id).first()
        return get_user_club_id(user) or CLUB_ID_DEFAULT
    except Exception:
        return CLUB_ID_DEFAULT


def _record_club_id(record, club_id=None):
    candidates = _membership_club_candidates(
        getattr(record, 'yonghu_id', ''),
        club_id=club_id,
        record_club_id=getattr(record, 'club_id', None),
    )
    if candidates:
        return candidates[0]
    from jituan.constants import CLUB_ID_DEFAULT
    return CLUB_ID_DEFAULT


def is_bundle_huiyuan(huiyuan_id):
    hid = normalize_huiyuan_id(huiyuan_id)
    if not hid:
        return False
    for bid in Huiyuan.query.filter(is_bundle=True).values_list('huiyuan_id', flat=True):
        if huiyuan_ids_match(bid, hid):
            return True
    return False


def _membership_record_active(record):
    """会员记录是否未过期（体验=正式；时区安全，不依赖 jiance 副作用）。"""
    if not record or int(record.huiyuan_zhuangtai or 0) != 1:
        return False
    if not record.daoqi_time:
        return False
    now = _normalize_datetime_for_compare(timezone.now())
    daoqi = _normalize_datetime_for_compare(record.daoqi_time)
    return daoqi > now


def user_has_huiyuan_access(yonghu_id, required_huiyuan_id, club_id=None):
    """
    抢单 /qiangdan 校验：有无对应会员且未过期（体验会员可抢对应类型单）。
    总会员按包含子会员判断。提现不走此函数。
    """
    req = normalize_huiyuan_id(required_huiyuan_id)
    if not req:
        return True

    def _record_valid(rec):
        return _membership_record_active(rec)

    req_set = {req, req.lstrip('0') or '0'}
    for rec in Huiyuangoumai.query.filter(yonghu_id=yonghu_id):
        if not _record_valid(rec):
            continue
        if huiyuan_ids_match(rec.huiyuan_id, req):
            return True
        if is_bundle_huiyuan(rec.huiyuan_id):
            included = resolve_bundle_included_ids(
                rec.huiyuan_id,
                club_id=club_id,
                yonghu_id=yonghu_id,
                record_club_id=rec.club_id,
            )
            if req_set & _included_huiyuan_id_set(included):
                return True

    if _czjilu_grants_huiyuan_access(yonghu_id, req, club_id):
        return True

    from jituan.services.gift_huiyuan import user_has_gift_huiyuan_access
    if user_has_gift_huiyuan_access(yonghu_id, req, club_id=club_id):
        return True
    return False


def _czjilu_grants_huiyuan_access(yonghu_id, required_huiyuan_id, club_id=None):
    """已支付会员充值单（含体验）在有效期内，视同已开通；总会员含子类型。"""
    from jituan.constants import CLUB_ID_DEFAULT
    from jituan.services.member_recharge import (
        MEMBER_LEIXING,
        MEMBER_PAID_STATUS,
        _czjilu_entitlement_revoked,
        czjilu_entitlement_active,
    )
    from products.models import Czjilu

    req = normalize_huiyuan_id(required_huiyuan_id)
    if not req:
        return False
    req_set = {req, req.lstrip('0') or '0'}
    effective_club = _resolve_user_club_id(yonghu_id, club_id)
    for order in Czjilu.query.filter(
        yonghuid=yonghu_id,
        leixing=MEMBER_LEIXING,
        zhuangtai=MEMBER_PAID_STATUS,
    ).order_by('-CreateTime')[:30]:
        if _czjilu_entitlement_revoked(order):
            continue
        cid = getattr(order, 'club_id', None) or club_id or effective_club or CLUB_ID_DEFAULT
        if not czjilu_entitlement_active(order, cid):
            continue
        hid = normalize_huiyuan_id(order.huiyuan_id)
        if not hid:
            continue
        if huiyuan_ids_match(hid, req):
            return True
        if is_bundle_huiyuan(hid):
            included = resolve_bundle_included_ids(
                hid,
                club_id=club_id,
                yonghu_id=yonghu_id,
                record_club_id=getattr(order, 'club_id', None),
            )
            if req_set & _included_huiyuan_id_set(included):
                return True
    return False


def user_has_huiyuan_access_any(yonghu_id, required_ids, club_id=None):
    for rid in required_ids or []:
        if user_has_huiyuan_access(yonghu_id, rid, club_id=club_id):
            return True
    return False


def resolve_order_membership_ids(order_dict):
    """订单可能关联的会员 ID（订单字段 + 商品类型，兼容老数据）。"""
    ids = []
    mid = normalize_huiyuan_id(
        order_dict.get('MembershipID') or order_dict.get('huiyuan_id')
    )
    if mid:
        ids.append(mid)
    grab_req = int(order_dict.get('GrabRequirement') or order_dict.get('yaoqiuleixing') or 0)
    if grab_req != 1:
        return ids
    leixing_id = order_dict.get('ProductTypeID') or order_dict.get('leixing_id')
    if leixing_id:
        try:
            from products.models import ShangpinLeixing
            row = ShangpinLeixing.query.filter(id=leixing_id).values('huiyuan_id').first()
            if row:
                lx_hid = normalize_huiyuan_id(row.get('huiyuan_id'))
                if lx_hid and not any(huiyuan_ids_match(lx_hid, x) for x in ids):
                    ids.append(lx_hid)
        except Exception:
            pass
    return ids


def resolve_order_membership_id(order_dict):
    ids = resolve_order_membership_ids(order_dict)
    return ids[0] if ids else ''


def pool_display_has_valid_member(yonghu_id, required_huiyuan_id, club_id=None):
    """
    【仅 dddhq 抢单池展示】有无对应会员且未过期。
    体验会员与正式会员完全同等；不读 is_trial，不拦截体验。
    总会员按包含子会员判断。
    """
    return user_has_huiyuan_access(yonghu_id, required_huiyuan_id, club_id=club_id)


def pool_display_has_valid_member_for_order(order_dict, yonghu_id, club_id=None):
    """抢单池：本单所需会员类型是否有效（只看类型+过期，体验=正式）。"""
    grab_req = int(order_dict.get('GrabRequirement') or order_dict.get('yaoqiuleixing') or 0)
    if grab_req != 1:
        return True
    required_ids = resolve_order_membership_ids(order_dict)
    if not required_ids:
        return False
    for rid in required_ids:
        if pool_display_has_valid_member(yonghu_id, rid, club_id=club_id):
            return True
    return False


def resolve_grab_pool_member_display(order_dict, yonghu_id, club_id=None, user_clumber=None):
    """
    dddhq：原样返回抢单要求类型 1/2 + 对应 huiyuan_id（不改写成 0）。
    体验会员与正式会员同一套 clumber；老端用 clumber.huiyuanid 对订单 huiyuan_id。
    """
    grab_req = int(order_dict.get('GrabRequirement') or order_dict.get('yaoqiuleixing') or 0)
    membership_id = resolve_order_membership_id(order_dict)
    if grab_req != 1:
        return grab_req, membership_id or '', True

    has_valid = pool_display_has_valid_member_for_order(order_dict, yonghu_id, club_id=club_id)
    return 1, membership_id or '', has_valid


def enrich_clumber_item(record, club_id=None):
    """老端 clumber：体验会员与正式会员返回格式完全一致（不暴露 is_trial）。"""
    daoqi_str = format_legacy_daoqi(record.daoqi_time)
    item = {
        'huiyuanid': record.huiyuan_id,
        'huiyuan_id': record.huiyuan_id,
        'huiyuanming': record.jieshao or f'会员{record.huiyuan_id}',
        'daoqi': daoqi_str,
        'huiyuan_zhuangtai': 1,
        'shifou_daoqi': False,
        'display_owned': True,
    }
    if is_bundle_huiyuan(record.huiyuan_id):
        item['is_bundle'] = True
        item['bundle_huiyuan_id'] = record.huiyuan_id
        item['included_huiyuan_ids'] = resolve_bundle_included_ids(
            record.huiyuan_id,
            club_id=club_id,
            yonghu_id=record.yonghu_id,
            record_club_id=record.club_id,
        )
    return item


def _append_clumber_id_variants(items, seen, base_item, huiyuan_id):
    """为老端 == 比较补齐多种会员 ID 写法。仅首条供「我的会员」展示。"""
    hide_owned = (
        base_item.get('display_owned') is False
        or bool(base_item.get('from_bundle'))
        or bool(base_item.get('id_variant'))
    )
    first = True
    for vid in expand_huiyuan_id_variants(huiyuan_id):
        if vid in seen:
            continue
        seen.add(vid)
        row = {
            **base_item,
            'huiyuanid': vid,
            'huiyuan_id': vid,
        }
        if hide_owned or not first:
            row['display_owned'] = False
            if not first:
                row['id_variant'] = True
        else:
            row['display_owned'] = True
        first = False
        items.append(row)


def _append_bundle_sub_clumber(items, seen, base_item):
    """总会员有效时，为抢单页展开子会员虚拟条目（体验=正式）。"""
    if not base_item.get('is_bundle'):
        return
    bundle_hid = str(
        base_item.get('bundle_huiyuan_id')
        or base_item.get('huiyuanid')
        or base_item.get('huiyuan_id')
        or '',
    )
    for sub_id in base_item.get('included_huiyuan_ids') or []:
        sid = str(sub_id).strip()
        if not sid:
            continue
        # 子项用子会员自己的名称；不进「我的会员」主列表（仅抢单匹配）
        child_name = ''
        try:
            hy = Huiyuan.query.filter(huiyuan_id=sid).first()
            if not hy:
                for vid in expand_huiyuan_id_variants(sid):
                    hy = Huiyuan.query.filter(huiyuan_id=vid).first()
                    if hy:
                        break
            child_name = ((hy.jieshao if hy else '') or '').strip()
        except Exception:
            child_name = ''
        sub_base = {
            'huiyuanming': child_name or sid,
            'daoqi': base_item.get('daoqi', ''),
            'from_bundle': bundle_hid,
            'display_owned': False,
            'huiyuan_zhuangtai': 1,
            'shifou_daoqi': False,
        }
        _append_clumber_id_variants(items, seen, sub_base, sid)


def _build_user_clumber_core(yonghu_id, club_id=None):
    """huiyuangoumai 未过期会员 → clumber（体验与正式完全同等，不读 is_trial）。"""
    items = []
    seen = set()
    effective_club = _resolve_user_club_id(yonghu_id, club_id)
    for record in Huiyuangoumai.query.filter(yonghu_id=yonghu_id):
        if not _membership_record_active(record):
            continue
        base = enrich_clumber_item(record, club_id=effective_club)
        _append_clumber_id_variants(items, seen, base, record.huiyuan_id)
        _append_bundle_sub_clumber(items, seen, base)
    return items


def _merge_clumber_extra_items(items, extra):
    """只读合并：有效期内充值单合成的会员条目补进 clumber（体验=正式）。"""
    if not extra:
        return items
    seen_cores = set()
    for it in items:
        hid = normalize_huiyuan_id(it.get('huiyuanid') or it.get('huiyuan_id'))
        if hid:
            seen_cores.add(hid.lstrip('0') or '0')
    for it in extra:
        hid = normalize_huiyuan_id(it.get('huiyuanid') or it.get('huiyuan_id'))
        if not hid:
            continue
        core = hid.lstrip('0') or '0'
        if core in seen_cores:
            continue
        items.append(it)
        seen_cores.add(core)
    return items


def _build_clumber_from_paid_czjilu(yonghu_id, club_id=None):
    """只读：有效期内充值单合成 clumber（体验=正式，不写库）；总会员展开子类型。"""
    from jituan.constants import CLUB_ID_DEFAULT
    from jituan.services.member_recharge import (
        MEMBER_LEIXING,
        MEMBER_PAID_STATUS,
        czjilu_entitlement_active,
        _czjilu_entitlement_end,
    )
    from products.models import Czjilu, Huiyuan

    items = []
    seen = set()
    effective_club = _resolve_user_club_id(yonghu_id, club_id)
    for order in Czjilu.query.filter(
        yonghuid=yonghu_id,
        leixing=MEMBER_LEIXING,
        zhuangtai=MEMBER_PAID_STATUS,
    ).order_by('-CreateTime')[:30]:
        hid = normalize_huiyuan_id(order.huiyuan_id)
        if not hid:
            continue
        cid = getattr(order, 'club_id', None) or effective_club or CLUB_ID_DEFAULT
        if not czjilu_entitlement_active(order, cid):
            continue
        try:
            hy = Huiyuan.query.get(huiyuan_id=hid)
            ming = hy.jieshao or f'会员{hid}'
        except Huiyuan.DoesNotExist:
            ming = f'会员{hid}'
        daoqi = _czjilu_entitlement_end(order, cid)
        base = {
            'huiyuanming': ming,
            'daoqi': format_legacy_daoqi(daoqi),
            'huiyuan_zhuangtai': 1,
            'shifou_daoqi': False,
            'display_owned': True,
        }
        if is_bundle_huiyuan(hid):
            base['is_bundle'] = True
            base['bundle_huiyuan_id'] = hid
            base['included_huiyuan_ids'] = resolve_bundle_included_ids(
                hid,
                club_id=club_id,
                yonghu_id=yonghu_id,
                record_club_id=getattr(order, 'club_id', None),
            )
        _append_clumber_id_variants(items, seen, base, hid)
        _append_bundle_sub_clumber(items, seen, base)
    return items


def _finalize_clumber_for_legacy_client(items):
    """老端只认 huiyuanid；去掉 is_trial / is_gift 等内部字段。"""
    cleaned = []
    for it in items or []:
        row = dict(it)
        row.pop('is_trial', None)
        row.pop('is_gift', None)
        cleaned.append(row)
    return cleaned


def ensure_grab_pool_clumber(yonghu_id, club_id=None):
    """
    dddhq / dashouxinxi：只读 huiyuangoumai + 有效期内充值单 + 赠送会员 生成 clumber。
    总会员展开子会员虚拟条目供老端 clumber 比对；不写库、不补开通。
    """
    from jituan.services.gift_huiyuan import build_gift_clumber_items

    effective_club = _resolve_user_club_id(yonghu_id, club_id)
    items = _build_user_clumber_core(yonghu_id, club_id=effective_club)
    items = _merge_clumber_extra_items(
        items,
        _build_clumber_from_paid_czjilu(yonghu_id, club_id=effective_club),
    )
    items = _merge_clumber_extra_items(
        items,
        build_gift_clumber_items(yonghu_id, club_id=effective_club),
    )
    items = _finalize_clumber_for_legacy_client(items)
    return bool(items), items


def refresh_grab_pool_membership_state(yonghu_id, club_id=None):
    """
    抢单池/打手资料：只读生成 clumber（含总会员子类型展开）。
    供 /dingdan/ddhq、/yonghu/dashouxinxi、登录 clumber 调用；禁止写库补会员。
    """
    return ensure_grab_pool_clumber(yonghu_id, club_id=club_id)


def build_user_clumber(yonghu_id, club_id=None):
    """
    老小程序抢单页 clumber（体验=正式格式）。
    仅展示用；只含未过期会员。
    """
    _, items = ensure_grab_pool_clumber(yonghu_id, club_id=club_id)
    return items


def build_auth_clumber(yonghu_id, club_id=None):
    """登录 clumber：与 dddhq / dashouxinxi 同源，每次刷新会员状态。"""
    return refresh_grab_pool_membership_state(yonghu_id, club_id=club_id)[1]


def save_bundle_includes(club_id, bundle_huiyuan_id, included_ids):
    """保存俱乐部总会员包含的子会员（须为本俱乐部已上架的普通会员）。"""
    from jituan.models import ClubHuiyuanBundleInclude, ClubHuiyuanPrice

    if not is_bundle_huiyuan(bundle_huiyuan_id):
        return None, '该会员不是总会员类型'

    enabled = set(
        ClubHuiyuanPrice.query.filter(club_id=club_id, is_enabled=True).values_list(
            'huiyuan_id', flat=True,
        )
    )
    bundle_ids = set(
        Huiyuan.query.filter(is_bundle=True).values_list('huiyuan_id', flat=True)
    )
    cleaned = []
    for hid in included_ids or []:
        hid = str(hid).strip()
        if not hid or hid == bundle_huiyuan_id:
            continue
        if hid not in enabled:
            return None, f'子会员 {hid} 未在本俱乐部上架'
        if hid in bundle_ids:
            return None, '总会员不能包含另一个总会员'
        if not Huiyuan.query.filter(huiyuan_id=hid).exists():
            return None, f'子会员 {hid} 不存在'
        cleaned.append(hid)

    if not cleaned:
        return None, '请至少选择一个包含的子会员'

    ClubHuiyuanBundleInclude.query.filter(
        club_id=club_id, bundle_huiyuan_id=bundle_huiyuan_id,
    ).delete()
    for hid in cleaned:
        ClubHuiyuanBundleInclude.query.create(
            club_id=club_id,
            bundle_huiyuan_id=bundle_huiyuan_id,
            included_huiyuan_id=hid,
        )
    return {'included_huiyuan_ids': cleaned}, None
