require('dotenv').config();

const express = require('express');
const http = require('http');
const WebSocket = require('ws');

const Consommation = require('../models/consommation');

const mongoURI = process.env.MONGO_URI

// Constantes
const rayonSeau = process.env.RAYON_SEAU; // Le rayon du seau en cm
const hauteurInitialeEau = process.env.HAUTEUR_INITIALE_EAU; // la hauteur initiale de l'eau en cm
const distanceSupplementaire = process.env.DISTANCE_SUPPLEMENTAIRE; // la distance supplémentaire en cm entre le capteur et le bord supérieur du seau
let volumeEauInitial = Math.PI * rayonSeau ** 2 * hauteurInitialeEau; // Calcul du volume d'eau initial en cm^3 (mL)
let derniereDistance = distanceSupplementaire;

let methodeNotification = process.env.METHODE_NOTIFICATION;

const app = express();
const port = 3000;

// Créez un serveur HTTP en utilisant Express
const server = http.createServer(app);
// Créez un serveur WebSocket attaché à votre serveur HTTP
const wss = new WebSocket.Server({server});

const mongoose = require('mongoose');

const path = require('path');
const viewsPath = path.join(__dirname, '../views');

module.exports = app;
app.use(express.json());

app.use(express.static('public'));

app.set('views', viewsPath);
app.set('view engine', 'pug');

// Gérez les connexions WebSocket
wss.on('connection', (ws) => {
    console.log('Nouvelle connexion WebSocket établie.');

    // Écoutez les messages du client WebSocket
    ws.on('message', async (message) => {
        if (message instanceof Buffer) {
            message = message.toString('utf-8');
        }

        const now = new Date().toLocaleString();
        console.log(`Message reçu du client: ${message}`);
        console.log(`Date: ${now}`);
        let distanceCapteur = parseInt(message);

        if (distanceCapteur && typeof distanceCapteur === 'number' && distanceCapteur > 0 && distanceCapteur > derniereDistance) {

            const distanceReelle = distanceCapteur - distanceSupplementaire;

            if (distanceReelle > 0) {

                const hauteurEau = hauteurInitialeEau - distanceReelle;
                const volumeEau = Math.PI * rayonSeau ** 2 * hauteurEau;
                if (volumeEauInitial > volumeEau) {
                    const consommationEau = volumeEauInitial - volumeEau;

                    const consom = new Consommation({
                        consommation: consommationEau
                    });

                    await consom.save();

                    volumeEauInitial = volumeEau;
                    derniereDistance = distanceCapteur;
                }
            }
        }

        if (distanceCapteur < derniereDistance) {
            console.log('Le seau est en train d\'être rempli.');
            derniereDistance = distanceCapteur;
            volumeEauInitial = Math.PI * rayonSeau ** 2 * hauteurInitialeEau;
        }
    });

    // Envoyez un message au client WebSocket
    ws.send('Bienvenue sur le serveur WebSocket!');
});

// Connexion à MongoDB
mongoose.connect(mongoURI, {useNewUrlParser: true, useUnifiedTopology: true})
    .then(() => console.log('MongoDB Connected'))
    .catch(err => {
        console.error('MongoDB connection error:', err.message);
    });


app.get('/', (req, res) => {
    res.render('index');
});

app.get('/information-generale', (req, res) => {
    res.render('dashboard');
});

app.get('/graphique-consommations', (req, res) => {
    res.render('chart.pug');
});

app.get('/liste-consommations', (req, res) => {
    res.render('lastConsumptions.pug');
});

app.get('/consommations-par-periode', (req, res) => {
    res.render('consumptionsByPeriod.pug');
});

// Route pour récupérer toutes les consommations
app.get('/consommations', async (req, res) => {
    try {
        const consommations = await Consommation.find();
        res.json(consommations);
    } catch (error) {
        res.status(500).json({message: error.message});
    }
});

// Route pour récupérer les consommations dans une plage de dates spécifiée
app.get('/consommations/:startDate/:endDate', async (req, res) => {
    try {
        const {startDate, endDate} = req.params;

        const start = new Date(startDate);
        const end = new Date(endDate);

        const consommations = await Consommation.find({
            timestamp: {$gte: start, $lte: end}
        });

        res.json(consommations);
    } catch (error) {
        res.status(500).json({message: error.message});
    }
});

// Route pour récupérer les dernières consommations
app.get('/dernieres-consommations', async (req, res) => {
    try {
        const latestConsommations = await Consommation.find().sort({timestamp: -1}).limit(9);
        res.json(latestConsommations);
    } catch (error) {
        res.status(500).json({message: error.message});
    }
});

// Route pour obtenir les consommations par jour
app.get('/consommations/par-jour', async (req, res) => {
    try {
        const consommations = await Consommation.aggregate([
            {
                $group: {
                    _id: {$dateToString: {format: '%Y-%m-%d', date: '$timestamp'}},
                    totalConsommation: {$sum: {$toDouble: '$consommation'}}
                }
            },
            {$sort: {_id: 1}}
        ]);

        res.json(consommations);
    } catch (error) {
        res.status(500).json({message: error.message});
    }
});

// Route pour obtenir les consommations par semaine
app.get('/consommations/par-semaine', async (req, res) => {
    try {
        const consommations = await Consommation.aggregate([
            {
                $group: {
                    _id: {$isoWeek: '$timestamp'},
                    totalConsommation: {$sum: {$toDouble: '$consommation'}}
                }
            },
            {$sort: {_id: 1}}
        ]);

        res.json(consommations);
    } catch (error) {
        res.status(500).json({message: error.message});
    }
});

// Route pour obtenir les consommations par mois
app.get('/consommations/par-mois', async (req, res) => {
    try {
        const consommations = await Consommation.aggregate([
            {
                $group: {
                    _id: {$dateToString: {format: '%Y-%m', date: '$timestamp'}},
                    totalConsommation: {$sum: {$toDouble: '$consommation'}}
                }
            },
            {$sort: {_id: 1}}
        ]);

        res.json(consommations);
    } catch (error) {
        res.status(500).json({message: error.message});
    }
});

// Route pour obtenir les consommations par année
app.get('/consommations/par-annee', async (req, res) => {
    try {
        const consommations = await Consommation.aggregate([
            {
                $group: {
                    _id: {$year: '$timestamp'},
                    totalConsommation: {$sum: {$toDouble: '$consommation'}}
                }
            },
            {$sort: {_id: 1}}
        ]);

        res.json(consommations);
    } catch (error) {
        res.status(500).json({message: error.message});
    }
});

app.get('/parametres-seau', (req, res) => {
    try {
        // Retourne les paramètres du seau
        res.json({
            rayonSeau: rayonSeau,
            hauteurInitialeEau: hauteurInitialeEau,
            distanceSupplementaire: distanceSupplementaire,
            volumeEauInitial: volumeEauInitial
        });
    } catch (error) {
        res.status(500).json({message: error.message});
    }
});

// Route pour obtenir la méthode de notification actuelle
app.get('/methode-notification', async (req, res) => {
    try {
        res.json({methodeNotification: methodeNotification});
    } catch (error) {
        res.status(500).json({message: error.message});
    }
});

// Route pour mettre à jour la méthode de notification
app.put('/methode-notification', async (req, res) => {
    const {notificationMethod} = req.body;

    try {
        if (notificationMethod === "sound") {
            methodeNotification = "sound";
        } else if (notificationMethod === "leds") {
            methodeNotification = "leds";
        }

        wss.clients.forEach(client => {
            if (client.readyState === WebSocket.OPEN) {
                client.send(notificationMethod);
            }
        });

        res.json({message: 'Méthode de notification mise à jour avec succès.'});
    } catch (error) {
        res.status(500).json({message: error.message});
    }
});

server.listen(port, () => {
    console.log(`Server listening at http://localhost:${port}`);
});
