import sqlite3
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify
import psutil
import socket
from datetime import datetime
import requests
import threading
import time
from werkzeug.security import generate_password_hash, check_password_hash

app = Flask(__name__)
app.secret_key = 'votre_clé_secrète_complexe'
app.config['DATABASE'] = 'monitoring.db'

# Configuration initiale
class Config:
    MONITORING_INTERVAL = 10  # secondes
    ADMIN_USERNAME = 'admin'
    ADMIN_PASSWORD = generate_password_hash('admin123')

# Initialisation de la base de données
def init_db():
    with app.app_context():
        db = get_db()
        with app.open_resource('schema.sql', mode='r') as f:
            db.cursor().executescript(f.read())
        db.commit()

def get_db():
    if not hasattr(app, 'sqlite_db'):
        app.sqlite_db = sqlite3.connect(app.config['DATABASE'])
        app.sqlite_db.row_factory = sqlite3.Row
    return app.sqlite_db

@app.teardown_appcontext
def close_db(error):
    if hasattr(app, 'sqlite_db'):
        app.sqlite_db.close()

# Modèle de données
class ServerModel:
    @staticmethod
    def add_server(name, url, api_key, server_type):
        db = get_db()
        db.execute(
            'INSERT INTO servers (name, url, api_key, type, created_at) VALUES (?, ?, ?, ?, ?)',
            (name, url, api_key, server_type, datetime.now())
        )
        db.commit()

    @staticmethod
    def get_all_servers():
        db = get_db()
        return db.execute('SELECT * FROM servers ORDER BY created_at DESC').fetchall()

    @staticmethod
    def delete_server(server_id):
        db = get_db()
        db.execute('DELETE FROM servers WHERE id = ?', (server_id,))
        db.commit()

class MetricsModel:
    @staticmethod
    def save_metrics(server_id, metrics):
        db = get_db()
        db.execute(
            '''INSERT INTO metrics 
            (server_id, cpu, memory, disk, network_sent, network_recv, apache_status, timestamp) 
            VALUES (?, ?, ?, ?, ?, ?, ?, ?)''',
            (server_id, metrics['cpu'], metrics['memory'], metrics['disk'],
             metrics['network_sent'], metrics['network_recv'], 
             metrics.get('apache_status', 'unknown'), datetime.now())
        )
        db.commit()

    @staticmethod
    def get_latest_metrics(server_id, limit=24):
        db = get_db()
        return db.execute(
            '''SELECT * FROM metrics 
            WHERE server_id = ? 
            ORDER BY timestamp DESC 
            LIMIT ?''',
            (server_id, limit)
        ).fetchall()

# Surveillance
class MonitoringService:
    def __init__(self):
        self.running = False
        self.thread = None

    def start(self):
        if not self.running:
            self.running = True
            self.thread = threading.Thread(target=self.monitor_loop)
            self.thread.daemon = True
            self.thread.start()

    def stop(self):
        self.running = False

    def monitor_loop(self):
        while self.running:
            try:
                # Surveiller le serveur local
                local_metrics = self.get_local_metrics()
                MetricsModel.save_metrics(0, local_metrics)  # ID 0 pour le serveur local

                # Surveiller les serveurs distants
                servers = ServerModel.get_all_servers()
                for server in servers:
                    try:
                        response = requests.get(
                            f"{server['url']}/api/metrics",
                            params={'key': server['api_key']},
                            timeout=5
                        )
                        if response.status_code == 200:
                            remote_metrics = self.process_remote_metrics(response.json())
                            MetricsModel.save_metrics(server['id'], remote_metrics)
                    except Exception as e:
                        app.logger.error(f"Error monitoring {server['name']}: {str(e)}")

            except Exception as e:
                app.logger.error(f"Monitoring error: {str(e)}")

            time.sleep(Config.MONITORING_INTERVAL)

    def get_local_metrics(self):
        cpu = psutil.cpu_percent(interval=1)
        mem = psutil.virtual_memory().percent
        disk = psutil.disk_usage('/').percent
        net = psutil.net_io_counters()

        # Vérification Apache
        apache_status = 'stopped'
        for proc in psutil.process_iter(['name']):
            if proc.info['name'] in ('apache2', 'httpd'):
                apache_status = 'running'
                break

        return {
            'cpu': cpu,
            'memory': mem,
            'disk': disk,
            'network_sent': net.bytes_sent,
            'network_recv': net.bytes_recv,
            'apache_status': apache_status
        }

    def process_remote_metrics(self, data):
        return {
            'cpu': data['system']['cpu'],
            'memory': data['system']['memory']['percent'],
            'disk': data['system']['disk']['percent'],
            'network_sent': data['system']['network']['bytes_sent'],
            'network_recv': data['system']['network']['bytes_recv'],
            'apache_status': data['apache']['status']
        }

# Initialisation
monitoring_service = MonitoringService()
monitoring_service.start()

# Routes
@app.route('/')
def dashboard():
    local_metrics = MetricsModel.get_latest_metrics(0, 1)
    servers = ServerModel.get_all_servers()
    server_metrics = {}
    
    for server in servers:
        latest = MetricsModel.get_latest_metrics(server['id'], 1)
        if latest:
            server_metrics[server['id']] = latest[0]

    return render_template(
        'dashboard.html',
        local_metrics=local_metrics[0] if local_metrics else None,
        servers=servers,
        server_metrics=server_metrics,
        hostname=socket.gethostname()
    )

@app.route('/api/metrics/local')
def local_metrics_api():
    latest = MetricsModel.get_latest_metrics(0, 24)
    return jsonify({
        'metrics': [dict(row) for row in latest],
        'hostname': socket.gethostname()
    })

@app.route('/api/metrics/remote/<int:server_id>')
def remote_metrics_api(server_id):
    latest = MetricsModel.get_latest_metrics(server_id, 24)
    server = get_db().execute('SELECT name FROM servers WHERE id = ?', (server_id,)).fetchone()
    return jsonify({
        'metrics': [dict(row) for row in latest],
        'server_name': server['name'] if server else 'Unknown'
    })

@app.route('/admin/servers', methods=['GET', 'POST'])
def manage_servers():
    if request.method == 'POST':
        name = request.form['name']
        url = request.form['url']
        api_key = request.form['api_key']
        server_type = request.form['type']
        
        ServerModel.add_server(name, url, api_key, server_type)
        flash('Serveur ajouté avec succès', 'success')
        return redirect(url_for('manage_servers'))
    
    servers = ServerModel.get_all_servers()
    return render_template('admin/servers.html', servers=servers)

@app.route('/admin/servers/delete/<int:server_id>')
def delete_server(server_id):
    ServerModel.delete_server(server_id)
    flash('Serveur supprimé avec succès', 'success')
    return redirect(url_for('manage_servers'))

@app.route('/admin/login', methods=['GET', 'POST'])
def admin_login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        
        if username == Config.ADMIN_USERNAME and check_password_hash(Config.ADMIN_PASSWORD, password):
            session['admin_logged_in'] = True
            flash('Connexion réussie', 'success')
            return redirect(url_for('manage_servers'))
        else:
            flash('Identifiants incorrects', 'danger')
    
    return render_template('admin/login.html')

@app.route('/admin/logout')
def admin_logout():
    session.pop('admin_logged_in', None)
    flash('Vous avez été déconnecté', 'info')
    return redirect(url_for('dashboard'))

# Fichier schema.sql (à créer)
"""
CREATE TABLE IF NOT EXISTS servers (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    url TEXT NOT NULL,
    api_key TEXT NOT NULL,
    type TEXT NOT NULL,
    created_at DATETIME NOT NULL
);

CREATE TABLE IF NOT EXISTS metrics (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    server_id INTEGER NOT NULL,
    cpu REAL NOT NULL,
    memory REAL NOT NULL,
    disk REAL NOT NULL,
    network_sent INTEGER NOT NULL,
    network_recv INTEGER NOT NULL,
    apache_status TEXT NOT NULL,
    timestamp DATETIME NOT NULL,
    FOREIGN KEY (server_id) REFERENCES servers (id)
);
"""

if __name__ == '__main__':
    init_db()  # À exécuter seulement la première fois
    app.run(host='0.0.0.0', port=5000, debug=True)