"""Main payment orchestration engine."""

import logging
import uuid
from typing import Dict, Any, List, Optional
from datetime import datetime

from payment_orchestrator.models.payment import Payment
from payment_orchestrator.models.transaction import Transaction
from payment_orchestrator.core.validator import PaymentValidator
from payment_orchestrator.core.fraud_detector import FraudDetector
from payment_orchestrator.core.processor import PaymentProcessor
from payment_orchestrator.services.encryptor import PaymentEncryptor
from payment_orchestrator.services.secret_manager import SecretFetcher
from payment_orchestrator.services.notifier import PaymentNotifier
from payment_orchestrator.services.mailer import PaymentMailer
from payment_orchestrator.services.queue_manager import RetryQueueManager
from payment_orchestrator.services.db_manager import TransactionStore

logger = logging.getLogger(__name__)


class PaymentOrchestrator:
    """
    Main orchestrator implementing the facade pattern.
    
    This class coordinates all payment processing services and implements
    the complete payment flow from validation to notification.
    """
    
    def __init__(self, kms_key_id: str, secret_name: str, sns_topic_arn: str,
                 sqs_queue_url: str, dynamodb_table: str, sender_email: str,
                 region: str = "us-east-1"):
        """
        Initialize the PaymentOrchestrator with all service dependencies.
        
        Args:
            kms_key_id: KMS key ID for encryption
            secret_name: Secrets Manager secret name
            sns_topic_arn: SNS topic ARN for notifications
            sqs_queue_url: SQS queue URL for retries
            dynamodb_table: DynamoDB table name
            sender_email: Verified SES sender email
            region: AWS region
        """
        self.region = region
        
        # Initialize all service classes
        self.validator = PaymentValidator()
        self.fraud_detector = FraudDetector()
        self.encryptor = PaymentEncryptor(kms_key_id, region)
        self.secret_fetcher = SecretFetcher(region)
        self.notifier = PaymentNotifier(sns_topic_arn, region)
        self.mailer = PaymentMailer(sender_email, region)
        self.queue_manager = RetryQueueManager(sqs_queue_url, region)
        self.transaction_store = TransactionStore(dynamodb_table, region)
        
        # Fetch payment processor API key
        self.api_key = self.secret_fetcher.get_api_key(secret_name)
        if not self.api_key:
            logger.error("Failed to fetch payment processor API key")
            raise ValueError("Cannot initialize without API key")
        
        logger.info("PaymentOrchestrator initialized successfully")
    
    def process_payment(self, payment_data: dict) -> Dict[str, Any]:
        """
        Process payment following the complete orchestration flow.
        
        Flow:
        1. Validate payment
        2. Run fraud check
        3. Encrypt sensitive data
        4. Save transaction as PENDING
        5. Process payment
        6. Update transaction status
        7. Send notifications (SNS + SES) or queue for retry
        
        Args:
            payment_data: Payment request dictionary
            
        Returns:
            Dictionary with transaction result
        """
        transaction_id = str(uuid.uuid4())
        logger.info(f"Starting payment processing: {transaction_id}")
        
        try:
            # Step 1: Validate payment
            is_valid, error = self.validator.validate(payment_data)
            if not is_valid:
                logger.error(f"Validation failed: {error}")
                return self._create_error_response(transaction_id, error)
            
            # Step 2: Run fraud check
            is_fraud, fraud_reason = self.fraud_detector.check_fraud(payment_data)
            if is_fraud:
                logger.error(f"Fraud detected: {fraud_reason}")
                return self._create_error_response(transaction_id, f"Fraud detected: {fraud_reason}")
            
            # Step 3: Encrypt card number and CVV using KMS
            encrypted_card = self.encryptor.encrypt(payment_data["card_number"])
            encrypted_cvv = self.encryptor.encrypt(payment_data["cvv"])
            
            if not encrypted_card or not encrypted_cvv:
                error = "Encryption failed"
                logger.error(error)
                return self._create_error_response(transaction_id, error)
            
            # Create payment object
            payment = Payment(
                cardholder_name=payment_data["cardholder_name"],
                card_number=payment_data["card_number"],
                expiry_month=payment_data["expiry_month"],
                expiry_year=payment_data["expiry_year"],
                cvv=payment_data["cvv"],
                amount=payment_data["amount"],
                currency=payment_data["currency"],
                email=payment_data["email"],
                transaction_id=transaction_id,
                timestamp=datetime.utcnow().isoformat()
            )
            
            # Step 4: Save transaction with status PENDING to DynamoDB
            transaction_record = {
                "transaction_id": transaction_id,
                "status": "PENDING",
                "amount": payment.amount,
                "currency": payment.currency,
                "cardholder_name": payment.cardholder_name,
                "masked_card": payment.get_masked_card(),
                "email": payment.email,
                "timestamp": payment.timestamp,
                "encrypted_card": encrypted_card,
                "encrypted_cvv": encrypted_cvv,
                "retry_count": 0
            }
            
            if not self.transaction_store.save_transaction(transaction_record):
                error = "Failed to save transaction"
                logger.error(error)
                return self._create_error_response(transaction_id, error)
            
            # Step 5: Process payment using PaymentProcessor
            processor = PaymentProcessor(self.api_key)
            success, message = processor.process(
                payment.card_number,
                payment.cvv,
                payment.amount,
                payment.currency,
                payment.cardholder_name
            )
            
            # Step 6: Update transaction status in DynamoDB
            if success:
                # Step 7a: SUCCESS path
                self.transaction_store.update_transaction_status(
                    transaction_id, "SUCCESS"
                )
                
                # Publish to SNS
                self.notifier.notify_success(
                    transaction_id, payment.amount, payment.currency,
                    payment.cardholder_name, payment.email
                )
                
                # Send receipt via SES
                self.mailer.send_receipt(
                    payment.email, transaction_id, payment.amount,
                    payment.currency, payment.cardholder_name,
                    payment.get_masked_card(), "SUCCESS"
                )
                
                logger.info(f"Payment processed successfully: {transaction_id}")
                return {
                    "success": True,
                    "transaction_id": transaction_id,
                    "status": "SUCCESS",
                    "message": message
                }
            else:
                # Step 7b: FAILED path
                self.transaction_store.update_transaction_status(
                    transaction_id, "FAILED", message
                )
                
                # Send to SQS retry queue
                self.queue_manager.send_to_retry_queue(payment_data, retry_count=0)
                
                # Notify failure
                self.notifier.notify_failure(
                    transaction_id, payment.amount, payment.currency,
                    payment.cardholder_name, payment.email, message
                )
                
                logger.warning(f"Payment failed: {transaction_id}")
                return {
                    "success": False,
                    "transaction_id": transaction_id,
                    "status": "FAILED",
                    "message": message
                }
        
        except Exception as e:
            error = f"Unexpected error during payment processing: {str(e)}"
            logger.error(error)
            return self._create_error_response(transaction_id, error)
    
    def retry_failed_payment(self, payment_data: dict, retry_count: int) -> Dict[str, Any]:
        """
        Retry a failed payment from the queue.
        
        Args:
            payment_data: Original payment data
            retry_count: Current retry attempt number
            
        Returns:
            Dictionary with retry result
        """
        logger.info(f"Retrying payment, attempt {retry_count + 1}")
        
        # Maximum 3 retries
        if retry_count >= 3:
            logger.error("Maximum retry attempts reached")
            return {
                "success": False,
                "message": "Maximum retry attempts exceeded"
            }
        
        # Process payment again
        result = self.process_payment(payment_data)
        
        # If still failed and under retry limit, send back to queue
        if not result.get("success") and retry_count < 2:
            self.queue_manager.send_to_retry_queue(payment_data, retry_count + 1)
        
        return result
    
    def get_transaction_history(self, email: Optional[str] = None) -> List[Dict[str, Any]]:
        """
        Retrieve transaction history.
        
        Args:
            email: Optional email filter
            
        Returns:
            List of transaction dictionaries
        """
        if email:
            transactions = self.transaction_store.query_transactions_by_email(email)
        else:
            transactions = self.transaction_store.get_all_transactions()
        
        logger.info(f"Retrieved {len(transactions)} transactions")
        return transactions
    
    def _create_error_response(self, transaction_id: str, error: str) -> Dict[str, Any]:
        """
        Create standardized error response.
        
        Args:
            transaction_id: Transaction ID
            error: Error message
            
        Returns:
            Error response dictionary
        """
        return {
            "success": False,
            "transaction_id": transaction_id,
            "status": "ERROR",
            "message": error
        }
