"""
Client PRONOTE pour GMAO Collège
Utilise la bibliothèque pronotepy pour se connecter à Pronote via QR Code
"""
import json
import logging
import uuid
from datetime import datetime, timezone, timedelta
from typing import Optional, Dict, Any

# Import de pronotepy bundlée - la classe s'appelle 'Client'
# VieScolaireClient pour les établissements scolaires
from .pronotepy import Client as PronoteClient, VieScolaireClient

logger = logging.getLogger(__name__)

# Client global en mémoire
_pronote_client: Optional[PronoteClient] = None
_pronote_session: Optional[Dict[str, Any]] = None


def _get_or_create_device_uuid() -> str:
    """
    Récupère l'UUID stocké en base ou en génère un nouveau.
    L'UUID doit rester constant pour que PRONOTE reconnaisse l'appareil.
    Ne crée PAS de session partielle - juste retourne l'UUID.
    """
    try:
        from app_new.pronote.models import PronoteSession
        
        session = PronoteSession.get()
        if session and session.device_uuid:
            logger.info(f"UUID existant récupéré: {session.device_uuid}")
            return session.device_uuid
        
        # Générer un nouvel UUID (sera sauvegardé lors de la connexion)
        new_uuid = str(uuid.uuid4())
        logger.info(f"Nouveau UUID généré: {new_uuid}")
        return new_uuid
        
    except Exception as e:
        logger.error(f"Erreur récupération UUID: {e}")
        # Fallback: générer un UUID temporaire
        return str(uuid.uuid4())


def connect_with_qr(qr_code_json: str, pin: str, account_pin: Optional[str] = None) -> Optional[PronoteClient]:
    """
    Connecte à Pronote via un QR Code JSON.
    
    IMPORTANT: Une connexion QR est considérée comme un "nouvel appareil" par PRONOTE.
    On génère donc TOUJOURS un nouvel UUID pour chaque connexion QR.
    
    Args:
        qr_code_json: JSON du QR code (string ou dict)
        pin: PIN du QR Code (4 chiffres)
        account_pin: PIN du compte si différent du PIN QR
    
    Returns:
        PronoteClient si connexion réussie, None sinon
    """
    global _pronote_client, _pronote_session
    
    try:
        # Parser le JSON si c'est une string
        if isinstance(qr_code_json, str):
            qr_data = json.loads(qr_code_json)
        else:
            qr_data = qr_code_json
        
        # Vérifier les clés requises
        required_keys = ['login', 'jeton', 'url']
        if not all(k in qr_data for k in required_keys):
            raise ValueError("Le QR Code doit contenir les clés: login, jeton, url")
        
        # IMPORTANT: Pour une connexion QR, PRONOTE attend un NOUVEL UUID
        # (c'est un "nouvel appareil" pour lui)
        device_uuid = str(uuid.uuid4())
        logger.info(f"Nouvel UUID généré pour connexion QR: {device_uuid}")
        
        # Utiliser le PIN du compte si fourni, sinon le PIN QR
        effective_pin = account_pin if account_pin else pin
        
        # Appliquer la méthode de classe qrcode_login
        _pronote_client = PronoteClient.qrcode_login(
            qr_code=qr_data,
            pin=pin,
            uuid=device_uuid,
            account_pin=effective_pin,
            device_name='GMAO College'
        )
        
        # Stocker les infos de session
        # Utiliser client.password directement (comme l'ancien code)
        _pronote_session = {
            'url': qr_data.get('url'),
            'token': _pronote_client.password,  # Token en clair direct
            'pin': pin,
            'account_pin': account_pin,
            'device_uuid': device_uuid,
            'connected_at': datetime.now(timezone.utc)
        }
        
        # Sauvegarder en base
        save_session_to_db(_pronote_session)
       
        logger.info(f"Connexion PRONOTE réussie avec UUID: {device_uuid}")
        return _pronote_client
        
    except Exception as e:
        logger.error(f"Erreur connexion PRONOTE: {e}")
        _pronote_client = None
        _pronote_session = None
        return None


def connect_with_credentials(url: str, username: str, password: str, account_pin: Optional[str] = None) -> Optional['PronoteClient']:
    """
    Connecte à Pronote via identifiants classiques.
    
    Args:
        url: URL du serveur Pronote
        username: Nom d'utilisateur
        password: Mot de passe
        account_pin: PIN du compte (optionnel)
    
    Returns:
        Client si connexion réussie, None sinon
    """
    global _pronote_client, _pronote_session
    
    try:
        from .pronotepy import VieScolaireClient
        
        _pronote_client = VieScolaireClient(
            pronote_url=url,
            username=username,
            password=password,
            mode="token",
            uuid=str(uuid.uuid4()),
            account_pin=account_pin
        )
        
        # Récupérer le token pour sauvegarde
        token = _pronote_client.password if hasattr(_pronote_client, 'password') else None
        device_uuid = str(_pronote_client.uuid) if hasattr(_pronote_client, 'uuid') else str(uuid.uuid4())
        
        _pronote_session = {
            'url': url,
            'username': username,
            'token': token,
            'device_uuid': device_uuid,
            'account_pin': account_pin,
            'connected_at': datetime.now(timezone.utc)
        }
        
        # Sauvegarder en base
        save_session_to_db(_pronote_session)
        
        logger.info(f"Connexion PRONOTE réussie pour {username}")
        return _pronote_client
        
    except Exception as e:
        logger.error(f"Erreur connexion PRONOTE: {e}")
        return None


def get_client() -> Optional['PronoteClient']:
    """
    Retourne le client PRONOTE actuel s'il existe.
    Tente de reconstituer la session depuis la base si le client est None.
    
    Returns:
        PronoteClient ou None
    """
    global _pronote_client
    
    if _pronote_client is not None:
        return _pronote_client
    
    # Essayer de reconstituer depuis la base
    try:
        from app_new.extensions import db
        from app_new.pronote.models import PronoteSession
        from .pronotepy import VieScolaireClient as PronoteClient
        
        session = PronoteSession.get()
        if session and session.session_token and session.session_url:
            logger.info("Reconstruction session PRONOTE depuis la base")
            # Reconstituer le client avec token_login
            # L'ancien token devrait avoir été sauvegardé avec le UUID utilisé
            token_uuid = session.device_uuid or str(uuid.uuid4())
            _pronote_client = PronoteClient.token_login(
                pronote_url=session.session_url,
                username=session.username,
                password=session.session_token,
                uuid=token_uuid,
                device_name='GMAO College',
                account_pin=session.account_pin  # Utiliser le PIN de la base
            )
            
            # Sauvegarder le nouveau token après reconnexion (le token peut tourner)
            if _pronote_client:
                new_token = _pronote_client.password if hasattr(_pronote_client, 'password') else None
                if new_token and new_token != session.session_token:
                    logger.info("Mise à jour du token après reconnexion")
                    session.session_token = new_token
                    db.session.commit()
            
            return _pronote_client
    except Exception as e:
        logger.warning(f"Impossible de reconstituer session: {e}")
    
    return None


def get_session() -> Optional[Dict[str, Any]]:
    """
    Retourne les informations de session actuelles.
    
    Returns:
        Dict avec infos de session ou None
    """
    return _pronote_session


def disconnect():
    """Déconnecte le client PRONOTE."""
    global _pronote_client, _pronote_session
    _pronote_client = None
    _pronote_session = None
    logger.info("Déconnexion PRONOTE")


def is_connected() -> bool:
    """Vérifie si un client pronote est connecté."""
    return _pronote_client is not None


def get_all_salles(client) -> list:
    """
    Récupère toutes les salles depuis Pronote.
    
    Args:
        client: Client PRONOTE connecté
    
    Returns:
        Liste des salles avec leurs informations
    """
    try:
        # Utiliser l'API PRONOTE pour récupérer les ressources
        result = client.post('RessourceEDT', 16, {})
        ressources = result['dataSec']['data']['listeRessources']['V']
        
        salles = []
        for r in ressources:
            if r.get('G') == 81:  # Type salle = 81
                salles.extend(r.get('liste', {}).get('V', []))
        
        logger.info(f"Récupéré {len(salles)} salles depuis PRONOTE")
        return salles
        
    except Exception as e:
        logger.error(f"Erreur récupération salles: {e}")
        return []


def get_lessons_for_room(client, room_name: str, date_start, date_end, *, strict=False) -> list:
    """Récupère les cours pour une salle donnée en utilisant l'API directe."""
    try:
        # 1. Récupérer les salles
        result = client.post('RessourceEDT', 16, {})
        salles = []
        for r in result['dataSec']['data']['listeRessources']['V']:
            if r.get('G') == 81:
                salles = r.get('liste', {}).get('V', [])
                break
        
        # 2. Trouver l'ID de la salle
        salle_id = None
        for s in salles:
            if s.get('L') == room_name:
                salle_id = s.get('N')
                break
        
        if not salle_id:
            logger.warning(f"Salle {room_name} non trouvée dans Pronote")
            return []
        
        room_lessons = []
        
        # 3. Pour chaque jour de la semaine
        for d in range(7):
            target_date = date_start + timedelta(days=d)
            date_pronote = {"_T": 7, "V": f"{target_date.day}/{target_date.month}/{target_date.year} 0:0:0"}
            
            try:
                resultat = client.post('PageEmploiDuTemps', 16, {
                    "ressource": {"N": salle_id, "G": 17, "L": room_name},
                    "dateDebut": date_pronote,
                    "avecAbsencesEleve": False,
                    "avecConseilDeClasse": True,
                    "avecRetenuesEleve": True,
                    "estEDTPermanence": False,
                    "estEDTAnnuel": False,
                    "avecAbsencesRessource": True,
                    "avecCoursSortiePeda": True,
                    "avecDisponibilites": False,
                    "avecInfosPrefsGrille": True,
                    "edt": {"G": 81, "L": "Salles"},
                    "Ressource": {"N": salle_id, "G": 17, "L": room_name},
                    "DateDebut": date_pronote
                })
            except Exception as api_err:
                logger.warning(f"Erreur API pour {room_name} le {target_date}: {api_err}")
                if strict:
                    raise
                continue
            
            # 4. Parser les cours
            liste_cours = resultat.get('dataSec', {}).get('data', {}).get('ListeCours', [])
            if not liste_cours:
                continue
                
            for c in liste_cours:
                statut = c.get('Statut', 'OK')
                annule = c.get('estAnnule', False)
                
                liste = c.get('ListeContenus', {}).get('V', [])
                matiere = next((x.get('L') for x in liste if x.get('G') == 16), '')
                classe = next((x.get('L') for x in liste if x.get('G') == 1), '')
                prof = next((x.get('L') for x in liste if x.get('G') == 3), '')
                
                # Parser l'heure - format "jj/mm/aaaa HH:MM"
                heure_str = c.get('DateDuCours', {}).get('V', '')
                
                # Calculer start_time et end_time
                start_time = None
                end_time = None
                if ' ' in heure_str:
                    time_part = heure_str.split(' ')[-1]
                    try:
                        hours, minutes = map(int, time_part.split(':')[:2])
                        start_time = f"{hours:02d}:{minutes:02d}"
                        # Durée par défaut 1h, ou utiliser 'duree'
                        duree = c.get('duree', 1)
                        end_hours = hours + duree
                        end_time = f"{end_hours:02d}:{minutes:02d}"
                    except:
                        pass
                
                room_lessons.append({
                    'day_of_week': d,  # 0-6 (lundi=0)
                    'start_time': start_time,
                    'end_time': end_time,
                    'subject': matiere,
                    'teacher': prof,
                    'class_name': classe,
                    'statut': statut,
                    'annule': annule
                })
        
        logger.info(f"Récupéré {len(room_lessons)} cours pour salle {room_name}")
        # Trier par jour puis par heure
        room_lessons.sort(key=lambda x: (x['day_of_week'], x['start_time'] or ''))
        return room_lessons
        
    except Exception as e:
        logger.error(f"Erreur récupération cours pour {room_name}: {e}")
        import traceback
        logger.error(traceback.format_exc())
        if strict:
            raise
        return []


# Fonction pour sauvegarder la session en base
def save_session_to_db(session_data: Dict[str, Any]):
    """
    Sauvegarde les informations de session en base.
    
    Args:
        session_data: Données de session à sauvegarder
    """
    try:
        from app_new.extensions import db
        from app_new.pronote.models import PronoteSession
        
        # Supprimer les anciennes sessions
        PronoteSession.query.delete()
        
        # Créer nouvelle session
        session = PronoteSession(
            session_token=session_data.get('token'),
            session_url=session_data.get('url'),
            username=session_data.get('username'),
            device_uuid=session_data.get('device_uuid'),
            account_pin=session_data.get('account_pin'),
            last_connection=datetime.now(timezone.utc)
        )
        db.session.add(session)
        db.session.commit()
        
        logger.info("Session PRONOTE sauvegardée en base")
        
    except Exception as e:
        logger.error(f"Erreur sauvegarde session: {e}")
        try:
            db.session.rollback()
        except:
            pass


def load_session_from_db() -> Optional[Dict[str, Any]]:
    """
    Charge les informations de session depuis la base.
    
    Returns:
        Dict avec infos de session ou None
    """
    try:
        from app_new.pronote.models import PronoteSession
        
        session = PronoteSession.get()
        if session:
            return {
                'token': session.session_token,
                'url': session.session_url,
                'username': session.username,
                'last_connection': session.last_connection
            }
        return None
        
    except Exception as e:
        logger.error(f"Erreur chargement session: {e}")
        return None
