import logging
import re
import time
import hashlib
import base64
import json
import os
from urllib.parse import urlparse

from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.backends import default_backend
from Crypto.Cipher import AES

from django.conf import settings

logger = logging.getLogger(__name__)


def _get_v3_config(club_id=None):
    from jituan.services.wechat_pay import get_wechat_v3_config
    return get_wechat_v3_config(club_id)


def load_private_key(club_id=None):
    """加载私钥（支持 PKCS#1 和 PKCS#8）。"""
    cfg = _get_v3_config(club_id)
    key_path = cfg['PRIVATE_KEY_PATH']
    with open(key_path, 'rb') as f:
        key_data = f.read()

    return serialization.load_pem_private_key(
        key_data,
        password=None,
        backend=default_backend(),
    )


def build_authorization(method, url, body, club_id=None):
    """生成 V3 接口的 Authorization 头。"""
    cfg = _get_v3_config(club_id)
    private_key = load_private_key(club_id)
    mchid = cfg['MCHID']
    serial_no = (cfg.get('CERT_SERIAL_NO') or '').strip().upper()
    logger.warning(
        '[WX_V3_SIGN] club=%s mchid=%s serial=%s key_path=%s',
        club_id, mchid, serial_no, cfg.get('PRIVATE_KEY_PATH'),
    )

    parsed = urlparse(url)
    path = parsed.path
    if parsed.query:
        path += '?' + parsed.query

    timestamp = str(int(time.time()))
    nonce = hashlib.md5((timestamp + 'wechat').encode()).hexdigest()[:32]

    message = '\n'.join([method, path, timestamp, nonce, body]) + '\n'

    signature = private_key.sign(
        message.encode('utf-8'),
        padding.PKCS1v15(),
        hashes.SHA256(),
    )
    signature_b64 = base64.b64encode(signature).decode('utf-8')

    return (
        f'WECHATPAY2-SHA256-RSA2048 mchid="{mchid}",nonce_str="{nonce}",'
        f'timestamp="{timestamp}",serial_no="{serial_no}",signature="{signature_b64}"'
    )


def verify_wechat_sign(headers, body, club_id=None):
    """验证微信回调签名（需提前下载平台证书）。"""
    import rsa

    serial = headers.get('Wechatpay-Serial')
    signature = headers.get('Wechatpay-Signature')
    timestamp = headers.get('Wechatpay-Timestamp')
    nonce = headers.get('Wechatpay-Nonce')

    if not all([serial, signature, timestamp, nonce]):
        return False

    if not re.match(r'^[A-Za-z0-9-]+$', serial):
        return False

    try:
        ts = int(headers.get('Wechatpay-Timestamp', '0'))
        if abs(time.time() - ts) > 300:
            return False
    except (ValueError, TypeError):
        return False

    cfg = _get_v3_config(club_id)
    message = '\n'.join([timestamp, nonce, body]) + '\n'
    cert_path = f"{cfg['PLATFORM_CERT_DIR']}/{serial}.pem"
    if not os.path.exists(cert_path):
        return False

    with open(cert_path, 'rb') as f:
        pub_key = rsa.PublicKey.load_pkcs1_openssl_pem(f.read())

    signature_bytes = base64.b64decode(signature)
    try:
        rsa.verify(message.encode('utf-8'), signature_bytes, pub_key)
        return True
    except rsa.VerificationError:
        return False


def resolve_callback_club_id(headers, body):
    """
    多商户回调：依次用各俱乐部平台证书验签，返回匹配的 club_id。
    验签失败返回 None。
    """
    from jituan.constants import CLUB_ID_DEFAULT
    from jituan.models import Club

    if verify_wechat_sign(headers, body, club_id=CLUB_ID_DEFAULT):
        return CLUB_ID_DEFAULT

    for club in Club.query.filter(status=1).exclude(club_id=CLUB_ID_DEFAULT):
        if verify_wechat_sign(headers, body, club_id=club.club_id):
            return club.club_id
    return None


def decrypt_callback_ciphertext(associated_data, nonce, ciphertext, club_id=None):
    """解密回调数据（使用 AES-GCM）。"""
    cfg = _get_v3_config(club_id)
    api_v3_key = cfg['API_V3_KEY'].encode('utf-8')
    nonce_bytes = nonce.encode('utf-8')
    ciphertext_bytes = base64.b64decode(ciphertext)

    cipher = AES.new(api_v3_key, AES.MODE_GCM, nonce=nonce_bytes)
    if associated_data:
        cipher.update(associated_data.encode('utf-8'))
    plaintext = cipher.decrypt_and_verify(ciphertext_bytes[:-16], ciphertext_bytes[-16:])
    return plaintext.decode('utf-8')
