"""
Payment Gateway Integration for Broker Billing System
Supports multiple payment providers: Stripe, PayPal, Crypto (BTC, ETH, USDT)
"""

import logging
import hashlib
import hmac
import json
import urllib.parse
from datetime import datetime, timedelta
from typing import Optional, Dict, Any, List
from decimal import Decimal
from flask import request, jsonify

logger = logging.getLogger(__name__)


class PaymentGatewayBase:
    """Base class for payment gateway implementations"""
    
    def __init__(self, config: Dict[str, Any]):
        self.config = config
        self.gateway_name = "base"
    
    def create_payment(self, amount: Decimal, currency: str, description: str,
                       metadata: Dict[str, Any] = None) -> Dict[str, Any]:
        """Create a payment intent/charge"""
        raise NotImplementedError
    
    def verify_payment(self, payment_id: str) -> Dict[str, Any]:
        """Verify payment status"""
        raise NotImplementedError
    
    def process_webhook(self, payload: bytes, headers: Dict[str, str]) -> Dict[str, Any]:
        """Process webhook notification from payment provider"""
        raise NotImplementedError
    
    def refund_payment(self, payment_id: str, amount: Decimal = None) -> Dict[str, Any]:
        """Refund a payment (full or partial)"""
        raise NotImplementedError


class StripeGateway(PaymentGatewayBase):
    """Stripe payment gateway implementation"""
    
    def __init__(self, config: Dict[str, Any]):
        super().__init__(config)
        self.gateway_name = "stripe"
        self.api_key = config.get('api_key')
        self.webhook_secret = config.get('webhook_secret')
        self.api_base = "https://api.stripe.com/v1"
    
    def _get_headers(self) -> Dict[str, str]:
        return {
            'Authorization': f'Bearer {self.api_key}',
            'Content-Type': 'application/x-www-form-urlencoded'
        }
    
    def create_payment(self, amount: Decimal, currency: str, description: str,
                       metadata: Dict[str, Any] = None) -> Dict[str, Any]:
        """Create a Stripe Payment Intent"""
        try:
            import requests
            
            # Stripe expects amount in cents
            amount_cents = int(amount * 100)
            
            data = {
                'amount': amount_cents,
                'currency': currency.lower(),
                'description': description,
                'automatic_payment_methods[enabled]': 'true'
            }
            
            if metadata:
                for key, value in metadata.items():
                    data[f'metadata[{key}]'] = str(value)
            
            response = requests.post(
                f"{self.api_base}/payment_intents",
                headers=self._get_headers(),
                data=urllib.parse.urlencode(data)
            )
            
            if response.status_code == 200:
                result = response.json()
                return {
                    'success': True,
                    'payment_id': result['id'],
                    'client_secret': result.get('client_secret'),
                    'status': result['status'],
                    'amount': amount,
                    'currency': currency
                }
            else:
                logger.error(f"Stripe payment creation failed: {response.text}")
                return {
                    'success': False,
                    'error': response.json().get('error', {}).get('message', 'Unknown error')
                }
                
        except Exception as e:
            logger.error(f"Stripe payment creation error: {e}")
            return {'success': False, 'error': str(e)}
    
    def verify_payment(self, payment_id: str) -> Dict[str, Any]:
        """Verify Stripe payment status"""
        try:
            import requests
            
            response = requests.get(
                f"{self.api_base}/payment_intents/{payment_id}",
                headers=self._get_headers()
            )
            
            if response.status_code == 200:
                result = response.json()
                return {
                    'success': True,
                    'payment_id': result['id'],
                    'status': result['status'],
                    'amount': Decimal(result['amount']) / 100,
                    'currency': result['currency'].upper(),
                    'created': datetime.fromtimestamp(result['created'])
                }
            else:
                return {'success': False, 'error': 'Payment not found'}
                
        except Exception as e:
            logger.error(f"Stripe payment verification error: {e}")
            return {'success': False, 'error': str(e)}
    
    def process_webhook(self, payload: bytes, headers: Dict[str, str]) -> Dict[str, Any]:
        """Process Stripe webhook"""
        try:
            # Verify webhook signature
            sig_header = headers.get('Stripe-Signature', '')
            
            if self.webhook_secret:
                if not self._verify_webhook_signature(payload, sig_header):
                    return {'success': False, 'error': 'Invalid signature'}
            
            event = json.loads(payload)
            event_type = event.get('type')
            
            result = {
                'success': True,
                'event_type': event_type,
                'event_id': event.get('id')
            }
            
            if event_type == 'payment_intent.succeeded':
                payment_intent = event['data']['object']
                result.update({
                    'payment_id': payment_intent['id'],
                    'status': 'completed',
                    'amount': Decimal(payment_intent['amount']) / 100,
                    'currency': payment_intent['currency'].upper(),
                    'metadata': payment_intent.get('metadata', {})
                })
            elif event_type == 'payment_intent.payment_failed':
                payment_intent = event['data']['object']
                result.update({
                    'payment_id': payment_intent['id'],
                    'status': 'failed',
                    'error': payment_intent.get('last_payment_error', {}).get('message', 'Payment failed')
                })
            
            return result
            
        except Exception as e:
            logger.error(f"Stripe webhook processing error: {e}")
            return {'success': False, 'error': str(e)}
    
    def _verify_webhook_signature(self, payload: bytes, sig_header: str) -> bool:
        """Verify Stripe webhook signature"""
        try:
            import stripe
            stripe.WebhookSignature.verify_header(
                payload, sig_header, self.webhook_secret
            )
            return True
        except Exception:
            return False
    
    def refund_payment(self, payment_id: str, amount: Decimal = None) -> Dict[str, Any]:
        """Refund a Stripe payment"""
        try:
            import requests
            
            data = {'payment_intent': payment_id}
            if amount:
                data['amount'] = int(amount * 100)
            
            response = requests.post(
                f"{self.api_base}/refunds",
                headers=self._get_headers(),
                data=urllib.parse.urlencode(data)
            )
            
            if response.status_code == 200:
                result = response.json()
                return {
                    'success': True,
                    'refund_id': result['id'],
                    'status': result['status'],
                    'amount': Decimal(result['amount']) / 100
                }
            else:
                return {'success': False, 'error': response.text}
                
        except Exception as e:
            logger.error(f"Stripe refund error: {e}")
            return {'success': False, 'error': str(e)}


class PayPalGateway(PaymentGatewayBase):
    """PayPal payment gateway implementation"""
    
    def __init__(self, config: Dict[str, Any]):
        super().__init__(config)
        self.gateway_name = "paypal"
        self.client_id = config.get('client_id')
        self.client_secret = config.get('client_secret')
        self.sandbox = config.get('sandbox', True)
        self.api_base = "https://api-m.sandbox.paypal.com" if self.sandbox else "https://api-m.paypal.com"
        self._access_token = None
    
    def _get_access_token(self) -> Optional[str]:
        """Get PayPal access token"""
        if self._access_token:
            return self._access_token
        
        try:
            import requests
            import base64
            
            auth = base64.b64encode(f"{self.client_id}:{self.client_secret}".encode()).decode()
            
            response = requests.post(
                f"{self.api_base}/v1/oauth2/token",
                headers={
                    'Authorization': f'Basic {auth}',
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
                data='grant_type=client_credentials'
            )
            
            if response.status_code == 200:
                self._access_token = response.json()['access_token']
                return self._access_token
            
        except Exception as e:
            logger.error(f"PayPal token error: {e}")
        
        return None
    
    def _get_headers(self) -> Dict[str, str]:
        token = self._get_access_token()
        return {
            'Authorization': f'Bearer {token}',
            'Content-Type': 'application/json'
        }
    
    def create_payment(self, amount: Decimal, currency: str, description: str,
                       metadata: Dict[str, Any] = None, return_url: str = None,
                       cancel_url: str = None) -> Dict[str, Any]:
        """Create a PayPal order"""
        try:
            import requests
            
            order_data = {
                'intent': 'CAPTURE',
                'purchase_units': [{
                    'amount': {
                        'currency_code': currency.upper(),
                        'value': str(amount)
                    },
                    'description': description
                }]
            }
            
            if return_url and cancel_url:
                order_data['application_context'] = {
                    'return_url': return_url,
                    'cancel_url': cancel_url
                }
            
            response = requests.post(
                f"{self.api_base}/v2/checkout/orders",
                headers=self._get_headers(),
                json=order_data
            )
            
            if response.status_code == 201:
                result = response.json()
                # Extract approval URL
                approval_url = None
                for link in result.get('links', []):
                    if link.get('rel') == 'approve':
                        approval_url = link.get('href')
                
                return {
                    'success': True,
                    'payment_id': result['id'],
                    'status': result['status'],
                    'approval_url': approval_url,
                    'amount': amount,
                    'currency': currency
                }
            else:
                logger.error(f"PayPal order creation failed: {response.text}")
                return {'success': False, 'error': response.text}
                
        except Exception as e:
            logger.error(f"PayPal payment creation error: {e}")
            return {'success': False, 'error': str(e)}
    
    def capture_payment(self, order_id: str) -> Dict[str, Any]:
        """Capture a PayPal order"""
        try:
            import requests
            
            response = requests.post(
                f"{self.api_base}/v2/checkout/orders/{order_id}/capture",
                headers=self._get_headers()
            )
            
            if response.status_code == 201:
                result = response.json()
                return {
                    'success': True,
                    'payment_id': result['id'],
                    'status': result['status']
                }
            else:
                return {'success': False, 'error': response.text}
                
        except Exception as e:
            logger.error(f"PayPal capture error: {e}")
            return {'success': False, 'error': str(e)}
    
    def verify_payment(self, payment_id: str) -> Dict[str, Any]:
        """Verify PayPal order status"""
        try:
            import requests
            
            response = requests.get(
                f"{self.api_base}/v2/checkout/orders/{payment_id}",
                headers=self._get_headers()
            )
            
            if response.status_code == 200:
                result = response.json()
                purchase_unit = result.get('purchase_units', [{}])[0]
                amount = purchase_unit.get('amount', {})
                
                return {
                    'success': True,
                    'payment_id': result['id'],
                    'status': result['status'],
                    'amount': Decimal(amount.get('value', 0)),
                    'currency': amount.get('currency_code', 'USD')
                }
            else:
                return {'success': False, 'error': 'Order not found'}
                
        except Exception as e:
            logger.error(f"PayPal verification error: {e}")
            return {'success': False, 'error': str(e)}
    
    def process_webhook(self, payload: bytes, headers: Dict[str, str]) -> Dict[str, Any]:
        """Process PayPal webhook"""
        try:
            event = json.loads(payload)
            event_type = event.get('event_type')
            
            result = {
                'success': True,
                'event_type': event_type,
                'event_id': event.get('id')
            }
            
            if event_type == 'CHECKOUT.ORDER.APPROVED':
                order = event.get('resource', {})
                result.update({
                    'payment_id': order.get('id'),
                    'status': 'approved',
                    'action': 'capture_required'
                })
            elif event_type == 'PAYMENT.CAPTURE.COMPLETED':
                capture = event.get('resource', {})
                result.update({
                    'payment_id': capture.get('supplementary_data', {}).get('related_ids', {}).get('order_id'),
                    'capture_id': capture.get('id'),
                    'status': 'completed',
                    'amount': Decimal(capture.get('amount', {}).get('value', 0)),
                    'currency': capture.get('amount', {}).get('currency_code', 'USD')
                })
            
            return result
            
        except Exception as e:
            logger.error(f"PayPal webhook error: {e}")
            return {'success': False, 'error': str(e)}
    
    def refund_payment(self, payment_id: str, amount: Decimal = None) -> Dict[str, Any]:
        """Refund a PayPal payment"""
        try:
            import requests
            
            refund_data = {}
            if amount:
                refund_data['amount'] = {
                    'value': str(amount),
                    'currency_code': 'USD'  # Should be dynamic
                }
            
            response = requests.post(
                f"{self.api_base}/v2/payments/captures/{payment_id}/refund",
                headers=self._get_headers(),
                json=refund_data
            )
            
            if response.status_code == 201:
                result = response.json()
                return {
                    'success': True,
                    'refund_id': result['id'],
                    'status': result['status']
                }
            else:
                return {'success': False, 'error': response.text}
                
        except Exception as e:
            logger.error(f"PayPal refund error: {e}")
            return {'success': False, 'error': str(e)}


class CryptoGateway(PaymentGatewayBase):
    """Cryptocurrency payment gateway (BTC, ETH, USDT)"""
    
    SUPPORTED_CURRENCIES = ['BTC', 'ETH', 'USDT', 'USDC']
    
    def __init__(self, config: Dict[str, Any]):
        super().__init__(config)
        self.gateway_name = "crypto"
        self.wallet_addresses = config.get('wallet_addresses', {})
        self.confirmation_threshold = config.get('confirmation_threshold', 3)
    
    def create_payment(self, amount: Decimal, currency: str, description: str,
                       metadata: Dict[str, Any] = None) -> Dict[str, Any]:
        """Create a crypto payment request"""
        try:
            currency = currency.upper()
            
            if currency not in self.SUPPORTED_CURRENCIES:
                return {'success': False, 'error': f'Unsupported currency: {currency}'}
            
            wallet_address = self.wallet_addresses.get(currency)
            if not wallet_address:
                return {'success': False, 'error': f'No wallet configured for {currency}'}
            
            # Generate unique payment reference
            import secrets
            payment_ref = secrets.token_urlsafe(16)
            
            return {
                'success': True,
                'payment_id': payment_ref,
                'status': 'pending',
                'wallet_address': wallet_address,
                'amount': amount,
                'currency': currency,
                'description': description,
                'metadata': metadata or {}
            }
            
        except Exception as e:
            logger.error(f"Crypto payment creation error: {e}")
            return {'success': False, 'error': str(e)}
    
    def verify_payment(self, payment_id: str, tx_hash: str = None) -> Dict[str, Any]:
        """Verify crypto payment by checking blockchain"""
        # This would integrate with blockchain APIs (e.g., BlockCypher, Etherscan)
        # For now, return a placeholder
        return {
            'success': False,
            'error': 'Manual verification required for crypto payments'
        }
    
    def process_webhook(self, payload: bytes, headers: Dict[str, str]) -> Dict[str, Any]:
        """Process blockchain webhook notification"""
        try:
            event = json.loads(payload)
            
            # This would process webhooks from blockchain monitoring services
            return {
                'success': True,
                'event_type': 'blockchain_notification',
                'data': event
            }
            
        except Exception as e:
            logger.error(f"Crypto webhook error: {e}")
            return {'success': False, 'error': str(e)}
    
    def refund_payment(self, payment_id: str, amount: Decimal = None) -> Dict[str, Any]:
        """Crypto refunds require manual processing"""
        return {
            'success': False,
            'error': 'Crypto refunds require manual processing'
        }


class PaymentGatewayManager:
    """Manages multiple payment gateways"""
    
    GATEWAY_CLASSES = {
        'stripe': StripeGateway,
        'paypal': PayPalGateway,
        'crypto': CryptoGateway
    }
    
    def __init__(self, db_manager):
        self.db_manager = db_manager
        self._gateways = {}
        self._load_gateways()
    
    def _load_gateways(self):
        """Load configured payment gateways from database"""
        try:
            from ..database.models import PaymentGatewayModel
            
            session = self.db_manager.get_session()
            try:
                gateways = session.query(PaymentGatewayModel).filter_by(
                    is_active=True
                ).all()
                
                for gateway in gateways:
                    gateway_class = self.GATEWAY_CLASSES.get(gateway.gateway_name.lower())
                    if gateway_class:
                        config = {
                            'id': gateway.id,
                            'name': gateway.gateway_name,
                            'display_name': gateway.display_name,
                            **(gateway.config or {})
                        }
                        self._gateways[gateway.gateway_name.lower()] = gateway_class(config)
                        logger.info(f"Loaded payment gateway: {gateway.gateway_name}")
                
            finally:
                session.close()
                
        except Exception as e:
            logger.error(f"Failed to load payment gateways: {e}")
    
    def get_gateway(self, gateway_name: str) -> Optional[PaymentGatewayBase]:
        """Get a specific payment gateway"""
        return self._gateways.get(gateway_name.lower())
    
    def get_available_gateways(self) -> List[Dict[str, Any]]:
        """Get list of available payment gateways"""
        return [
            {
                'name': name,
                'display_name': gateway.config.get('display_name', name.title())
            }
            for name, gateway in self._gateways.items()
        ]
    
    def create_payment(self, gateway_name: str, amount: Decimal, currency: str,
                       description: str, metadata: Dict[str, Any] = None) -> Dict[str, Any]:
        """Create a payment using specified gateway"""
        gateway = self.get_gateway(gateway_name)
        if not gateway:
            return {'success': False, 'error': f'Gateway not available: {gateway_name}'}
        
        return gateway.create_payment(amount, currency, description, metadata)
    
    def verify_payment(self, gateway_name: str, payment_id: str) -> Dict[str, Any]:
        """Verify payment status"""
        gateway = self.get_gateway(gateway_name)
        if not gateway:
            return {'success': False, 'error': f'Gateway not available: {gateway_name}'}
        
        return gateway.verify_payment(payment_id)
    
    def process_webhook(self, gateway_name: str, payload: bytes, 
                        headers: Dict[str, str]) -> Dict[str, Any]:
        """Process webhook from payment gateway"""
        gateway = self.get_gateway(gateway_name)
        if not gateway:
            return {'success': False, 'error': f'Gateway not available: {gateway_name}'}
        
        return gateway.process_webhook(payload, headers)
    
    def refund_payment(self, gateway_name: str, payment_id: str, 
                       amount: Decimal = None) -> Dict[str, Any]:
        """Refund a payment"""
        gateway = self.get_gateway(gateway_name)
        if not gateway:
            return {'success': False, 'error': f'Gateway not available: {gateway_name}'}
        
        return gateway.refund_payment(payment_id, amount)


# Flask routes for payment gateway
def init_payment_routes(app, payment_manager: PaymentGatewayManager, auth_manager):
    """Initialize payment gateway routes"""
    
    @app.route('/api/payments/gateways', methods=['GET'])
    def get_payment_gateways():
        """Get available payment gateways"""
        try:
            gateways = payment_manager.get_available_gateways()
            return jsonify({
                'success': True,
                'gateways': gateways
            })
        except Exception as e:
            logger.error(f"Failed to get payment gateways: {e}")
            return jsonify({'error': str(e)}), 500
    
    @app.route('/api/payments/create', methods=['POST'])
    def create_payment():
        """Create a payment"""
        try:
            from functools import wraps
            from .auth import require_api_auth
            
            @require_api_auth
            def _create():
                data = request.get_json()
                gateway_name = data.get('gateway')
                amount = Decimal(str(data.get('amount', 0)))
                currency = data.get('currency', 'USD')
                description = data.get('description', '')
                metadata = data.get('metadata', {})
                
                if amount <= 0:
                    return jsonify({'error': 'Invalid amount'}), 400
                
                result = payment_manager.create_payment(
                    gateway_name, amount, currency, description, metadata
                )
                
                if result.get('success'):
                    # Record payment in database
                    from ..database.models import BrokerTransactionModel
                    session = payment_manager.db_manager.get_session()
                    try:
                        transaction = BrokerTransactionModel(
                            broker_id=request.current_user.get('user_id'),
                            transaction_type='payment',
                            amount=amount,
                            currency=currency,
                            payment_gateway=gateway_name,
                            payment_id=result.get('payment_id'),
                            status='pending',
                            description=description,
                            metadata=metadata
                        )
                        session.add(transaction)
                        session.commit()
                        
                        result['transaction_id'] = transaction.id
                    finally:
                        session.close()
                    
                    return jsonify(result)
                else:
                    return jsonify(result), 400
            
            return _create()
            
        except Exception as e:
            logger.error(f"Payment creation error: {e}")
            return jsonify({'error': str(e)}), 500
    
    @app.route('/api/payments/<gateway_name>/webhook', methods=['POST'])
    def payment_webhook(gateway_name):
        """Handle payment gateway webhook"""
        try:
            payload = request.get_data()
            headers = dict(request.headers)
            
            result = payment_manager.process_webhook(gateway_name, payload, headers)
            
            if result.get('success'):
                # Update transaction status in database
                if result.get('payment_id'):
                    from ..database.models import BrokerTransactionModel
                    session = payment_manager.db_manager.get_session()
                    try:
                        transaction = session.query(BrokerTransactionModel).filter_by(
                            payment_id=result['payment_id']
                        ).first()
                        
                        if transaction:
                            transaction.status = result.get('status', 'completed')
                            transaction.completed_at = datetime.utcnow()
                            session.commit()
                    finally:
                        session.close()
                
                return jsonify({'received': True})
            else:
                return jsonify(result), 400
                
        except Exception as e:
            logger.error(f"Webhook processing error: {e}")
            return jsonify({'error': str(e)}), 500
    
    @app.route('/api/payments/<payment_id>/verify', methods=['GET'])
    def verify_payment(payment_id):
        """Verify payment status"""
        try:
            from ..database.models import BrokerTransactionModel
            
            session = payment_manager.db_manager.get_session()
            try:
                transaction = session.query(BrokerTransactionModel).filter_by(
                    id=payment_id
                ).first()
                
                if not transaction:
                    return jsonify({'error': 'Transaction not found'}), 404
                
                # Verify with gateway
                if transaction.payment_gateway and transaction.payment_id:
                    result = payment_manager.verify_payment(
                        transaction.payment_gateway,
                        transaction.payment_id
                    )
                    
                    if result.get('success'):
                        # Update transaction status
                        transaction.status = result.get('status', transaction.status)
                        session.commit()
                        
                        return jsonify({
                            'success': True,
                            'transaction': {
                                'id': transaction.id,
                                'status': transaction.status,
                                'amount': float(transaction.amount),
                                'currency': transaction.currency
                            }
                        })
                    else:
                        return jsonify(result), 400
                else:
                    return jsonify({
                        'success': True,
                        'transaction': {
                            'id': transaction.id,
                            'status': transaction.status,
                            'amount': float(transaction.amount),
                            'currency': transaction.currency
                        }
                    })
                    
            finally:
                session.close()
                
        except Exception as e:
            logger.error(f"Payment verification error: {e}")
            return jsonify({'error': str(e)}), 500
    
    logger.info("Payment gateway routes initialized")