"""
支付宝网关实现 (Alipay Gateway)
基于 www.lytiao.com 项目提取

作者: 卡若
版本: v4.0
"""

import json
import base64
import hashlib
import logging
from typing import Dict, Any, Optional
from urllib.parse import urlencode, parse_qs

try:
    from Crypto.PublicKey import RSA
    from Crypto.Signature import PKCS1_v1_5
    from Crypto.Hash import SHA256
except ImportError:
    print("请安装 pycryptodome: pip install pycryptodome")

from payment_factory import (
    AbstractGateway, CreateTradeData, TradeResult, NotifyResult,
    SignatureError, logger
)


class AlipayGateway(AbstractGateway):
    """
    支付宝网关
    
    支持:
    - 电脑网站支付 (platform_type='web')
    - 手机网站支付 (platform_type='wap')
    - 扫码支付 (platform_type='qr')
    """
    
    GATEWAY_URL = 'https://openapi.alipay.com/gateway.do'
    SANDBOX_URL = 'https://openapi.alipaydev.com/gateway.do'
    
    def __init__(self, config: Dict[str, Any]):
        super().__init__(config)
        self.app_id = config.get('app_id', '')
        self.pid = config.get('pid', '')
        self.seller_email = config.get('seller_email', '')
        self.private_key = config.get('private_key', '')
        self.public_key = config.get('public_key', '')
        self.md5_key = config.get('md5_key', '')
        
    def create_trade(self, data: CreateTradeData) -> TradeResult:
        """创建支付宝交易"""
        platform_type = data.platform_type.capitalize()
        
        if platform_type == 'Web':
            return self._create_web_trade(data)
        elif platform_type == 'Wap':
            return self._create_wap_trade(data)
        elif platform_type == 'Qr':
            return self._create_qr_trade(data)
        else:
            raise ValueError(f"不支持的支付类型: {platform_type}")
    
    def _create_web_trade(self, data: CreateTradeData) -> TradeResult:
        """电脑网站支付"""
        biz_content = {
            'subject': data.goods_title[:256],
            'out_trade_no': data.trade_sn,
            'total_amount': str(data.amount / 100),  # 分转元
            'product_code': 'FAST_INSTANT_TRADE_PAY',
            'body': data.goods_detail[:128] if data.goods_detail else '',
            'passback_params': json.dumps(data.attach) if data.attach else '',
        }
        
        params = self._build_params('alipay.trade.page.pay', biz_content, data.return_url, data.notify_url)
        
        # 生成签名
        sign = self._generate_sign(params)
        params['sign'] = sign
        
        # 构建跳转URL
        pay_url = f"{self.GATEWAY_URL}?{urlencode(params)}"
        
        return TradeResult(
            type='url',
            payload=pay_url,
            trade_sn=data.trade_sn
        )
    
    def _create_wap_trade(self, data: CreateTradeData) -> TradeResult:
        """手机网站支付"""
        biz_content = {
            'subject': data.goods_title[:256],
            'out_trade_no': data.trade_sn,
            'total_amount': str(data.amount / 100),
            'product_code': 'QUICK_WAP_WAY',
            'body': data.goods_detail[:128] if data.goods_detail else '',
            'passback_params': json.dumps(data.attach) if data.attach else '',
        }
        
        params = self._build_params('alipay.trade.wap.pay', biz_content, data.return_url, data.notify_url)
        sign = self._generate_sign(params)
        params['sign'] = sign
        
        pay_url = f"{self.GATEWAY_URL}?{urlencode(params)}"
        
        return TradeResult(
            type='url',
            payload=pay_url,
            trade_sn=data.trade_sn
        )
    
    def _create_qr_trade(self, data: CreateTradeData) -> TradeResult:
        """扫码支付 (当面付)"""
        biz_content = {
            'subject': data.goods_title[:256],
            'out_trade_no': data.trade_sn,
            'total_amount': str(data.amount / 100),
            'body': data.goods_detail[:128] if data.goods_detail else '',
        }
        
        params = self._build_params('alipay.trade.precreate', biz_content, '', data.notify_url)
        sign = self._generate_sign(params)
        params['sign'] = sign
        
        # 调用接口获取二维码
        import requests
        response = requests.post(self.GATEWAY_URL, data=params)
        result = response.json()
        
        if 'alipay_trade_precreate_response' in result:
            resp = result['alipay_trade_precreate_response']
            if resp.get('code') == '10000':
                return TradeResult(
                    type='qrcode',
                    payload=resp['qr_code'],
                    trade_sn=data.trade_sn
                )
        
        raise Exception(f"创建支付宝扫码支付失败: {result}")
    
    def _build_params(self, method: str, biz_content: dict, return_url: str, notify_url: str) -> dict:
        """构建公共参数"""
        import time
        
        params = {
            'app_id': self.app_id,
            'method': method,
            'charset': 'utf-8',
            'sign_type': 'RSA2',
            'timestamp': time.strftime('%Y-%m-%d %H:%M:%S'),
            'version': '1.0',
            'biz_content': json.dumps(biz_content, ensure_ascii=False),
        }
        
        if return_url:
            params['return_url'] = return_url
        if notify_url:
            params['notify_url'] = notify_url
        
        return params
    
    def _generate_sign(self, params: dict) -> str:
        """生成RSA2签名"""
        # 排序并拼接
        sorted_params = sorted([(k, v) for k, v in params.items() if v])
        sign_str = '&'.join([f'{k}={v}' for k, v in sorted_params])
        
        # RSA2签名
        key = RSA.import_key(f"-----BEGIN RSA PRIVATE KEY-----\n{self.private_key}\n-----END RSA PRIVATE KEY-----")
        signer = PKCS1_v1_5.new(key)
        hash_obj = SHA256.new(sign_str.encode('utf-8'))
        signature = signer.sign(hash_obj)
        
        return base64.b64encode(signature).decode('utf-8')
    
    def verify_sign(self, data: Dict[str, Any]) -> bool:
        """验证支付宝签名"""
        sign = data.pop('sign', '')
        sign_type = data.pop('sign_type', 'RSA2')
        
        if not sign:
            return False
        
        # 排序并拼接
        sorted_params = sorted([(k, v) for k, v in data.items() if v and k != 'sign_type'])
        sign_str = '&'.join([f'{k}={v}' for k, v in sorted_params])
        
        try:
            key = RSA.import_key(f"-----BEGIN PUBLIC KEY-----\n{self.public_key}\n-----END PUBLIC KEY-----")
            verifier = PKCS1_v1_5.new(key)
            hash_obj = SHA256.new(sign_str.encode('utf-8'))
            verifier.verify(hash_obj, base64.b64decode(sign))
            return True
        except (ValueError, TypeError) as e:
            logger.error(f"支付宝签名验证失败: {e}")
            return False
    
    def parse_notify(self, data: Dict[str, Any]) -> NotifyResult:
        """解析支付宝回调"""
        trade_status = data.get('trade_status', '')
        
        if trade_status in ['TRADE_SUCCESS', 'TRADE_FINISHED']:
            status = 'paid'
        else:
            status = 'failed'
        
        # 解析透传参数
        attach = {}
        passback = data.get('passback_params', '')
        if passback:
            try:
                attach = json.loads(passback)
            except:
                pass
        
        # 解析支付时间
        import time
        gmt_payment = data.get('gmt_payment', '')
        if gmt_payment:
            pay_time = int(time.mktime(time.strptime(gmt_payment, '%Y-%m-%d %H:%M:%S')))
        else:
            pay_time = int(time.time())
        
        return NotifyResult(
            status=status,
            trade_sn=data.get('out_trade_no', ''),
            platform_sn=data.get('trade_no', ''),
            pay_amount=int(float(data.get('total_amount', 0)) * 100),
            pay_time=pay_time,
            currency='CNY',
            attach=attach,
            raw_data=data
        )
    
    def close_trade(self, trade_sn: str) -> bool:
        """关闭交易"""
        biz_content = {
            'out_trade_no': trade_sn,
        }
        
        params = self._build_params('alipay.trade.close', biz_content, '', '')
        sign = self._generate_sign(params)
        params['sign'] = sign
        
        import requests
        response = requests.post(self.GATEWAY_URL, data=params)
        result = response.json()
        
        resp = result.get('alipay_trade_close_response', {})
        return resp.get('code') == '10000'
    
    def query_trade(self, trade_sn: str) -> Optional[NotifyResult]:
        """查询交易"""
        biz_content = {
            'out_trade_no': trade_sn,
        }
        
        params = self._build_params('alipay.trade.query', biz_content, '', '')
        sign = self._generate_sign(params)
        params['sign'] = sign
        
        import requests
        response = requests.post(self.GATEWAY_URL, data=params)
        result = response.json()
        
        resp = result.get('alipay_trade_query_response', {})
        if resp.get('code') == '10000' and resp.get('trade_status') in ['TRADE_SUCCESS', 'TRADE_FINISHED']:
            return self.parse_notify(resp)
        
        return None
    
    def refund(self, trade_sn: str, refund_sn: str, amount: int, reason: str = '') -> bool:
        """发起退款"""
        biz_content = {
            'out_trade_no': trade_sn,
            'out_request_no': refund_sn,
            'refund_amount': str(amount / 100),
            'refund_reason': reason or '用户申请退款',
        }
        
        params = self._build_params('alipay.trade.refund', biz_content, '', '')
        sign = self._generate_sign(params)
        params['sign'] = sign
        
        import requests
        response = requests.post(self.GATEWAY_URL, data=params)
        result = response.json()
        
        resp = result.get('alipay_trade_refund_response', {})
        return resp.get('code') == '10000'
    
    def success_response(self) -> str:
        return 'success'
