# /opt/docker/dev/service_finder/backend/app/services/payment_router.py
"""
Payment Router - Fizetési irányító a Kettős Lakat (Double Lock) biztonsággal.

Felelős:
1. PaymentIntent létrehozása (Prior Intent)
2. Stripe Checkout Session indítása vagy belső ajándékozás kezelése
3. Webhook validáció és atomi tranzakció végrehajtása
4. SmartDeduction integráció belső fizetésekhez
"""

import logging
import uuid
from datetime import datetime, timedelta
from typing import Dict, Any, Optional, Tuple
from decimal import Decimal

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, update

from app.models.marketplace.payment import PaymentIntent, PaymentIntentStatus
from app.models import WalletType
from app.models.identity import User, Wallet, ActiveVoucher
from app.services.billing_engine import AtomicTransactionManager, SmartDeduction
from app.services.stripe_adapter import stripe_adapter

logger = logging.getLogger("payment-router")


class PaymentRouter:
    """Fizetési irányító a Kettős Lakat biztonsággal."""
    
    @classmethod
    async def create_payment_intent(
        cls,
        db: AsyncSession,
        payer_id: int,
        net_amount: float,
        handling_fee: float,
        target_wallet_type: WalletType,
        beneficiary_id: Optional[int] = None,
        currency: str = "EUR",
        metadata: Optional[Dict[str, Any]] = None
    ) -> PaymentIntent:
        """
        PaymentIntent létrehozása (Prior Intent - Kettős Lakat 1. lépés).
        
        Args:
            db: Database session
            payer_id: Fizető felhasználó ID
            net_amount: Kedvezményezett által kapott összeg
            handling_fee: Kényelmi díj
            target_wallet_type: Cél pénztárca típus
            beneficiary_id: Kedvezményezett felhasználó ID (opcionális)
            currency: Pénznem
            metadata: Extra metadata
            
        Returns:
            PaymentIntent: Létrehozott PaymentIntent
        """
        # Összeg validáció
        if net_amount <= 0:
            raise ValueError("net_amount pozitív szám kell legyen")
        if handling_fee < 0:
            raise ValueError("handling_fee nem lehet negatív")
        
        gross_amount = net_amount + handling_fee
        
        # Felhasználó ellenőrzése
        payer_stmt = select(User).where(User.id == payer_id)
        payer_result = await db.execute(payer_stmt)
        payer = payer_result.scalar_one_or_none()
        
        if not payer:
            raise ValueError(f"Payer nem található: id={payer_id}")
        
        # Beneficiary ellenőrzése (ha meg van adva)
        if beneficiary_id:
            beneficiary_stmt = select(User).where(User.id == beneficiary_id)
            beneficiary_result = await db.execute(beneficiary_stmt)
            beneficiary = beneficiary_result.scalar_one_or_none()
            
            if not beneficiary:
                raise ValueError(f"Beneficiary nem található: id={beneficiary_id}")
        
        # PaymentIntent létrehozása
        payment_intent = PaymentIntent(
            payer_id=payer_id,
            beneficiary_id=beneficiary_id,
            target_wallet_type=target_wallet_type,
            net_amount=Decimal(str(net_amount)),
            handling_fee=Decimal(str(handling_fee)),
            gross_amount=Decimal(str(gross_amount)),
            currency=currency,
            status=PaymentIntentStatus.PENDING,
            expires_at=datetime.utcnow() + timedelta(hours=24),
            metadata=metadata or {},
        )
        
        db.add(payment_intent)
        await db.flush()
        await db.refresh(payment_intent)
        
        logger.info(
            f"PaymentIntent létrehozva: id={payment_intent.id}, "
            f"token={payment_intent.intent_token}, "
            f"net={net_amount}, fee={handling_fee}, gross={gross_amount}"
        )
        
        return payment_intent
    
    @classmethod
    async def initiate_stripe_payment(
        cls,
        db: AsyncSession,
        payment_intent_id: int,
        success_url: str,
        cancel_url: str
    ) -> Dict[str, Any]:
        """
        Stripe fizetés indítása PaymentIntent alapján.
        
        Args:
            db: Database session
            payment_intent_id: PaymentIntent ID
            success_url: Sikeres fizetés URL
            cancel_url: Megszakítás URL
            
        Returns:
            Dict: Stripe Checkout Session adatai
        """
        # PaymentIntent lekérdezése
        stmt = select(PaymentIntent).where(PaymentIntent.id == payment_intent_id)
        result = await db.execute(stmt)
        payment_intent = result.scalar_one_or_none()
        
        if not payment_intent:
            raise ValueError(f"PaymentIntent nem található: id={payment_intent_id}")
        
        if payment_intent.status != PaymentIntentStatus.PENDING:
            raise ValueError(f"PaymentIntent nem PENDING státuszú: {payment_intent.status}")
        
        # Stripe Checkout Session létrehozása
        session_data = await stripe_adapter.create_checkout_session(
            payment_intent=payment_intent,
            success_url=success_url,
            cancel_url=cancel_url,
            metadata={
                "payment_intent_id": payment_intent.id,
                "payer_email": payment_intent.payer.email if payment_intent.payer else None,
            }
        )
        
        # PaymentIntent frissítése Stripe adatokkal
        payment_intent.stripe_session_id = session_data["session_id"]
        payment_intent.stripe_payment_intent_id = session_data.get("payment_intent_id")
        payment_intent.expires_at = session_data["expires_at"]
        
        
        logger.info(
            f"Stripe fizetés indítva: payment_intent={payment_intent.id}, "
            f"session={session_data['session_id']}"
        )
        
        return {
            "payment_intent_id": payment_intent.id,
            "intent_token": str(payment_intent.intent_token),
            "stripe_session_id": session_data["session_id"],
            "checkout_url": session_data["url"],
            "expires_at": session_data["expires_at"].isoformat(),
        }
    
    @classmethod
    async def process_internal_payment(
        cls,
        db: AsyncSession,
        payment_intent_id: int
    ) -> Dict[str, Any]:
        """
        Belső ajándékozás feldolgozása (SmartDeduction használatával).
        
        Args:
            db: Database session
            payment_intent_id: PaymentIntent ID
            
        Returns:
            Dict: Tranzakció eredménye
        """
        logger.info(f"process_internal_payment kezdődik: payment_intent_id={payment_intent_id}")
        
        # PaymentIntent lekérdezése zárolással
        stmt = select(PaymentIntent).where(
            PaymentIntent.id == payment_intent_id,
            PaymentIntent.status == PaymentIntentStatus.PENDING
        )
        result = await db.execute(stmt)
        payment_intent = result.scalar_one_or_none()
        
        if not payment_intent:
            logger.error(f"PaymentIntent nem található vagy nem PENDING: id={payment_intent_id}")
            raise ValueError(f"PaymentIntent nem található vagy nem PENDING: id={payment_intent_id}")
        
        logger.info(f"PaymentIntent megtalálva: id={payment_intent.id}, payer={payment_intent.payer_id}, beneficiary={payment_intent.beneficiary_id}, net_amount={payment_intent.net_amount}, target_wallet_type={payment_intent.target_wallet_type}")
        
        # Státusz frissítése PROCESSING-re
        payment_intent.status = PaymentIntentStatus.PROCESSING
        
        async def execute_logic():
            logger.info("execute_logic: SmartDeduction.deduct_from_wallets hívása...")
            # SmartDeduction használata a fizető pénztárcájából
            used_amounts = await SmartDeduction.deduct_from_wallets(
                db=db,
                user_id=payment_intent.payer_id,
                amount=float(payment_intent.net_amount)
            )
            logger.info(f"SmartDeduction eredmény: {used_amounts}")
            
            # Ha van beneficiary, akkor hozzáadjuk a net_amount-ot a cél pénztárcájához
            if payment_intent.beneficiary_id:
                # Beneficiary wallet lekérdezése
                wallet_stmt = select(Wallet).where(Wallet.user_id == payment_intent.beneficiary_id)
                wallet_result = await db.execute(wallet_stmt)
                beneficiary_wallet = wallet_result.scalar_one_or_none()
                
                if not beneficiary_wallet:
                    logger.error(f"Beneficiary wallet nem található: user_id={payment_intent.beneficiary_id}")
                    raise ValueError(f"Beneficiary wallet nem található: user_id={payment_intent.beneficiary_id}")
                
                logger.info(f"Beneficiary wallet megtalálva: id={beneficiary_wallet.id}")
                
                # Összeg hozzáadása a megfelelő wallet típushoz
                amount_decimal = Decimal(str(payment_intent.net_amount))
                if payment_intent.target_wallet_type == WalletType.EARNED:
                    beneficiary_wallet.earned_credits += amount_decimal
                    logger.info(f"Beneficiary earned_credits növelve: +{amount_decimal}")
                elif payment_intent.target_wallet_type == WalletType.PURCHASED:
                    beneficiary_wallet.purchased_credits += amount_decimal
                    logger.info(f"Beneficiary purchased_credits növelve: +{amount_decimal}")
                elif payment_intent.target_wallet_type == WalletType.SERVICE_COINS:
                    beneficiary_wallet.service_coins += amount_decimal
                    logger.info(f"Beneficiary service_coins növelve: +{amount_decimal}")
                elif payment_intent.target_wallet_type == WalletType.VOUCHER:
                    # VOUCHER esetén ActiveVoucher rekordot hozunk létre
                    voucher = ActiveVoucher(
                        wallet_id=beneficiary_wallet.id,
                        amount=float(payment_intent.net_amount),
                        original_amount=float(payment_intent.net_amount),
                        expires_at=datetime.utcnow() + timedelta(days=365)
                    )
                    db.add(voucher)
                    logger.info(f"ActiveVoucher létrehozva: wallet_id={beneficiary_wallet.id}, amount={payment_intent.net_amount}")
                # Ha más típus, nem csinálunk semmit
            
            
            logger.info("AtomicTransactionManager.atomic_billing_transaction hívása a used_amounts-szel...")
            # Atomi tranzakció létrehozása a főkönyvbe, már meglévő used_amounts átadásával
            transaction_details = await AtomicTransactionManager.atomic_billing_transaction(
                db=db,
                user_id=payment_intent.payer_id,
                amount=float(payment_intent.net_amount),
                description=f"Internal payment to {payment_intent.beneficiary_id or 'system'}",
                reference_type="internal_payment",
                reference_id=payment_intent.id,
                used_amounts=used_amounts,  # Pass the already calculated used_amounts
                beneficiary_id=payment_intent.beneficiary_id
            )
            
            # PaymentIntent befejezése
            payment_intent.mark_completed(transaction_id=transaction_details["transaction_id"])
            
            logger.info(
                f"Belső fizetés sikeres: payment_intent={payment_intent.id}, "
                f"transaction={transaction_details['transaction_id']}, "
                f"amount={payment_intent.net_amount}"
            )
            
            return {
                "success": True,
                "payment_intent_id": payment_intent.id,
                "transaction_id": transaction_details["transaction_id"],
                "used_amounts": used_amounts,
                "beneficiary_credited": payment_intent.beneficiary_id is not None,
            }

        try:
            # Start atomic transaction only if not already in one
            if not db.in_transaction():
                logger.info("Nincs aktív tranzakció, új tranzakció indítása...")
                # No active transaction, start a new one
                async with db.begin():
                    result = await execute_logic()
                    logger.info(f"Tranzakció sikeresen commitálva: payment_intent_id={payment_intent_id}")
                    return result
            else:
                logger.info("Már van aktív tranzakció, a meglévőn belül fut...")
                # Already in a transaction, execute logic within existing transaction
                return await execute_logic()
            
        except Exception as e:
            logger.error(f"Belső fizetés sikertelen: {e}", exc_info=True)
            payment_intent.mark_failed(reason=str(e))
            raise
    
    @classmethod
    async def process_stripe_webhook(
        cls,
        db: AsyncSession,
        payload: bytes,
        signature: str
    ) -> Dict[str, Any]:
        """
        Stripe webhook feldolgozása Kettős Lakat validációval.
        
        Args:
            db: Database session
            payload: HTTP request body
            signature: Stripe-Signature header
            
        Returns:
            Dict: Webhook feldolgozási eredmény
        """
        # 1. HMAC aláírás validálása (Kettős Lakat - 1. lépés)
        is_valid, event = await stripe_adapter.verify_webhook_signature(payload, signature)
        
        if not is_valid or not event:
            logger.error("Stripe webhook aláírás érvénytelen")
            return {"success": False, "error": "Invalid signature"}
        
        event_type = event.get("type")
        logger.info(f"Stripe webhook fogadva: {event_type}")
        
        # 2. checkout.session.completed esemény feldolgozása
        if event_type == "checkout.session.completed":
            return await cls._handle_checkout_completed(db, event)
        
        # 3. payment_intent.succeeded esemény feldolgozása
        elif event_type == "payment_intent.succeeded":
            return await cls._handle_payment_intent_succeeded(db, event)
        
        # Egyéb események naplózása
        else:
            logger.info(f"Stripe webhook esemény nem feldolgozva: {event_type}")
            return {"success": True, "event": event_type, "processed": False}
    
    @classmethod
    async def _handle_checkout_completed(
        cls,
        db: AsyncSession,
        event: Dict[str, Any]
    ) -> Dict[str, Any]:
        """checkout.session.completed esemény feldolgozása."""
        # Stripe adatok kinyerése
        stripe_data = await stripe_adapter.handle_checkout_completed(event)
        
        if not stripe_data["success"]:
            return stripe_data
        
        intent_token = stripe_data["intent_token"]
        stripe_amount = stripe_data["amount_total"]
        
        # 2. PaymentIntent keresése intent_token alapján (Kettős Lakat - 2. lépés)
        stmt = select(PaymentIntent).where(
            PaymentIntent.intent_token == uuid.UUID(intent_token),
            PaymentIntent.status == PaymentIntentStatus.PENDING
        )
        result = await db.execute(stmt)
        payment_intent = result.scalar_one_or_none()
        
        if not payment_intent:
            logger.error(f"PaymentIntent nem található intent_token alapján: {intent_token}")
            return {"success": False, "error": "PaymentIntent not found or not PENDING"}
        
        # 3. Összeg egyezés ellenőrzése (Kettős Lakat - 3. lépés)
        # Stripe centben küldi, mi pedig gross_amount-ban tároljuk
        if abs(float(payment_intent.gross_amount) - stripe_amount) > 0.01:  # 1 cent tolerancia
            logger.error(
                f"Összeg nem egyezik: PaymentIntent={payment_intent.gross_amount}, "
                f"Stripe={stripe_amount}"
            )
            payment_intent.mark_failed(reason="Amount mismatch with Stripe")
            return {"success": False, "error": "Amount mismatch"}
        
        # 4. Stripe adatok frissítése
        payment_intent.stripe_session_id = stripe_data["session_id"]
        payment_intent.stripe_payment_intent_id = stripe_data.get("payment_intent_id")
        
        try:
            # 5. Atomi tranzakció végrehajtása (Kettős Lakat - 4. lépés)
            transaction_details = await AtomicTransactionManager.atomic_billing_transaction(
                db=db,
                user_id=payment_intent.payer_id,
                amount=float(payment_intent.net_amount),
                description=f"Stripe payment for {payment_intent.target_wallet_type.value}",
                reference_type="stripe_payment",
                reference_id=payment_intent.id
            )
            
            # 6. PaymentIntent befejezése
            payment_intent.mark_completed(transaction_id=transaction_details["transaction_id"])
            
            logger.info(
                f"Stripe webhook sikeresen feldolgozva: payment_intent={payment_intent.id}, "
                f"transaction={transaction_details['transaction_id']}"
            )
            
            return {
                "success": True,
                "payment_intent_id": payment_intent.id,
                "transaction_id": transaction_details["transaction_id"],
                "amount": float(payment_intent.net_amount),
                "handling_fee": float(payment_intent.handling_fee),
                "event": "checkout.session.completed",
            }
            
        except Exception as e:
            logger.error(f"Atomi tranzakció sikertelen: {e}")
            payment_intent.mark_failed(reason=str(e))
            return {"success": False, "error": f"Transaction failed: {str(e)}"}
    
    @classmethod
    async def _handle_payment_intent_succeeded(
        cls,
        db: AsyncSession,
        event: Dict[str, Any]
    ) -> Dict[str, Any]:
        """payment_intent.succeeded esemény feldolgozása."""
        stripe_data = await stripe_adapter.handle_payment_intent_succeeded(event)
        
        if not stripe_data["success"]:
            return stripe_data
        
        # PaymentIntent keresése stripe_payment_intent_id alapján
        stripe_payment_intent_id = stripe_data["payment_intent_id"]
        
        stmt = select(PaymentIntent).where(
            PaymentIntent.stripe_payment_intent_id == stripe_payment_intent_id
        )
        result = await db.execute(stmt)
        payment_intent = result.scalar_one_or_none()
        
        if payment_intent and payment_intent.status == PaymentIntentStatus.PENDING:
            # Ha még PENDING, akkor frissítsük COMPLETED-re
            payment_intent.status = PaymentIntentStatus.COMPLETED
            payment_intent.completed_at = datetime.utcnow()
        
        logger.info(f"Stripe payment intent succeeded: {stripe_payment_intent_id}")
        
        return {
            "success": True,
            "event": "payment_intent.succeeded",
            "stripe_payment_intent_id": stripe_payment_intent_id,
            "payment_intent_id": payment_intent.id if payment_intent else None,
        }
    
    @classmethod
    async def get_payment_intent_status(
        cls,
        db: AsyncSession,
        payment_intent_id: int
    ) -> Dict[str, Any]:
        """
        PaymentIntent státusz lekérdezése.
        
        Args:
            db: Database session
            payment_intent_id: PaymentIntent ID
            
        Returns:
            Dict: PaymentIntent adatai és státusza
        """
        stmt = select(PaymentIntent).where(PaymentIntent.id == payment_intent_id)
        result = await db.execute(stmt)
        payment_intent = result.scalar_one_or_none()
        
        if not payment_intent:
            raise ValueError(f"PaymentIntent nem található: id={payment_intent_id}")
        
        return {
            "id": payment_intent.id,
            "intent_token": str(payment_intent.intent_token),
            "status": payment_intent.status.value,
            "payer_id": payment_intent.payer_id,
            "beneficiary_id": payment_intent.beneficiary_id,
            "target_wallet_type": payment_intent.target_wallet_type.value,
            "net_amount": float(payment_intent.net_amount),
            "handling_fee": float(payment_intent.handling_fee),
            "gross_amount": float(payment_intent.gross_amount),
            "currency": payment_intent.currency,
            "created_at": payment_intent.created_at.isoformat() if payment_intent.created_at else None,
            "updated_at": payment_intent.updated_at.isoformat() if payment_intent.updated_at else None,
            "completed_at": payment_intent.completed_at.isoformat() if payment_intent.completed_at else None,
            "stripe_session_id": payment_intent.stripe_session_id,
            "stripe_payment_intent_id": payment_intent.stripe_payment_intent_id,
            "transaction_id": str(payment_intent.transaction_id) if payment_intent.transaction_id else None
        }