
import stripe
from flask import request, jsonify, current_app
from ..extensions import db
from ..models import Payment
import logging

logger = logging.getLogger(__name__)

class ProcessedWebhookEvent(db.Model):
    """Track processed Stripe webhook events to prevent duplicates"""
    __tablename__ = 'processed_webhook_events'
    
    id = db.Column(db.Integer, primary_key=True)
    stripe_event_id = db.Column(db.String(255), unique=True, nullable=False, index=True)
    event_type = db.Column(db.String(100))
    processed_at = db.Column(db.DateTime, default=db.func.now())
    
    def __repr__(self):
        return f'<ProcessedWebhookEvent {self.stripe_event_id}>'

def handle_stripe_webhook():
    """Handle Stripe webhooks for payment updates"""
    payload = request.get_data()
    sig_header = request.headers.get('Stripe-Signature')
    
    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, current_app.config['STRIPE_WEBHOOK_SECRET']
        )
    except ValueError:
        logger.error("Invalid payload in Stripe webhook")
        return jsonify({"error": "Invalid payload"}), 400
    except stripe.error.SignatureVerificationError:
        logger.error("Invalid signature in Stripe webhook")
        return jsonify({"error": "Invalid signature"}), 400
    
    # Check if event already processed (idempotency) - use atomic insert
    try:
        processed_event = ProcessedWebhookEvent(
            stripe_event_id=event['id'],
            event_type=event['type']
        )
        db.session.add(processed_event)
        db.session.commit()
    except Exception as e:
        # Unique constraint violation means already processed
        db.session.rollback()
        if 'unique' in str(e).lower() or 'duplicate' in str(e).lower():
            logger.info(f"Webhook event {event['id']} already processed, ignoring")
            return jsonify({"status": "already_processed"}), 200
        else:
            logger.error(f"Failed to store processed webhook event: {e}")
            # Continue processing even if storage fails (less critical)
    
    # Handle the event
    if event['type'] == 'payment_intent.succeeded':
        payment_intent = event['data']['object']
        handle_payment_succeeded(payment_intent)
        
    elif event['type'] == 'payment_intent.payment_failed':
        payment_intent = event['data']['object']
        handle_payment_failed(payment_intent)
        
    elif event['type'] == 'payment_intent.canceled':
        payment_intent = event['data']['object']
        handle_payment_canceled(payment_intent)
    
    else:
        logger.info(f"Unhandled Stripe event type: {event['type']}")
    
    return jsonify({"status": "success"}), 200

def handle_payment_succeeded(payment_intent):
    """Handle successful payment - move to escrow"""
    try:
        payment = Payment.query.filter_by(
            stripe_payment_intent_id=payment_intent['id']
        ).with_for_update().first()  # Lock row for update
        
        if payment:
            # Only update if not already in final state
            if payment.status not in ['held_escrow', 'transferred', 'refunded']:
                payment.status = 'held_escrow'
                payment.stripe_status = payment_intent['status']
                db.session.commit()
                
                logger.info(f"Payment {payment.payment_reference} moved to escrow")
            else:
                logger.info(f"Payment {payment.payment_reference} already in state {payment.status}, skipping")
            
    except Exception as e:
        logger.error(f"Error handling payment success: {str(e)}")
        db.session.rollback()

def handle_payment_failed(payment_intent):
    """Handle failed payment"""
    try:
        payment = Payment.query.filter_by(
            stripe_payment_intent_id=payment_intent['id']
        ).first()
        
        if payment:
            payment.status = 'failed'
            payment.stripe_status = payment_intent['status']
            db.session.commit()
            
            logger.warning(f"Payment {payment.payment_reference} failed")
            
    except Exception as e:
        logger.error(f"Error handling payment failure: {str(e)}")
        db.session.rollback()

def handle_payment_canceled(payment_intent):
    """Handle canceled payment"""
    try:
        payment = Payment.query.filter_by(
            stripe_payment_intent_id=payment_intent['id']
        ).first()
        
        if payment:
            payment.status = 'canceled'
            payment.stripe_status = payment_intent['status']
            db.session.commit()
            
            logger.info(f"Payment {payment.payment_reference} canceled")
            
    except Exception as e:
        logger.error(f"Error handling payment cancellation: {str(e)}")
        db.session.rollback()
