"""
Service d'intégration CinetPay pour les transferts Mobile Money
Supporte: Wave, Orange Money, MTN Money, Moov Money
"""
import requests
import logging
from typing import Optional, Dict, Any, List
from datetime import datetime, timedelta
from core.config import settings
from cinetpay_sdk.s_d_k import Cinetpay

logger = logging.getLogger(__name__)

class CinetPayService:
    """Service pour gérer les transferts CinetPay vers Mobile Money"""
    
    BASE_URL = settings.CINETPAY_URL or "https://secure.cinetpay.com"
    
    
    # Cache pour le token (éviter de se reconnecter à chaque requête)
    _token: Optional[str] = None
    _token_expiry: Optional[datetime] = None
    
    @classmethod
    def _get_headers(cls) -> Dict[str, str]:
        """Headers pour les requêtes API"""
        return {
            "Content-Type": "application/x-www-form-urlencoded",
            "Accept": "application/json"
        }
    
    @classmethod
    def _is_token_valid(cls) -> bool:
        """Vérifier si le token actuel est encore valide"""
        if not cls._token or not cls._token_expiry:
            return False
        return datetime.utcnow() < cls._token_expiry
    
    @classmethod
    def authenticate(cls, force_refresh: bool = False, lang: str = "fr") -> Dict[str, Any]:
        """
        Authentification auprès de CinetPay

        Documentation officielle:
        URL: https://client.cinetpay.com/v1/auth/login
        Méthode: POST
        Content-Type: application/x-www-form-urlencoded
        Paramètre GET: lang (en ou fr)
        Paramètres POST: apikey, password

        Args:
            force_refresh: Forcer une nouvelle authentification même si le token est valide
            lang: Langue de la réponse (fr ou en)

        Returns:
            Dict contenant le token et les informations de connexion
        """
        # Utiliser le token en cache s'il est encore valide
        if not force_refresh and cls._is_token_valid():
            return {
                "success": True,
                "token": cls._token,
                "cached": True
            }

        try:
            logger.info("🔐 Authentification CinetPay...")

            # Récupérer les credentials depuis les settings
            apikey = getattr(settings, 'CINETPAY_API_KEY', '')
            password = getattr(settings, 'CINETPAY_SECRET_KEY', '')

            if not apikey or not password:
                raise ValueError("CinetPay credentials not configured in settings")

            # Vérifier si le mot de passe est le placeholder par défaut
            if password == "your-cinetpay-secret-key":
                raise ValueError(
                    "CinetPay SECRET_KEY not configured. "
                    "Please update CINETPAY_SECRET_KEY in .env.mysql with your API password"
                )

            # Préparer les paramètres GET (lang)
            params = {
                "lang": lang
            }

            # Préparer les données POST (apikey, password)
            data = {
                "apikey": apikey,
                "password": password
            }

            response = requests.post(
                f"{cls.BASE_URL}/v1/auth/login",
                headers=cls._get_headers(),
                params=params,  # Paramètre GET: lang
                data=data,      # Paramètres POST: apikey, password
                timeout=30
            )

            response.raise_for_status()
            result = response.json()

            if result.get("code") == "00":
                cls._token = result.get("data", {}).get("token")
                # Le token expire généralement après 15 minutes
                cls._token_expiry = datetime.utcnow() + timedelta(minutes=14)

                logger.info("✅ Authentification CinetPay réussie")
                return {
                    "success": True,
                    "token": cls._token,
                    "data": result.get("data"),
                    "message": result.get("message"),
                    "cached": False
                }
            else:
                logger.error(f"❌ Échec authentification CinetPay: {result.get('message')}")
                return {
                    "success": False,
                    "error": result.get("message", "Authentication failed"),
                    "code": result.get("code")
                }

        except requests.RequestException as e:
            logger.error(f"❌ Erreur réseau CinetPay: {str(e)}")
            return {
                "success": False,
                "error": f"Network error: {str(e)}"
            }
        except Exception as e:
            logger.error(f"❌ Erreur authentification CinetPay: {str(e)}")
            return {
                "success": False,
                "error": str(e)
            }
    
    @classmethod
    def add_contact(cls, contacts: List[Dict[str, str]]) -> Dict[str, Any]:
        """
        Ajouter un ou plusieurs contacts pour les transferts
        
        Args:
            contacts: Liste de contacts avec prefix, phone, name, surname, email
            
        Returns:
            Résultat de l'ajout des contacts
        """
        try:
            # S'assurer d'avoir un token valide
            auth_result = cls.authenticate()
            if not auth_result.get("success"):
                return auth_result
            
            token = auth_result.get("token")
            
            logger.info(f"📇 Ajout de {len(contacts)} contact(s) CinetPay...")
            
            response = requests.post(
                f"{cls.BASE_URL}/v1/transfer/contact",
                headers=cls._get_headers(),
                params={"token": token},
                data={"data": str(contacts)},
                timeout=30
            )
            
            response.raise_for_status()
            data = response.json()
            
            if data.get("code") == "00":
                logger.info("✅ Contact(s) ajouté(s) avec succès")
                return {
                    "success": True,
                    "data": data.get("data")
                }
            else:
                logger.error(f"❌ Échec ajout contact: {data.get('message')}")
                return {
                    "success": False,
                    "error": data.get("message", "Failed to add contact")
                }
                
        except Exception as e:
            logger.error(f"❌ Erreur ajout contact: {str(e)}")
            return {
                "success": False,
                "error": str(e)
            }
    
    @classmethod
    def send_money(
        cls,
        amount: float,
        phone: str,
        prefix: str,
        transaction_id: str,
        notify_url: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        Envoyer de l'argent vers un numéro Mobile Money
        
        Args:
            amount: Montant à envoyer
            phone: Numéro de téléphone (sans préfixe)
            prefix: Préfixe du pays (ex: "225" pour CI, "221" pour SN)
            transaction_id: ID unique de la transaction
            notify_url: URL de notification pour le callback
            
        Returns:
            Résultat du transfert
        """
        try:
            # S'assurer d'avoir un token valide
            auth_result = cls.authenticate()
            if not auth_result.get("success"):
                return auth_result
            
            token = auth_result.get("token")
            
            logger.info(f"💸 Envoi de {amount} FCFA vers +{prefix}{phone}...")
            
            # Préparer les données du transfert
            transfer_data = [{
                "amount": str(int(amount)),
                "phone": phone,
                "prefix": prefix,
                "notify_url": notify_url or getattr(settings, 'CINETPAY_CALLBACK_URL', '')
            }]
            
            response = requests.post(
                f"{cls.BASE_URL}/v1/transfer/money/send/contact",
                headers=cls._get_headers(),
                params={
                    "token": token,
                    "transaction_id": transaction_id
                },
                data={"data": str(transfer_data)},
                timeout=30
            )
            
            response.raise_for_status()
            data = response.json()
            
            if data.get("code") == "00":
                logger.info(f"✅ Transfert initié avec succès: {transaction_id}")
                return {
                    "success": True,
                    "transaction_id": transaction_id,
                    "data": data.get("data"),
                    "message": data.get("message")
                }
            else:
                logger.error(f"❌ Échec transfert: {data.get('message')}")
                return {
                    "success": False,
                    "error": data.get("message", "Transfer failed"),
                    "transaction_id": transaction_id
                }
                
        except Exception as e:
            logger.error(f"❌ Erreur transfert: {str(e)}")
            return {
                "success": False,
                "error": str(e),
                "transaction_id": transaction_id
            }
    
    @classmethod
    def check_transfer_status(cls, transaction_id: str) -> Dict[str, Any]:
        """
        Vérifier le statut d'un transfert
        
        Args:
            transaction_id: ID de la transaction à vérifier
            
        Returns:
            Statut du transfert
        """
        try:
            # S'assurer d'avoir un token valide
            auth_result = cls.authenticate()
            if not auth_result.get("success"):
                return auth_result
            
            token = auth_result.get("token")
            
            logger.info(f"🔍 Vérification statut transfert: {transaction_id}...")
            
            response = requests.get(
                f"{cls.BASE_URL}/v1/transfer/check/money",
                headers=cls._get_headers(),
                params={
                    "token": token,
                    "transaction_id": transaction_id
                },
                timeout=30
            )
            
            response.raise_for_status()
            data = response.json()
            
            if data.get("code") == "00":
                status_data = data.get("data", {})
                logger.info(f"✅ Statut: {status_data.get('status')}")
                return {
                    "success": True,
                    "transaction_id": transaction_id,
                    "status": status_data.get("status"),
                    "data": status_data
                }
            else:
                logger.error(f"❌ Échec vérification: {data.get('message')}")
                return {
                    "success": False,
                    "error": data.get("message", "Status check failed"),
                    "transaction_id": transaction_id
                }
                
        except Exception as e:
            logger.error(f"❌ Erreur vérification statut: {str(e)}")
            return {
                "success": False,
                "error": str(e),
                "transaction_id": transaction_id
            }
    
    @classmethod
    def get_callback_url(cls) -> str:
        """
        Obtenir l'URL de base pour les callbacks CinetPay

        Returns:
            URL de base du serveur backend
        """
        # Récupérer l'URL depuis les settings ou utiliser une valeur par défaut
        backend_url = getattr(settings, 'BACKEND_URL', 'http://localhost:8001')
        return f"{backend_url}/api"

    @classmethod
    async def create_payment_url(cls, payment_data: Dict[str, Any]) -> Dict[str, Any]:
        """
        Créer une URL de paiement CinetPay pour les achats (cartes virtuelles, etc.)

        Args:
            payment_data: Données du paiement (transaction_id, amount, description, etc.)

        Returns:
            Dict contenant l'URL de paiement et le token
        """
        try:
            logger.info(f"💳 Création URL de paiement CinetPay pour {payment_data.get('transaction_id')}")

            # Récupérer les credentials
            apikey = getattr(settings, 'CINETPAY_API_KEY', '')
            site_id = getattr(settings, 'CINETPAY_SITE_ID', '')
            
            client = Cinetpay(apikey=apikey,site_id=site_id)

            if not apikey or not site_id:
                raise ValueError("CinetPay credentials not configured")

            # Préparer les données pour CinetPay
            # data = {
            #     "apikey": apikey,
            #     "site_id": site_id,
            #     "transaction_id": payment_data.get("transaction_id"),
            #     "amount": int(payment_data.get("amount", 0)),
            #     "currency": "XOF",
            #     "description": payment_data.get("description", "Achat"),
            #     "customer_name": payment_data.get("customer_name", ""),
            #     "customer_surname": payment_data.get("customer_surname", ""),
            #     "customer_email": payment_data.get("customer_email", ""),
            #     "customer_phone_number": payment_data.get("customer_phone", ""),
            #     "notify_url": payment_data.get("notify_url", ""),
            #     "return_url": payment_data.get("return_url", ""),
            #     "channels": "ALL",
            #     "lang": "fr"
            # }
            
            data = { 
                'amount' : int(payment_data.get("amount", 0)),
                'currency' : payment_data.get("description", "Achat"),            
                'transaction_id' : payment_data.get("transaction_id"),  
                'description' : "TRANSACTION DESCRIPTION",  
               "notify_url": payment_data.get("notify_url", ""),
                "return_url": payment_data.get("return_url", ""), 
                'customer_name' :payment_data.get("customer_name", ""),                              
                'customer_surname' : payment_data.get("customer_surname", ""),       
            }  
            
            print(client.PaymentInitialization(data) )
            return 
        #     response = requests.post(
        #         "#{BASE_URL}/v2/payment",
        #         json=data,
        #         headers={"Content-Type": "application/json"},
        #         timeout=30
        #     )

        #     response.raise_for_status()
        #     result = response.json()

        #     if result.get("code") == "201":
        #         logger.info(f"✅ URL de paiement créée: {result.get('data', {}).get('payment_url')}")
        #         return {
        #             "success": True,
        #             "payment_url": result.get("data", {}).get("payment_url"),
        #             "payment_token": result.get("data", {}).get("payment_token"),
        #             "data": result.get("data", {})
        #         }
        #     else:
        #         logger.error(f"❌ Erreur CinetPay: {result.get('message')}")
        #         return {
        #             "success": False,
        #             "error": result.get("message", "Erreur inconnue")
        #         }

        except Exception as e:
            logger.error(f"❌ Erreur création URL paiement: {str(e)}")
            return {
                "success": False,
                "error": str(e)
            }

    @classmethod
    def get_country_prefix(cls, operator: str) -> str:
        """
        Obtenir le préfixe pays selon l'opérateur Mobile Money

        Args:
            operator: Nom de l'opérateur (wave, orange, mtn, moov)

        Returns:
            Préfixe du pays
        """
        # Mapping des opérateurs vers les préfixes pays
        operator_prefixes = {
            "wave": "221",      # Sénégal
            "orange_ci": "225", # Côte d'Ivoire
            "mtn_ci": "225",    # Côte d'Ivoire
            "moov_ci": "225",   # Côte d'Ivoire
            "orange_ml": "223", # Mali
            "orange_sn": "221", # Sénégal
        }

        return operator_prefixes.get(operator.lower(), "225")  # Par défaut CI

    @classmethod
    async def check_payment_status(cls, transaction_id: str) -> Dict[str, Any]:
        """
        Vérifier le statut d'un paiement CinetPay

        Args:
            transaction_id: ID de la transaction CinetPay

        Returns:
            Dict contenant le statut du paiement
        """
        try:
            logger.info(f"🔍 Vérification statut paiement CinetPay: {transaction_id}")

            # Récupérer les credentials
            apikey = getattr(settings, 'CINETPAY_API_KEY', '')
            site_id = getattr(settings, 'CINETPAY_SITE_ID', '')

            if not apikey or not site_id:
                raise ValueError("CinetPay credentials not configured")

            # Préparer les données pour CinetPay
            data = {
                "apikey": apikey,
                "site_id": site_id,
                "transaction_id": transaction_id
            }
            client = Cinetpay(apikey=apikey,site_id=site_id)
            response = requests.post(
                "#{BASE_URL}/v2/payment/check",
                json=data,
                headers={"Content-Type": "application/json"},
                timeout=30
            )

            response.raise_for_status()
            result = response.json()

            if result.get("code") == "00":
                payment_data = result.get("data", {})
                logger.info(f"✅ Statut paiement récupéré: {payment_data.get('payment_status')}")

                return {
                    "success": True,
                    "status": payment_data.get("payment_status"),
                    "amount": payment_data.get("amount"),
                    "currency": payment_data.get("currency"),
                    "payment_method": payment_data.get("payment_method"),
                    "payment_date": payment_data.get("payment_date"),
                    "operator_id": payment_data.get("operator_id"),
                    "data": payment_data
                }
            else:
                logger.error(f"❌ Erreur vérification statut: {result.get('message')}")
                return {
                    "success": False,
                    "error": result.get("message", "Erreur inconnue")
                }

        except Exception as e:
            logger.error(f"❌ Erreur vérification statut paiement: {str(e)}")
            return {
                "success": False,
                "error": str(e)
            }
