import os
import time
import sqlite3
import psutil
import requests
import subprocess
from datetime import datetime
from threading import Thread
from flask import Flask, render_template, jsonify, request, redirect, url_for, flash, session
import socket
from prometheus_client import start_http_server, Gauge, Counter, generate_latest
from werkzeug.middleware.dispatcher import DispatcherMiddleware
from werkzeug.security import generate_password_hash, check_password_hash
import logging
from logging.handlers import RotatingFileHandler

# Configuration du logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

app = Flask(__name__)
app.secret_key = 'neuropulse_secret_key'
app.config['DATABASE'] = 'monitoring.db'

# Configuration
APACHE_STATUS_URL = "http://localhost/server-status?auto"
REFRESH_INTERVAL = 5  # secondes

# Initialisation de la base de données
def init_db():
    try:
        db = get_db()
        db.execute('''
            CREATE TABLE IF NOT EXISTS servers (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT NOT NULL,
                url TEXT NOT NULL,
                api_key TEXT NOT NULL,
                created_at DATETIME DEFAULT CURRENT_TIMESTAMP
            )
        ''')
        db.execute('''
            CREATE TABLE IF NOT EXISTS metrics (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                server_id INTEGER,
                cpu REAL,
                memory REAL,
                disk REAL,
                busy_workers INTEGER,
                idle_workers INTEGER,
                apache_cpu REAL,
                apache_mem REAL,
                timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
            )
        ''')
        db.execute('''
            CREATE TABLE IF NOT EXISTS users (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                username TEXT UNIQUE NOT NULL,
                password TEXT NOT NULL
            )
        ''')
        db.commit()
        logger.info("Database initialized successfully")
    except Exception as e:
        logger.error(f"Error initializing database: {str(e)}")
        raise

def get_db():
    try:
        if not hasattr(app, 'sqlite_db'):
            app.sqlite_db = sqlite3.connect(app.config['DATABASE'])
            app.sqlite_db.row_factory = sqlite3.Row
            logger.debug("Database connection established")
        return app.sqlite_db
    except sqlite3.Error as e:
        logger.error(f"Database connection error: {str(e)}")
        # Retry logic
        try:
            app.sqlite_db = sqlite3.connect(app.config['DATABASE'])
            app.sqlite_db.row_factory = sqlite3.Row
            return app.sqlite_db
        except:
            raise

@app.teardown_appcontext
def close_db(error):
    if hasattr(app, 'sqlite_db'):
        app.sqlite_db.close()
        logger.debug("Database connection closed")

class ApacheMonitor:
    def __init__(self):
        self.metrics = {
            'apache': {
                'workers': Gauge('apache_workers', 'Apache workers status', ['state']),
                'requests': Counter('apache_total_requests', 'Total Apache requests'),
                'cpu_load': Gauge('apache_cpu_load', 'Apache CPU load'),
                'mem_usage': Gauge('apache_mem_usage', 'Apache memory usage (MB)')
            },
            'system': {
                'cpu': Gauge('system_cpu', 'System CPU usage'),
                'memory': Gauge('system_memory', 'System memory usage'),
                'disk': Gauge('system_disk', 'Disk usage percentage')
            }
        }
        
        self.history = {
            'time': [],
            'workers': {'busy': [], 'idle': []},
            'system': {'cpu': [], 'memory': []},
            'apache': {'cpu': [], 'memory': []}
        }
        
        self.alerts = []
        self.start_time = time.time()
        
        # Démarrer le thread de monitoring
        self.thread = Thread(target=self.monitor_loop)
        self.thread.daemon = True
        self.thread.start()
        logger.info("ApacheMonitor started")

    # ... [Le reste de votre code ApacheMonitor] ...

# Routes principales avec gestion robuste des erreurs
@app.route('/')
def dashboard():
    try:
        logger.info("Accessing dashboard")
        
        # Vérifier la session utilisateur
        if 'user' not in session:
            logger.warning("User not authenticated, redirecting to login")
            return redirect(url_for('login'))
        
        # Récupérer les métriques locales
        local_metrics = {}
        try:
            local_metrics = {
                'cpu': psutil.cpu_percent(),
                'memory': psutil.virtual_memory().percent,
                'disk': psutil.disk_usage('/').percent,
                'apache_status': 'running' if any(p.name() in ['apache2', 'httpd'] for p in psutil.process_iter()) else 'stopped'
            }
            logger.debug(f"Local metrics collected: {local_metrics}")
        except Exception as e:
            logger.error(f"Error collecting local metrics: {str(e)}")
            local_metrics = {
                'cpu': 0,
                'memory': 0,
                'disk': 0,
                'apache_status': 'error'
            }
        
        # Récupérer les serveurs distants
        remote_servers = []
        servers_metrics = {}
        try:
            db = get_db()
            remote_servers = db.execute('SELECT * FROM servers').fetchall()
            logger.debug(f"Found {len(remote_servers)} remote servers")
            
            for server in remote_servers:
                metrics = db.execute(
                    'SELECT * FROM metrics WHERE server_id = ? ORDER BY timestamp DESC LIMIT 1',
                    (server['id'],)
                ).fetchone()
                
                if metrics:
                    servers_metrics[server['id']] = dict(metrics)
        except Exception as e:
            logger.error(f"Error fetching remote servers data: {str(e)}")
            # Fallback to empty data to prevent template errors
            remote_servers = []
            servers_metrics = {}
        
        # Récupérer l'uptime
        uptime = 0
        try:
            if 'monitor' in globals():
                uptime = monitor.get_uptime()
        except:
            pass
        
        return render_template(
            'dashboard.html',
            hostname=socket.gethostname(),
            uptime=uptime,
            remote_servers=remote_servers,
            servers_metrics=servers_metrics,
            local_metrics=local_metrics
        )
    except Exception as e:
        logger.error(f"Critical error in dashboard route: {str(e)}", exc_info=True)
        return render_template('error.html', message="Erreur critique dans le tableau de bord"), 500

@app.route('/api/metrics')
def get_metrics():
    try:
        if 'monitor' not in globals():
            return jsonify({"error": "Monitor not initialized"}), 500
        
        return jsonify({
            'history': monitor.history,
            'alerts': monitor.alerts[-5:][::-1],
            'system': {
                'cpu': psutil.cpu_percent(),
                'memory': psutil.virtual_memory().percent,
                'disk': psutil.disk_usage('/').percent
            }
        })
    except Exception as e:
        logger.error(f"API metrics error: {str(e)}")
        return jsonify({"error": str(e)}), 500

# ... [Les autres routes avec la même gestion d'erreurs] ...

# Création de l'utilisateur par défaut
def create_default_user():
    try:
        db = get_db()
        username = 'admin'
        password = 'admin'
        
        # Vérifier si l'utilisateur existe déjà
        user = db.execute('SELECT * FROM users WHERE username = ?', (username,)).fetchone()
        if not user:
            hashed_password = generate_password_hash(password)
            db.execute('INSERT INTO users (username, password) VALUES (?, ?)', (username, hashed_password))
            db.commit()
            logger.info("Default user created")
    except Exception as e:
        logger.error(f"Error creating default user: {str(e)}")

# Initialisation du monitor
monitor = None

if __name__ == '__main__':
    try:
        logger.info("Starting application")
        init_db()
        create_default_user()
        
        # Démarrer le monitoring
        global monitor
        monitor = ApacheMonitor()
        
        # Vérifier que Apache est en cours d'exécution
        try:
            subprocess.run(["systemctl", "is-active", "--quiet", "apache2"], check=True)
        except (subprocess.CalledProcessError, FileNotFoundError):
            logger.warning("Apache is not running. Attempting to start...")
            try:
                subprocess.run(["sudo", "systemctl", "start", "apache2"], check=True)
                logger.info("Apache started successfully")
            except Exception as e:
                logger.error(f"Failed to start Apache: {str(e)}")
        
        app.run(host='0.0.0.0', port=5000, debug=True)
    except Exception as e:
        logger.critical(f"Failed to start application: {str(e)}", exc_info=True)
        raise