"""
Optimisations de base de données pour améliorer les performances.

Ce module contient des fonctions pour créer des index supplémentaires,
optimiser les requêtes et améliorer les performances globales.

Exigences: 14.1, 14.2
"""

import logging
from sqlalchemy import Index, text
from sqlalchemy.orm import Session

logger = logging.getLogger(__name__)


def create_performance_indexes(session: Session) -> None:
    """
    Crée des index supplémentaires pour optimiser les requêtes fréquentes.
    
    Ces index améliorent les performances des requêtes suivantes :
    - Recherche de codes par stay_id et type
    - Recherche de faits cliniques par stay_id et type
    - Recherche d'audit records par stay_id et event_type
    - Recherche de validation issues par severity
    - Recherche de codes par confidence
    
    Args:
        session: Session SQLAlchemy
        
    Exigences: 14.1, 14.2
    """
    logger.info("Création des index de performance...")
    
    # Index composites pour les requêtes fréquentes
    indexes = [
        # Index pour recherche de codes par séjour et type
        Index('idx_codes_stay_type', 'codes.stay_id', 'codes.type'),
        
        # Index pour recherche de codes par séjour et confiance
        Index('idx_codes_stay_confidence', 'codes.stay_id', 'codes.confidence'),
        
        # Index pour recherche de faits par séjour et type
        Index('idx_facts_stay_type', 'clinical_facts.stay_id', 'clinical_facts.type'),
        
        # Index pour recherche d'audit records par séjour et type d'événement
        Index('idx_audit_stay_event', 'audit_records.stay_id', 'audit_records.event_type'),
        
        # Index pour recherche d'audit records par timestamp
        Index('idx_audit_timestamp', 'audit_records.timestamp'),
        
        # Index pour recherche de validation issues par severity
        Index('idx_validation_severity', 'validation_issues.severity'),
        
        # Index pour recherche de validation issues par séjour et severity
        Index('idx_validation_stay_severity', 'validation_issues.stay_id', 'validation_issues.severity'),
        
        # Index pour recherche de questions par séjour et priorité
        Index('idx_questions_stay_priority', 'questions.stay_id', 'questions.priority'),
        
        # Index pour recherche de corrections TIM par séjour
        Index('idx_corrections_stay', 'tim_corrections.stay_id'),
        
        # Index pour recherche de corrections TIM par user_id
        Index('idx_corrections_user', 'tim_corrections.user_id'),
        
        # Index pour recherche de corrections TIM par timestamp
        Index('idx_corrections_timestamp', 'tim_corrections.timestamp'),
        
        # Index pour recherche de documents par séjour et type
        Index('idx_documents_stay_type', 'clinical_documents.stay_id', 'clinical_documents.document_type'),
        
        # Index pour recherche de stays par status
        Index('idx_stays_status', 'stays.status'),
        
        # Index pour recherche de stays par date d'admission et status
        Index('idx_stays_admission_status', 'stays.admission_date', 'stays.status'),
    ]
    
    # Créer les index s'ils n'existent pas déjà
    for index in indexes:
        try:
            # Vérifier si l'index existe déjà
            index_name = index.name
            check_query = text(f"""
                SELECT 1 FROM sqlite_master 
                WHERE type='index' AND name=:index_name
            """)
            result = session.execute(check_query, {"index_name": index_name}).fetchone()
            
            if result is None:
                # Créer l'index
                index.create(session.bind)
                logger.info(f"Index créé: {index_name}")
            else:
                logger.debug(f"Index déjà existant: {index_name}")
                
        except Exception as e:
            logger.warning(f"Erreur lors de la création de l'index {index.name}: {e}")
    
    logger.info("Index de performance créés avec succès")


def optimize_database(session: Session) -> None:
    """
    Optimise la base de données SQLite pour de meilleures performances.
    
    Cette fonction applique plusieurs optimisations SQLite :
    - PRAGMA optimize : Optimise les statistiques des index
    - PRAGMA analysis_limit : Limite l'analyse pour de meilleures performances
    - VACUUM : Compacte la base de données (optionnel, peut être lent)
    
    Args:
        session: Session SQLAlchemy
        
    Exigences: 14.1, 14.2
    """
    logger.info("Optimisation de la base de données...")
    
    try:
        # Optimiser les statistiques des index
        session.execute(text("PRAGMA optimize"))
        
        # Définir la limite d'analyse pour de meilleures performances
        session.execute(text("PRAGMA analysis_limit=1000"))
        
        # Activer le cache de requêtes
        session.execute(text("PRAGMA cache_size=10000"))
        
        # Activer le mode WAL pour de meilleures performances en écriture
        session.execute(text("PRAGMA journal_mode=WAL"))
        
        # Synchronisation normale (bon compromis performance/sécurité)
        session.execute(text("PRAGMA synchronous=NORMAL"))
        
        session.commit()
        logger.info("Base de données optimisée avec succès")
        
    except Exception as e:
        logger.error(f"Erreur lors de l'optimisation de la base de données: {e}")
        session.rollback()


def analyze_query_performance(session: Session, query_sql: str) -> dict:
    """
    Analyse les performances d'une requête SQL.
    
    Utilise EXPLAIN QUERY PLAN pour analyser comment SQLite exécute la requête.
    
    Args:
        session: Session SQLAlchemy
        query_sql: Requête SQL à analyser
        
    Returns:
        Dictionnaire avec les informations d'analyse
        
    Exigences: 14.1, 14.2
    """
    logger.info(f"Analyse de la requête: {query_sql[:100]}...")
    
    try:
        # Exécuter EXPLAIN QUERY PLAN
        explain_query = text(f"EXPLAIN QUERY PLAN {query_sql}")
        result = session.execute(explain_query)
        
        # Collecter les résultats
        plan = []
        for row in result:
            plan.append({
                "id": row[0],
                "parent": row[1],
                "notused": row[2],
                "detail": row[3]
            })
        
        # Analyser le plan pour détecter les problèmes
        issues = []
        for step in plan:
            detail = step["detail"].lower()
            
            # Détecter les scans de table complets
            if "scan table" in detail and "using index" not in detail:
                issues.append(f"Full table scan détecté: {step['detail']}")
            
            # Détecter les scans d'index temporaires
            if "using temporary b-tree" in detail:
                issues.append(f"Index temporaire utilisé: {step['detail']}")
        
        return {
            "query": query_sql,
            "plan": plan,
            "issues": issues,
            "has_issues": len(issues) > 0
        }
        
    except Exception as e:
        logger.error(f"Erreur lors de l'analyse de la requête: {e}")
        return {
            "query": query_sql,
            "error": str(e)
        }


def get_database_statistics(session: Session) -> dict:
    """
    Récupère des statistiques sur la base de données.
    
    Args:
        session: Session SQLAlchemy
        
    Returns:
        Dictionnaire avec les statistiques
        
    Exigences: 14.1, 14.2
    """
    logger.info("Récupération des statistiques de la base de données...")
    
    stats = {}
    
    try:
        # Nombre de séjours
        result = session.execute(text("SELECT COUNT(*) FROM stays"))
        stats["total_stays"] = result.scalar()
        
        # Nombre de documents
        result = session.execute(text("SELECT COUNT(*) FROM clinical_documents"))
        stats["total_documents"] = result.scalar()
        
        # Nombre de faits cliniques
        result = session.execute(text("SELECT COUNT(*) FROM clinical_facts"))
        stats["total_facts"] = result.scalar()
        
        # Nombre de codes
        result = session.execute(text("SELECT COUNT(*) FROM codes"))
        stats["total_codes"] = result.scalar()
        
        # Nombre d'audit records
        result = session.execute(text("SELECT COUNT(*) FROM audit_records"))
        stats["total_audit_records"] = result.scalar()
        
        # Taille de la base de données
        result = session.execute(text("SELECT page_count * page_size as size FROM pragma_page_count(), pragma_page_size()"))
        size_bytes = result.scalar()
        stats["database_size_mb"] = round(size_bytes / (1024 * 1024), 2)
        
        # Nombre d'index
        result = session.execute(text("SELECT COUNT(*) FROM sqlite_master WHERE type='index'"))
        stats["total_indexes"] = result.scalar()
        
        logger.info(f"Statistiques: {stats}")
        return stats
        
    except Exception as e:
        logger.error(f"Erreur lors de la récupération des statistiques: {e}")
        return {"error": str(e)}


def vacuum_database(session: Session) -> None:
    """
    Compacte la base de données avec VACUUM.
    
    ATTENTION: Cette opération peut être lente sur de grandes bases de données.
    Elle doit être exécutée pendant les périodes de faible activité.
    
    Args:
        session: Session SQLAlchemy
        
    Exigences: 14.1, 14.2
    """
    logger.warning("Exécution de VACUUM - cette opération peut être lente...")
    
    try:
        # VACUUM doit être exécuté en dehors d'une transaction
        session.commit()
        session.execute(text("VACUUM"))
        logger.info("VACUUM exécuté avec succès")
        
    except Exception as e:
        logger.error(f"Erreur lors de l'exécution de VACUUM: {e}")
