#!/usr/bin/env python
"""
Script pour résoudre définitivement le problème d'accès à l'administration en local.
Ce script modifie directement les fichiers source pour garantir l'accès à l'administration.
"""
import os
import sys
import shutil
import sqlite3
import getpass
import hashlib
from pathlib import Path
from datetime import datetime

# Couleurs pour les messages console
COLORS = {
    'GREEN': '\033[92m',
    'YELLOW': '\033[93m',
    'RED': '\033[91m',
    'BOLD': '\033[1m',
    'END': '\033[0m'
}

# Fonction pour afficher des messages colorés
def print_colored(message, color):
    print(f"{COLORS[color]}{message}{COLORS['END']}")

def print_header(message):
    print("\n" + "=" * 80)
    print_colored(f" {message.upper()} ", 'BOLD')
    print("=" * 80)

def print_step(step, message):
    print(f"\n{COLORS['BOLD']}[{step}]{COLORS['END']} {message}")

def print_error(message):
    print(f"{COLORS['RED']}ERROR: {message}{COLORS['END']}")

def print_success(message):
    print(f"{COLORS['GREEN']}SUCCESS: {message}{COLORS['END']}")

def print_warning(message):
    print(f"{COLORS['YELLOW']}WARNING: {message}{COLORS['END']}")

# Correction de admin_routes.py pour simplifier la logique d'authentification
def fix_admin_routes():
    print_step("1", "Correction du fichier admin_routes.py...")
    
    admin_routes_file = Path('admin_routes.py')
    if not admin_routes_file.exists():
        print_error("Fichier admin_routes.py non trouvé!")
        return False
    
    # Sauvegarde du fichier original
    backup_file = Path(f"admin_routes.py.bak.{datetime.now().strftime('%Y%m%d%H%M%S')}")
    shutil.copy2(admin_routes_file, backup_file)
    print_success(f"Backup créé: {backup_file}")
    
    # Lire le contenu du fichier
    with open(admin_routes_file, 'r', encoding='utf-8') as f:
        content = f.read()
    
    # Modifications pour l'import de flask-login
    if 'from flask_login import' not in content:
        # Ajouter l'import pour flask-login
        if 'from flask import' in content:
            content = content.replace(
                'from flask import',
                'from flask import\nfrom flask_login import login_user, logout_user, login_required, current_user'
            )
        else:
            content = 'from flask_login import login_user, logout_user, login_required, current_user\n' + content
    
    # Simplifier la route de login pour qu'elle fonctionne toujours
    if 'def login():' in content:
        login_pattern = "def login():"
        login_code = """
def login():
    """Admin login page"""
    from models import User
    form = LoginForm()
    
    if form.validate_on_submit():
        username = form.username.data
        password = form.password.data
        
        user = User.query.filter_by(username=username).first()
        
        if user and user.check_password(password):
            login_user(user)
            session['admin_logged_in'] = True
            session['admin_username'] = user.username
            flash('Connexion réussie!', 'success')
            return redirect(url_for('admin.dashboard'))
        else:
            flash('Nom d\'utilisateur ou mot de passe incorrect.', 'danger')
    
    return render_template('admin/login.html', form=form, title="Administration - Connexion")
"""
        
        # Remplacer la fonction login par la version simplifiée
        content = content.split(login_pattern)
        if len(content) > 1:
            # Trouver la fin de la fonction
            rest_of_content = content[1]
            func_lines = rest_of_content.split('\n')
            
            # Trouver où la fonction se termine
            end_line = 0
            inside_func = False
            for i, line in enumerate(func_lines):
                if i == 0:
                    # Première ligne est toujours dans la fonction
                    inside_func = True
                    continue
                
                # Si ligne vide, continuer
                if not line.strip():
                    continue
                
                # Détecter le niveau d'indentation
                indent = len(line) - len(line.lstrip())
                
                # Si on trouve une ligne sans indentation, c'est la fin de la fonction
                if inside_func and indent == 0:
                    end_line = i
                    break
            
            # Reconstruire le contenu
            if end_line > 0:
                content = content[0] + login_code + '\n'.join(func_lines[end_line:])
            else:
                # Si on n'a pas trouvé la fin, remplacer tout ce qui reste
                content = content[0] + login_code
    
    # Simplifier la route de logout
    if 'def logout():' in content:
        logout_pattern = "def logout():"
        logout_code = """
def logout():
    """Admin logout"""
    logout_user()
    session.pop('admin_logged_in', None)
    session.pop('admin_username', None)
    flash('Déconnexion réussie!', 'success')
    return redirect(url_for('admin.login'))
"""
        
        # Remplacer la fonction logout par la version simplifiée
        content = content.split(logout_pattern)
        if len(content) > 1:
            # Trouver la fin de la fonction
            rest_of_content = content[1]
            func_lines = rest_of_content.split('\n')
            
            # Trouver où la fonction se termine
            end_line = 0
            inside_func = False
            for i, line in enumerate(func_lines):
                if i == 0:
                    # Première ligne est toujours dans la fonction
                    inside_func = True
                    continue
                
                # Si ligne vide, continuer
                if not line.strip():
                    continue
                
                # Détecter le niveau d'indentation
                indent = len(line) - len(line.lstrip())
                
                # Si on trouve une ligne sans indentation, c'est la fin de la fonction
                if inside_func and indent == 0:
                    end_line = i
                    break
            
            # Reconstruire le contenu
            if end_line > 0:
                content = content[0] + logout_code + '\n'.join(func_lines[end_line:])
            else:
                # Si on n'a pas trouvé la fin, remplacer tout ce qui reste
                content = content[0] + logout_code
    
    # Ajouter le décorateur @login_required aux routes d'administration essentielles
    routes_to_protect = [
        'def dashboard():', 
        'def products():', 
        'def categories():', 
        'def services():', 
        'def contacts():', 
        'def users():',
        'def settings():',
        'def pages():',
        'def sections():'
    ]
    
    for route in routes_to_protect:
        if route in content and '@login_required' not in content.split(route)[0].split('\n')[-2]:
            # Trouver où ajouter le décorateur
            route_parts = content.split(route)
            # Chercher la dernière ligne avant la route qui contient un décorateur
            lines_before = route_parts[0].split('\n')
            
            # Trouver l'indice du dernier décorateur
            decorator_indices = [i for i, line in enumerate(lines_before) if '@' in line and 'route' in line]
            
            if decorator_indices:
                last_decorator_idx = max(decorator_indices)
                # Insérer après ce décorateur
                lines_before.insert(last_decorator_idx + 1, '@login_required')
                route_parts[0] = '\n'.join(lines_before)
                content = route.join(route_parts)
    
    # Écrire les modifications
    with open(admin_routes_file, 'w', encoding='utf-8') as f:
        f.write(content)
    
    print_success("admin_routes.py corrigé avec succès.")
    return True

# Mettre à jour app.py pour ajouter l'initialisation de flask-login
def fix_app_py():
    print_step("2", "Correction du fichier app.py...")
    
    app_file = Path('app.py')
    if not app_file.exists():
        print_error("Fichier app.py non trouvé!")
        return False
    
    # Sauvegarde du fichier original
    backup_file = Path(f"app.py.bak.{datetime.now().strftime('%Y%m%d%H%M%S')}")
    shutil.copy2(app_file, backup_file)
    print_success(f"Backup créé: {backup_file}")
    
    # Lire le contenu du fichier
    with open(app_file, 'r', encoding='utf-8') as f:
        content = f.read()
    
    # Ajouter les imports manquants
    if 'from flask_login import LoginManager' not in content:
        if 'from flask_sqlalchemy import SQLAlchemy' in content:
            content = content.replace(
                'from flask_sqlalchemy import SQLAlchemy',
                'from flask_sqlalchemy import SQLAlchemy\nfrom flask_login import LoginManager'
            )
        else:
            # Ajouter au début après les imports existants
            import_end = content.find('\n\n', content.find('import'))
            if import_end > 0:
                content = content[:import_end] + '\nfrom flask_login import LoginManager' + content[import_end:]
            else:
                content = 'from flask_login import LoginManager\n' + content
    
    # Vérifier et ajouter l'initialisation de LoginManager
    if 'login_manager = LoginManager()' not in content:
        if 'db = SQLAlchemy(' in content:
            content = content.replace(
                'db = SQLAlchemy(',
                'login_manager = LoginManager()\ndb = SQLAlchemy('
            )
    
    # Vérifier et ajouter login_manager.init_app
    if 'login_manager.init_app(' not in content:
        if 'db.init_app(app)' in content:
            content = content.replace(
                'db.init_app(app)',
                'db.init_app(app)\n    login_manager.init_app(app)\n    login_manager.login_view = "admin.login"'
            )
    
    # Vérifier et ajouter load_user
    if '@login_manager.user_loader' not in content:
        if 'with app.app_context():' in content:
            user_loader = """
@login_manager.user_loader
def load_user(user_id):
    from models import User
    return User.query.get(int(user_id))
"""
            content = content.replace(
                'with app.app_context():',
                f'{user_loader}\nwith app.app_context():'
            )
    
    # Écrire les modifications
    with open(app_file, 'w', encoding='utf-8') as f:
        f.write(content)
    
    print_success("app.py corrigé avec succès.")
    return True

# Mettre à jour models.py pour ajouter UserMixin
def fix_models_py():
    print_step("3", "Correction du fichier models.py...")
    
    models_file = Path('models.py')
    if not models_file.exists():
        print_error("Fichier models.py non trouvé!")
        return False
    
    # Sauvegarde du fichier original
    backup_file = Path(f"models.py.bak.{datetime.now().strftime('%Y%m%d%H%M%S')}")
    shutil.copy2(models_file, backup_file)
    print_success(f"Backup créé: {backup_file}")
    
    # Lire le contenu du fichier
    with open(models_file, 'r', encoding='utf-8') as f:
        content = f.read()
    
    # Ajouter l'import de UserMixin
    if 'from flask_login import UserMixin' not in content:
        # Ajouter après les imports existants
        import_end = content.find('\n\n', content.find('import'))
        if import_end > 0:
            content = content[:import_end] + '\nfrom flask_login import UserMixin' + content[import_end:]
        else:
            content = 'from flask_login import UserMixin\n' + content
    
    # Vérifier la classe User et ajouter UserMixin si nécessaire
    if 'class User(' in content and 'UserMixin' not in content.split('class User(')[1].split(')')[0]:
        # Modifier la définition de classe
        content = content.replace(
            'class User(db.Model)',
            'class User(UserMixin, db.Model)'
        )
        content = content.replace(
            'class User(db.Model, ',
            'class User(UserMixin, db.Model, '
        )
    
    # Écrire les modifications
    with open(models_file, 'w', encoding='utf-8') as f:
        f.write(content)
    
    print_success("models.py corrigé avec succès.")
    return True

# Créer un nouveau fichier .env ou mettre à jour l'existant
def fix_env_file():
    print_step("4", "Mise à jour du fichier .env...")
    
    env_file = Path('.env')
    env_vars = {
        'FLASK_ENV': 'development',
        'FLASK_DEBUG': '1',
        'SESSION_SECRET': 'gmpl_local_development_secret_key_very_secure_and_long_enough',
        'DATABASE_URL': 'sqlite:///instance/gmpl.db',
        'UPLOAD_FOLDER': 'static/uploads',
        'MAX_CONTENT_LENGTH': '8388608',
        'IMAGE_CACHE_BUSTING': 'true'
    }
    
    if env_file.exists():
        # Lire les variables existantes
        existing_vars = {}
        with open(env_file, 'r') as f:
            for line in f:
                if '=' in line and not line.strip().startswith('#'):
                    key, value = line.strip().split('=', 1)
                    existing_vars[key] = value
        
        # Mettre à jour avec les nouvelles variables
        for key, value in env_vars.items():
            if key not in existing_vars:
                existing_vars[key] = value
        
        # Écrire le fichier mis à jour
        with open(env_file, 'w') as f:
            for key, value in existing_vars.items():
                f.write(f"{key}={value}\n")
    else:
        # Créer un nouveau fichier .env
        with open(env_file, 'w') as f:
            for key, value in env_vars.items():
                f.write(f"{key}={value}\n")
    
    print_success("Fichier .env mis à jour avec succès.")
    return True

# Vérifier/créer un utilisateur admin dans la base de données
def ensure_admin_user():
    print_step("5", "Vérification/création d'un utilisateur admin...")
    
    instance_dir = Path('instance')
    if not instance_dir.exists():
        instance_dir.mkdir(exist_ok=True)
    
    db_file = instance_dir / 'gmpl.db'
    admin_exists = False
    
    if db_file.exists():
        try:
            # Vérifier si l'utilisateur admin existe
            conn = sqlite3.connect(db_file)
            cursor = conn.cursor()
            
            # Vérifier si la table user existe
            cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='user'")
            if cursor.fetchone():
                # Vérifier si admin existe
                cursor.execute("SELECT * FROM user WHERE username='admin'")
                if cursor.fetchone():
                    admin_exists = True
                    print_success("Utilisateur admin trouvé dans la base de données.")
            
            conn.close()
        except sqlite3.Error as e:
            print_error(f"Erreur lors de la vérification de la base de données: {e}")
    
    if not admin_exists:
        print_warning("Aucun utilisateur admin trouvé. Création...")
        
        try:
            # Créer l'administrateur via le script create_admin.py
            if Path('create_admin.py').exists():
                import create_admin
                create_admin.create_admin_user()
                print_success("Utilisateur admin créé avec succès (via create_admin.py).")
            elif Path('create_admin_custom.py').exists():
                import create_admin_custom
                create_admin_custom.create_admin_user()
                print_success("Utilisateur admin créé avec succès (via create_admin_custom.py).")
            else:
                print_warning("Scripts create_admin.py/create_admin_custom.py non trouvés.")
                print_warning("Tentative de création directe d'un admin...")
                
                # Créer l'admin directement
                try:
                    # Importer les modules nécessaires
                    from app import app, db
                    from models import User
                    
                    with app.app_context():
                        # Vérifier si l'admin existe déjà
                        admin = User.query.filter_by(username='admin').first()
                        if admin:
                            print_success("Utilisateur admin existe déjà.")
                        else:
                            # Créer l'admin
                            admin = User(username='admin', email='admin@example.com', role='admin')
                            admin.set_password('admin123456')
                            db.session.add(admin)
                            db.session.commit()
                            print_success("Utilisateur admin créé avec succès.")
                except Exception as e:
                    print_error(f"Erreur lors de la création directe de l'admin: {e}")
                    return False
        except Exception as e:
            print_error(f"Erreur lors de la création de l'admin: {e}")
            return False
    
    return True

# Fonction principale
def main():
    print_header("Résolution définitive du problème d'accès à l'administration")
    
    # Étapes de correction
    success = True
    success = fix_admin_routes() and success
    success = fix_app_py() and success
    success = fix_models_py() and success
    success = fix_env_file() and success
    success = ensure_admin_user() and success
    
    if success:
        print_header("Corrections appliquées avec succès")
        print(f"\n{COLORS['GREEN']}Le problème d'accès à l'administration a été résolu avec succès.{COLORS['END']}")
        print("\nPour tester:")
        print(f"1. Exécutez: {COLORS['BOLD']}python run_local_debug.py{COLORS['END']}")
        print(f"2. Accédez à: {COLORS['BOLD']}http://localhost:5000/admin{COLORS['END']}")
        print(f"3. Identifiants: {COLORS['BOLD']}admin / admin123456{COLORS['END']}")
    else:
        print_header("Certaines corrections ont échoué")
        print(f"\n{COLORS['YELLOW']}Des erreurs ont été rencontrées lors des corrections.{COLORS['END']}")
        print("Essayez de lancer quand même le site:")
        print(f"1. Exécutez: {COLORS['BOLD']}python run_local_debug.py{COLORS['END']}")

if __name__ == "__main__":
    main()
