# Fichier: app.py (VERSION POSTGRESQL + FLASK-MIGRATE READY)
import os
import sys
import logging
from datetime import datetime, timedelta
from logging.handlers import RotatingFileHandler
from flask import Flask, redirect, url_for, render_template
from flask_login import current_user
from dotenv import load_dotenv
from flask_migrate import Migrate

# --- FIX DU CHEMIN ---
project_root = os.path.abspath(os.path.dirname(__file__))
if project_root not in sys.path:
    sys.path.append(project_root)

# Importations des modules locaux
from config import Config
from extensions import db, bcrypt, login_manager, mail, talisman, csrf, limiter
from models import User

# Importation des Blueprints
from blueprints.auth import auth as auth_bp
from blueprints.main import main as main_bp
from blueprints.audit import audit as audit_bp
from blueprints.admin import admin as admin_bp
from blueprints.manager import manager as manager_bp

# Charge les variables d'environnement
load_dotenv()

def create_app(config_class=Config):
    """
    Fonction usine (Application Factory) pour créer et configurer l'instance de l'application Flask.
    """
    app = Flask(__name__)

    # ✅ AJOUT SÉCURISÉ : Active le ProxyFix UNIQUEMENT en Production
    # Cela permet de récupérer la vraie IP des clients derrière Nginx/AWS/Heroku
    if os.environ.get('FLASK_ENV') == 'production':
        try:
            from werkzeug.middleware.proxy_fix import ProxyFix
            app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1)
            # Pas de print ici pour ne pas polluer les logs, mais c'est actif
        except ImportError:
            pass

    app.config.from_object(config_class)

    # Assure que SECRET_KEY est une chaîne
    app.config['SECRET_KEY'] = str(app.config['SECRET_KEY'])
    
    # Configuration des sessions
    app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=2)

    # ========================================================================
    # INITIALISATION DES EXTENSIONS
    # ========================================================================
    db.init_app(app)
    bcrypt.init_app(app)
    
    # ✅ FLASK-MIGRATE : Initialisation après db.init_app()
    migrate = Migrate(app, db)
    
    # Configuration Login Manager
    login_manager.init_app(app)
    login_manager.login_view = 'auth.login'
    login_manager.login_message = "Veuillez vous connecter pour accéder à cette page."
    login_manager.login_message_category = 'info'
    login_manager.session_protection = 'strong'
    
    mail.init_app(app)
    csrf.init_app(app)  # Protection CSRF
    limiter.init_app(app)  # Rate Limiting
    
    # ========================================================================
    # TALISMAN : SÉCURITÉ PRODUCTION (CSP AJUSTÉE)
    # ========================================================================
    if app.config.get('FLASK_ENV') == 'production':
        csp = {
            'default-src': ["'self'"],
            'script-src': [
                "'self'", 
                "'unsafe-inline'",  # Requis pour Alpine.js dans les attributs HTML
                "'unsafe-eval'",    # Requis pour Alpine.js
                'https://cdn.tailwindcss.com',
                'https://cdn.jsdelivr.net',
                'https://unpkg.com',
                'https://cdnjs.cloudflare.com'
            ],
            'style-src': [
                "'self'", 
                "'unsafe-inline'", # Requis pour Tailwind CDN
                'https://cdn.tailwindcss.com', 
                'https://fonts.googleapis.com',
            ],
            'font-src': ["'self'", 'https://fonts.gstatic.com', 'data:'],
            'img-src': ["'self'", 'data:', 'https:'],
            'connect-src': ["'self'"],
            'object-src': ["'none'"],
            'frame-ancestors': ["'none'"]
        }
        
        talisman.init_app(
            app, 
            content_security_policy=csp, 
            force_https=True,
            strict_transport_security=True,
            strict_transport_security_max_age=31536000,
            session_cookie_secure=True,
            session_cookie_samesite='Lax',
            x_content_type_options=True,
            x_xss_protection=True,
            referrer_policy='strict-origin-when-cross-origin'
        )
        app.logger.info("🔒 Talisman activé (mode production)")
    else:
        talisman.init_app(app, content_security_policy=None, force_https=False)
        app.logger.warning("⚠️  Mode DEV : Talisman permissif")

    # ========================================================================
    # LOGGING PRODUCTION
    # ========================================================================
    if not app.debug and not app.testing:
        if not os.path.exists('logs'):
            try:
                os.mkdir('logs')
            except OSError:
                pass
        
        if os.path.exists('logs') and os.access('logs', os.W_OK):
            file_handler = RotatingFileHandler(
                'logs/mcyber.log', maxBytes=10240000, backupCount=10
            )
            file_handler.setFormatter(logging.Formatter(
                '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
            ))
            file_handler.setLevel(logging.INFO)
            app.logger.addHandler(file_handler)
        
        stream_handler = logging.StreamHandler()
        stream_handler.setLevel(logging.INFO)
        app.logger.addHandler(stream_handler)
        
        app.logger.setLevel(logging.INFO)
        app.logger.info('MCyber Audit startup')

    # ========================================================================
    # GESTION DES ERREURS
    # ========================================================================
    @app.errorhandler(404)
    def page_not_found(e):
        return render_template('errors/404.html'), 404
    
    @app.errorhandler(403)
    def forbidden(e):
        return render_template('errors/403.html'), 403
    
    @app.errorhandler(500)
    def internal_error(e):
        db.session.rollback()
        app.logger.error(f'Erreur 500: {e}')
        return render_template('errors/500.html'), 500
    
    @app.errorhandler(429)
    def ratelimit_handler(e):
        return render_template('errors/429.html'), 429

    @app.context_processor
    def inject_global_variables():
        """Injecte des variables globales dans tous les templates"""
        context = {'datetime': datetime}
        
        # Injecter le nombre d'erreurs non résolues pour les admins
        if current_user.is_authenticated and current_user.role == 'admin':
            from models import AuditLog
            try:
                unresolved_count = AuditLog.query.filter(
                    AuditLog.level.in_(['ERROR', 'CRITICAL']),
                    AuditLog.resolved == False
                ).count()
                context['unresolved_errors_count'] = unresolved_count
            except Exception as e:
                app.logger.error(f"Erreur comptage erreurs non résolues: {e}")
                context['unresolved_errors_count'] = 0
        else:
            context['unresolved_errors_count'] = 0
        
        return context

    # ========================================================================
    # USER LOADER (Flask-Login)
    # ========================================================================
    @login_manager.user_loader
    def load_user(user_id):
        """Charge un utilisateur depuis son ID pour Flask-Login."""
        if user_id is not None:
            return User.query.get(user_id)
        return None

    # ========================================================================
    # CRÉATION BASE DE DONNÉES & ADMIN
    # ========================================================================
    with app.app_context():
        if app.config.get('FLASK_ENV') != 'production':
            migrations_dir = os.path.join(project_root, 'migrations')
            
            if not os.path.exists(migrations_dir):
                app.logger.warning("⚠️  Aucune migration détectée. Utilisez 'flask db init' puis 'flask db migrate'")
            else:
                app.logger.info("📦 Migrations détectées. Utilisez 'flask db upgrade' pour mettre à jour.")
        else:
            app.logger.info("📦 Production : Utilisez 'flask db upgrade' pour gérer la base de données")
        
        # Création conditionnelle de l'admin
        admin_email = app.config.get('ADMIN_EMAIL')
        admin_password_raw = app.config.get('ADMIN_PASSWORD')

        if admin_email and admin_password_raw:
            try:
                existing_admin = User.query.filter_by(email=admin_email).first()
                if existing_admin is None:
                    hashed_password = bcrypt.generate_password_hash(admin_password_raw).decode('utf-8')
                    admin_user = User(
                        email=admin_email, 
                        password=hashed_password,
                        role='admin',
                        is_active_account=True
                    )
                    db.session.add(admin_user)
                    db.session.commit()
                    app.logger.info(f"✅ Admin par défaut créé : {admin_email}")
                else:
                    app.logger.info(f"ℹ️  Admin déjà existant : {admin_email}")
            except Exception as e:
                app.logger.error(f"❌ Erreur création admin : {e}")
                db.session.rollback()

    # ========================================================================
    # RENOUVELLEMENT AUTOMATIQUE DE SESSION
    # ========================================================================
    
    from flask import session
    
    @app.before_request
    def refresh_session():
        """Renouvelle la session à chaque requête pour éviter l'expiration."""
        if current_user.is_authenticated:
            session.permanent = True
            session.modified = True

    # ========================================================================
    # ENREGISTREMENT DES BLUEPRINTS
    # ========================================================================
    app.register_blueprint(auth_bp)
    app.register_blueprint(main_bp)
    app.register_blueprint(audit_bp)
    app.register_blueprint(admin_bp)
    app.register_blueprint(manager_bp)
    
    return app