"""俱乐部微信配置与 openid 解析。"""
import logging
import re

import requests
from django.conf import settings

from jituan.constants import CLUB_ID_DEFAULT
from jituan.models import Club

logger = logging.getLogger(__name__)

_REFERER_APPID_RE = re.compile(r'servicewechat\.com/(wx[0-9a-fA-F]+)/', re.I)


def get_club_miniapp_appid(club):
    """小程序 AppID：登录 jscode2session 与 JSAPI 支付须一致。"""
    if not club:
        return ''
    return (club.wx_appid or club.pay_app_id or '').strip()


def resolve_club_by_appid(app_id):
    if not app_id:
        return None
    app_id = app_id.strip()
    try:
        club = Club.query.filter(wx_appid=app_id, status=1).first()
        if club:
            return club
        return Club.query.filter(pay_app_id=app_id, status=1).first()
    except Exception:
        club = Club.objects.filter(wx_appid=app_id, status=1).first()
        if club:
            return club
        return Club.objects.filter(pay_app_id=app_id, status=1).first()


def get_club_wx_credentials(club_id):
    """优先读 club 表；xq 缺省时回退 settings 全局配置（兼容现网）。"""
    club_id = club_id or CLUB_ID_DEFAULT
    club = None
    try:
        club = Club.query.get(club_id=club_id)
    except Club.DoesNotExist:
        club = None
    except Exception:
        logger.exception('读取俱乐部失败 club_id=%s', club_id)
        club = None

    appid = get_club_miniapp_appid(club)
    secret = (club.wx_secret or '').strip() if club else ''
    if appid and secret:
        return appid, secret, club

    if club_id == CLUB_ID_DEFAULT:
        return (
            getattr(settings, 'WEIXIN_APPID', ''),
            getattr(settings, 'WEIXIN_SECRET', ''),
            club,
        )
    # 非默认俱乐部未配置时给出明确原因，避免后续空指针 500
    if not club:
        return '', '', None
    return appid or '', secret or '', club


def _invite_code_from_request(request):
    data = getattr(request, 'data', None) or {}
    if not isinstance(data, dict):
        return ''
    return (data.get('inviteCode') or data.get('invite_code') or '').strip()


def resolve_club_from_invite_code(invite_code):
    """老端无 Header/Referer 时，从管事邀请码反查俱乐部。"""
    invite_code = (invite_code or '').strip()
    if not invite_code:
        return None
    try:
        from users.models import UserGuanshi
        from jituan.services.club_user import resolve_invite_party_club

        guanshi = UserGuanshi.query.select_related('user').get(yaoqingma=invite_code)
        user = guanshi.user
        club = resolve_invite_party_club(user, None)
        if club:
            return club
        explicit = (getattr(user, 'ClubID', None) or '').strip()
        return explicit or None
    except Exception:
        return None


def resolve_club_id_for_wechat_request(request):
    """
    小程序请求解析 club_id（登录/扫码注册）。
    优先级：X-Club-Id > body.appId > Referer > body.inviteCode 反查管事 > JWT 用户归属 > 默认 xq。
    老端 fighter.js 的 wdlyhdl 未带头时，靠 Referer 或邀请码识别星之界。
    """
    from jituan.services.club_context import resolve_club_id_from_request

    header = (request.META.get('HTTP_X_CLUB_ID') or '').strip()
    if not header and hasattr(request, 'headers'):
        header = (request.headers.get('X-Club-Id') or '').strip()
    if header:
        return header

    app_id = ''
    data = getattr(request, 'data', None) or {}
    if isinstance(data, dict):
        app_id = (data.get('appId') or data.get('app_id') or '').strip()

    if not app_id:
        referer = request.META.get('HTTP_REFERER', '') or ''
        matched = _REFERER_APPID_RE.search(referer)
        if matched:
            app_id = matched.group(1)

    if app_id:
        club = resolve_club_by_appid(app_id)
        if club:
            return club.club_id

    invite_club = resolve_club_from_invite_code(_invite_code_from_request(request))
    if invite_club:
        return invite_club

    # 已登录走 dashouzhuce 时往往无 Referer，用 JWT 用户归属俱乐部
    user = getattr(request, 'user', None)
    if user is not None and getattr(user, 'is_authenticated', False):
        from jituan.services.club_user import get_user_club_id
        user_club = get_user_club_id(user)
        if user_club:
            return user_club

    return resolve_club_id_from_request(request)


def wechat_jscode2session_for_request(request, code):
    """按请求上下文换取 openid（多俱乐部小程序）。"""
    club_id = resolve_club_id_for_wechat_request(request)
    return jscode2session(code, club_id=club_id)


def get_club_official_credentials(club_id):
    """服务号 AppID/Secret（H5 网页授权 + 公众号 JSAPI）。"""
    club_id = club_id or CLUB_ID_DEFAULT
    club = None
    try:
        club = Club.query.get(club_id=club_id)
    except Club.DoesNotExist:
        club = None
    except Exception:
        logger.exception('读取俱乐部失败 club_id=%s', club_id)
        club = None
    if not club:
        return '', '', None
    appid = (club.official_appid or '').strip()
    secret = (club.official_secret or '').strip()
    return appid, secret, club


def oauth2_access_token(code, club_id=None):
    """
    服务号网页授权：code → openid（sns/oauth2/access_token）。
    """
    appid, secret, club = get_club_official_credentials(club_id)
    if not appid or not secret:
        return {'errmsg': '俱乐部服务号配置未设置（official_appid/official_secret）'}

    url = 'https://api.weixin.qq.com/sns/oauth2/access_token'
    params = {
        'appid': appid,
        'secret': secret,
        'code': code,
        'grant_type': 'authorization_code',
    }
    try:
        response = requests.get(url, params=params, timeout=10)
        result = response.json()
        if 'openid' in result:
            return {
                'openid': result['openid'],
                'access_token': result.get('access_token', ''),
                'refresh_token': result.get('refresh_token', ''),
                'scope': result.get('scope', ''),
                'unionid': result.get('unionid', ''),
                'club_id': (club.club_id if club else None) or club_id or CLUB_ID_DEFAULT,
                'wx_appid': appid,
            }
        errcode = result.get('errcode', 'unknown')
        errmsg = result.get('errmsg', '未知错误')
        return {'errmsg': f'[{errcode}]{errmsg}'}
    except requests.exceptions.Timeout:
        return {'errmsg': '请求微信接口超时'}
    except Exception as e:
        logger.exception('oauth2_access_token 异常 club_id=%s', club_id)
        return {'errmsg': f'请求微信接口异常: {str(e)}'}


def jscode2session(code, club_id=None, app_id=None):
    """
    按俱乐部微信配置换取 openid。
    club_id 与 app_id 二选一；app_id 用于从小程序 appId 反查 club。
    """
    club = None
    if app_id:
        club = resolve_club_by_appid(app_id)
        club_id = club.club_id if club else club_id
    appid, secret, club = get_club_wx_credentials(club_id)
    if not appid or not secret:
        return {'errmsg': '俱乐部微信配置未设置'}

    url = 'https://api.weixin.qq.com/sns/jscode2session'
    params = {
        'appid': appid,
        'secret': secret,
        'js_code': code,
        'grant_type': 'authorization_code',
    }
    try:
        response = requests.get(url, params=params, timeout=10)
        result = response.json()
        if 'openid' in result:
            return {
                'openid': result['openid'],
                'session_key': result.get('session_key', ''),
                'unionid': result.get('unionid', ''),
                'club_id': club_id or CLUB_ID_DEFAULT,
                'wx_appid': appid,
            }
        errcode = result.get('errcode', 'unknown')
        errmsg = result.get('errmsg', '未知错误')
        return {'errmsg': f'[{errcode}]{errmsg}'}
    except requests.exceptions.Timeout:
        return {'errmsg': '请求微信接口超时'}
    except Exception as e:
        logger.exception('jscode2session 异常 club_id=%s', club_id)
        return {'errmsg': f'请求微信接口异常: {str(e)}'}
