"""
Service de gestion des cartes NFC physiques
"""
import uuid
import json
from datetime import datetime
from sqlalchemy.orm import Session
from models.models import NFCCard, NFCCardTransaction, User, Transaction, TransactionType, TransactionStatus
from typing import Optional, Dict, Any
import logging

logger = logging.getLogger(__name__)

class NFCCardService:
    """Service pour gérer les cartes NFC physiques"""
    
    @staticmethod
    def create_nfc_card(
        db: Session,
        user_id: str,
        card_uid: str,
        card_number: str,
        card_type: str = "physical",
        max_balance: float = 50000.0,
        is_primary: bool = False
    ) -> NFCCard:
        """Créer une nouvelle carte NFC"""
        
        # Vérifier que l'utilisateur existe
        user = db.query(User).filter(User.id == user_id).first()
        if not user:
            raise ValueError("Utilisateur introuvable")
        
        # Vérifier que l'UID n'existe pas déjà
        existing_card = db.query(NFCCard).filter(NFCCard.card_uid == card_uid).first()
        if existing_card:
            raise ValueError("Une carte avec cet UID existe déjà")
        
        # Si c'est la carte principale, désactiver les autres cartes principales
        if is_primary:
            db.query(NFCCard).filter(
                NFCCard.user_id == user_id,
                NFCCard.is_primary == True
            ).update({"is_primary": False})
        
        # Créer la carte
        nfc_card = NFCCard(
            id=str(uuid.uuid4()),
            card_uid=card_uid,
            card_number=card_number,
            user_id=user_id,
            card_type=card_type,
            status="active",
            balance=0.0,
            max_balance=max_balance,
            is_primary=is_primary
        )
        
        db.add(nfc_card)
        db.commit()
        db.refresh(nfc_card)
        
        logger.info(f"Carte NFC créée: {card_uid} pour utilisateur {user_id}")
        return nfc_card
    
    @staticmethod
    def link_card_to_user(
        db: Session,
        card_uid: str,
        card_number: str,
        user_phone: str
    ) -> NFCCard:
        """Lier une carte NFC à un utilisateur via son téléphone"""
        
        # Trouver l'utilisateur par téléphone
        user = db.query(User).filter(User.phone == user_phone).first()
        if not user:
            raise ValueError("Utilisateur introuvable avec ce numéro de téléphone")
        
        # Vérifier que la carte n'est pas déjà liée
        existing_card = db.query(NFCCard).filter(NFCCard.card_uid == card_uid).first()
        if existing_card:
            if existing_card.user_id == user.id:
                return existing_card  # Déjà liée au même utilisateur
            else:
                raise ValueError("Cette carte est déjà liée à un autre utilisateur")
        
        # Créer la liaison
        return NFCCardService.create_nfc_card(
            db=db,
            user_id=user.id,
            card_uid=card_uid,
            card_number=card_number,
            is_primary=len(user.nfc_cards) == 0  # Première carte = principale
        )
    
    @staticmethod
    def get_user_cards(db: Session, user_id: str) -> list[NFCCard]:
        """Récupérer toutes les cartes d'un utilisateur"""
        return db.query(NFCCard).filter(
            NFCCard.user_id == user_id,
            NFCCard.status == "active"
        ).all()
    
    @staticmethod
    def get_card_by_uid(db: Session, card_uid: str) -> Optional[NFCCard]:
        """Récupérer une carte par son UID"""
        return db.query(NFCCard).filter(NFCCard.card_uid == card_uid).first()
    
    @staticmethod
    def update_card_balance(
        db: Session,
        card_uid: str,
        amount: float,
        operation: str = "deposit"
    ) -> NFCCard:
        """Mettre à jour le solde d'une carte"""
        
        card = NFCCardService.get_card_by_uid(db, card_uid)
        if not card:
            raise ValueError("Carte introuvable")
        
        if card.status != "active":
            raise ValueError("Carte inactive")
        
        # Calculer le nouveau solde
        if operation == "deposit":
            new_balance = card.balance + amount
        elif operation == "withdrawal":
            new_balance = card.balance - amount
        else:
            raise ValueError("Opération invalide")
        
        # Vérifier les limites
        if new_balance < 0:
            raise ValueError("Solde insuffisant")
        
        if new_balance > card.max_balance:
            raise ValueError("Limite de solde dépassée")
        
        # Mettre à jour
        card.balance = new_balance
        card.last_used_at = datetime.utcnow()
        
        db.commit()
        db.refresh(card)
        
        logger.info(f"Solde carte {card_uid} mis à jour: {card.balance}")
        return card
    
    @staticmethod
    def write_card_data(
        db: Session,
        card_uid: str,
        card_data: Dict[str, Any],
        device_info: str = None,
        location: str = None
    ) -> NFCCardTransaction:
        """Écrire des données sur une carte NFC"""
        
        card = NFCCardService.get_card_by_uid(db, card_uid)
        if not card:
            raise ValueError("Carte introuvable")
        
        if card.status != "active":
            raise ValueError("Carte inactive")
        
        # Créer l'enregistrement de transaction
        card_transaction = NFCCardTransaction(
            id=str(uuid.uuid4()),
            card_id=card.id,
            transaction_id=str(uuid.uuid4()),  # Transaction temporaire
            operation_type="write",
            card_data=json.dumps(card_data),
            device_info=device_info,
            location=location
        )
        
        db.add(card_transaction)
        db.commit()
        db.refresh(card_transaction)
        
        logger.info(f"Données écrites sur carte {card_uid}")
        return card_transaction
    
    @staticmethod
    def read_card_data(
        db: Session,
        card_uid: str,
        device_info: str = None
    ) -> Dict[str, Any]:
        """Lire les données d'une carte NFC"""
        
        card = NFCCardService.get_card_by_uid(db, card_uid)
        if not card:
            raise ValueError("Carte introuvable")
        
        if card.status != "active":
            raise ValueError("Carte inactive")
        
        # Créer l'enregistrement de lecture
        card_transaction = NFCCardTransaction(
            id=str(uuid.uuid4()),
            card_id=card.id,
            transaction_id=str(uuid.uuid4()),
            operation_type="read",
            device_info=device_info
        )
        
        db.add(card_transaction)
        db.commit()
        
        # Retourner les données de la carte
        return {
            "card_uid": card.card_uid,
            "card_number": card.card_number,
            "user_id": card.user_id,
            "balance": card.balance,
            "max_balance": card.max_balance,
            "status": card.status,
            "is_primary": card.is_primary,
            "last_used_at": card.last_used_at
        }
    
    @staticmethod
    def process_nfc_transaction(
        db: Session,
        card_uid: str,
        amount: float,
        transaction_type: str,
        merchant_name: str = None,
        location: str = None,
        device_info: str = None
    ) -> Transaction:
        """Traiter une transaction NFC"""
        
        card = NFCCardService.get_card_by_uid(db, card_uid)
        if not card:
            raise ValueError("Carte introuvable")
        
        if card.status != "active":
            raise ValueError("Carte inactive")
        
        # Créer la transaction
        transaction = Transaction(
            id=str(uuid.uuid4()),
            user_id=card.user_id,
            transaction_type=TransactionType.NFC_DEPOSIT if transaction_type == "deposit" else TransactionType.NFC_WITHDRAWAL,
            amount=amount,
            status=TransactionStatus.PENDING,
            description=f"Transaction NFC - {transaction_type}",
            reference=f"NFC-{str(uuid.uuid4())[:8]}",
            merchant_name=merchant_name,
            location=location,
            device_info=device_info
        )
        
        db.add(transaction)
        db.commit()
        db.refresh(transaction)
        
        # Créer l'enregistrement de transaction carte
        card_transaction = NFCCardTransaction(
            id=str(uuid.uuid4()),
            card_id=card.id,
            transaction_id=transaction.id,
            operation_type="update_balance",
            device_info=device_info,
            location=location
        )
        
        db.add(card_transaction)
        db.commit()
        
        logger.info(f"Transaction NFC traitée: {transaction.id}")
        return transaction
    
    @staticmethod
    def unlink_card(
        db: Session,
        card_uid: str,
        reason: str = None
    ) -> bool:
        """Délier une carte NFC"""
        
        card = NFCCardService.get_card_by_uid(db, card_uid)
        if not card:
            raise ValueError("Carte introuvable")
        
        # Marquer la carte comme inactive
        card.status = "inactive"
        card.updated_at = datetime.utcnow()
        
        db.commit()
        
        logger.info(f"Carte {card_uid} déliée. Raison: {reason}")
        return True
