"""
支付网关工厂 (Payment Gateway Factory)
基于 www.lytiao.com 项目提取的支付逻辑，适配 Python FastAPI

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

import os
import time
import hashlib
import logging
from abc import ABC, abstractmethod
from typing import Dict, Any, Optional, Tuple
from dataclasses import dataclass
from enum import Enum

logger = logging.getLogger('payment')


class PaymentPlatform(Enum):
    """支付平台枚举"""
    ALIPAY = 'alipay'
    WECHAT = 'wechat'
    PAYPAL = 'paypal'
    STRIPE = 'stripe'
    USDT = 'usdt'
    COIN = 'coin'


class TradeType(Enum):
    """交易类型"""
    PURCHASE = 'purchase'  # 购买
    RECHARGE = 'recharge'  # 充值


class TradeStatus(Enum):
    """交易状态"""
    PAYING = 'paying'
    PAID = 'paid'
    CLOSED = 'closed'
    REFUNDED = 'refunded'


@dataclass
class CreateTradeData:
    """创建交易请求数据"""
    goods_title: str
    goods_detail: str
    trade_sn: str
    order_sn: str
    amount: int  # 金额，单位：分
    notify_url: str
    return_url: str = ''
    platform_type: str = 'web'  # web/wap/jsapi/native/h5/app
    create_ip: str = ''
    open_id: str = ''  # 微信JSAPI支付需要
    attach: Dict[str, Any] = None


@dataclass
class TradeResult:
    """交易结果"""
    type: str  # url/qrcode/json/address/direct
    payload: Any  # 支付数据
    trade_sn: str
    expiration: int = 1800  # 过期时间（秒）


@dataclass
class NotifyResult:
    """回调解析结果"""
    status: str
    trade_sn: str
    platform_sn: str
    pay_amount: int  # 分
    pay_time: int  # 时间戳
    currency: str = 'CNY'
    attach: Dict[str, Any] = None
    raw_data: Dict[str, Any] = None


class PaymentException(Exception):
    """支付异常基类"""
    pass


class SignatureError(PaymentException):
    """签名验证失败"""
    pass


class AmountMismatchError(PaymentException):
    """金额不匹配"""
    pass


class GatewayNotFoundError(PaymentException):
    """支付网关不存在"""
    pass


class AbstractGateway(ABC):
    """
    支付网关抽象基类
    所有支付网关必须实现此接口
    """
    
    def __init__(self, config: Dict[str, Any] = None):
        self.config = config or {}
    
    @abstractmethod
    def create_trade(self, data: CreateTradeData) -> TradeResult:
        """
        创建交易
        
        Args:
            data: 交易数据
            
        Returns:
            TradeResult: 包含支付参数的结果
        """
        pass
    
    @abstractmethod
    def verify_sign(self, data: Dict[str, Any]) -> bool:
        """
        验证签名
        
        Args:
            data: 回调原始数据
            
        Returns:
            bool: 签名是否有效
        """
        pass
    
    @abstractmethod
    def parse_notify(self, data: Any) -> NotifyResult:
        """
        解析回调数据
        
        Args:
            data: 回调原始数据 (可能是dict或xml字符串)
            
        Returns:
            NotifyResult: 解析后的结果
        """
        pass
    
    @abstractmethod
    def close_trade(self, trade_sn: str) -> bool:
        """
        关闭交易
        
        Args:
            trade_sn: 交易流水号
            
        Returns:
            bool: 是否关闭成功
        """
        pass
    
    @abstractmethod
    def query_trade(self, trade_sn: str) -> Optional[NotifyResult]:
        """
        查询交易状态
        
        Args:
            trade_sn: 交易流水号
            
        Returns:
            NotifyResult: 交易状态，未支付返回None
        """
        pass
    
    @abstractmethod
    def refund(self, trade_sn: str, refund_sn: str, amount: int, reason: str = '') -> bool:
        """
        发起退款
        
        Args:
            trade_sn: 原交易流水号
            refund_sn: 退款单号
            amount: 退款金额（分）
            reason: 退款原因
            
        Returns:
            bool: 是否成功
        """
        pass
    
    def success_response(self) -> str:
        """回调成功响应"""
        return 'success'
    
    def fail_response(self) -> str:
        """回调失败响应"""
        return 'fail'


class PaymentFactory:
    """
    支付网关工厂
    
    使用示例:
        # 注册网关
        PaymentFactory.register('alipay', AlipayGateway)
        PaymentFactory.register('wechat', WechatGateway)
        
        # 创建网关实例
        gateway = PaymentFactory.create('wechat_jsapi')
        result = gateway.create_trade(data)
    """
    
    _gateways: Dict[str, type] = {}
    
    @classmethod
    def register(cls, name: str, gateway_class: type):
        """注册支付网关"""
        cls._gateways[name] = gateway_class
        logger.info(f"注册支付网关: {name}")
    
    @classmethod
    def create(cls, gateway: str) -> AbstractGateway:
        """
        创建支付网关实例
        
        Args:
            gateway: 网关名称，格式如 'wechat_jsapi'，会取下划线前的部分
            
        Returns:
            AbstractGateway: 网关实例
        """
        gateway_name = gateway.split('_')[0]
        
        if gateway_name not in cls._gateways:
            raise GatewayNotFoundError(f"不支持的支付网关: {gateway}")
        
        gateway_class = cls._gateways[gateway_name]
        config = cls._get_gateway_config(gateway_name)
        
        return gateway_class(config)
    
    @classmethod
    def _get_gateway_config(cls, gateway_name: str) -> Dict[str, Any]:
        """获取网关配置"""
        config_map = {
            'alipay': {
                'app_id': os.getenv('ALIPAY_APP_ID', ''),
                'pid': os.getenv('ALIPAY_PID', ''),
                'seller_email': os.getenv('ALIPAY_SELLER_EMAIL', ''),
                'private_key': os.getenv('ALIPAY_PRIVATE_KEY', ''),
                'public_key': os.getenv('ALIPAY_PUBLIC_KEY', ''),
                'md5_key': os.getenv('ALIPAY_MD5_KEY', ''),
            },
            'wechat': {
                'appid': os.getenv('WECHAT_APPID', ''),
                'app_secret': os.getenv('WECHAT_APP_SECRET', ''),
                'mch_id': os.getenv('WECHAT_MCH_ID', ''),
                'mch_key': os.getenv('WECHAT_MCH_KEY', ''),
                'cert_path': os.getenv('WECHAT_CERT_PATH', ''),
                'key_path': os.getenv('WECHAT_KEY_PATH', ''),
            },
            'paypal': {
                'client_id': os.getenv('PAYPAL_CLIENT_ID', ''),
                'client_secret': os.getenv('PAYPAL_CLIENT_SECRET', ''),
                'mode': os.getenv('PAYPAL_MODE', 'sandbox'),
            },
            'stripe': {
                'public_key': os.getenv('STRIPE_PUBLIC_KEY', ''),
                'secret_key': os.getenv('STRIPE_SECRET_KEY', ''),
                'webhook_secret': os.getenv('STRIPE_WEBHOOK_SECRET', ''),
            },
        }
        
        return config_map.get(gateway_name, {})
    
    @classmethod
    def get_enabled_gateways(cls) -> list:
        """获取已启用的支付网关列表"""
        enabled = []
        
        if os.getenv('ALIPAY_ENABLED', 'false').lower() == 'true':
            enabled.append({
                'gateway': 'alipay',
                'name': '支付宝',
                'icon': '/icons/alipay.png',
                'types': ['web', 'wap', 'qr']
            })
        
        if os.getenv('WECHAT_ENABLED', 'false').lower() == 'true':
            enabled.append({
                'gateway': 'wechat',
                'name': '微信支付',
                'icon': '/icons/wechat.png',
                'types': ['native', 'jsapi', 'h5', 'app']
            })
        
        if os.getenv('PAYPAL_ENABLED', 'false').lower() == 'true':
            enabled.append({
                'gateway': 'paypal',
                'name': 'PayPal',
                'icon': '/icons/paypal.png',
                'types': ['redirect']
            })
        
        if os.getenv('STRIPE_ENABLED', 'false').lower() == 'true':
            enabled.append({
                'gateway': 'stripe',
                'name': 'Stripe',
                'icon': '/icons/stripe.png',
                'types': ['redirect']
            })
        
        return enabled


def generate_trade_sn(prefix: str = 'T') -> str:
    """
    生成交易流水号
    
    格式: 前缀 + 年月日时分秒 + 5位随机数
    示例: T2024011710053012345
    """
    import random
    timestamp = time.strftime('%Y%m%d%H%M%S')
    random_num = random.randint(10000, 99999)
    return f"{prefix}{timestamp}{random_num}"


def yuan_to_fen(yuan: float) -> int:
    """元转分"""
    return int(round(yuan * 100))


def fen_to_yuan(fen: int) -> float:
    """分转元"""
    return round(fen / 100, 2)
