#!/usr/bin/env node
// Serveur WebSocket simple pour recevoir les logs temps réel

const WebSocket = require('ws');
const fs = require('fs');
const path = require('path');
const pino = require('pino');
const http = require('http');
const url = require('url');
const crypto = require('crypto');
const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));

const wsPort = 8082;
const httpPort = 8083;

// Configuration du logger Pino avec fichier daté
const now = new Date();
const timestamp = now.toISOString().slice(0, 10) + '_' +
                 now.toLocaleTimeString('fr-FR').replace(/:/g, '-');
const logFile = path.join(__dirname, '..', 'logs', `class-generator-${timestamp}.log`);

// Créer le dossier logs s'il n'existe pas
const logsDir = path.join(__dirname, '..', 'logs');
if (!fs.existsSync(logsDir)) {
    fs.mkdirSync(logsDir, { recursive: true });
    console.log('📁 Dossier logs créé');
}

// Logger Pino avec sortie fichier et console
const logger = pino(
    {
        level: 'debug',
        timestamp: pino.stdTimeFunctions.isoTime,
    },
    pino.multistream([
        { stream: pino.destination({ dest: logFile, mkdir: true, sync: false }) },
        { stream: pino.transport({ target: 'pino-pretty', options: { colorize: true, translateTime: 'HH:MM:ss.l' } }) }
    ])
);

// Configuration DigitalOcean
const DO_CONFIG = {
    ACCESS_KEY: 'DO8018LC8QF7CFBF7E2K',
    SECRET_KEY: 'RLH4bUidH4zb1XQAtBUeUnA4vjizdkQ78D1fOZ5gYpk',
    REGION: 'fra1',
    ENDPOINT: 'https://autocollant.fra1.digitaloceanspaces.com',
    BUCKET_ENDPOINT: 'https://fra1.digitaloceanspaces.com',
    BUCKET: 'autocollant',
    CONTENT_PATH: 'Class_generator/ContentMe'
};

// Fonctions AWS Signature
function sha256(message) {
    return crypto.createHash('sha256').update(message, 'utf8').digest('hex');
}

function hmacSha256(key, message) {
    return crypto.createHmac('sha256', key).update(message, 'utf8').digest();
}

async function generateAWSSignature(method, targetUrl) {
    const now = new Date();
    const dateStamp = now.toISOString().slice(0, 10).replace(/-/g, '');
    const timeStamp = now.toISOString().slice(0, 19).replace(/[-:]/g, '') + 'Z';

    const urlObj = new URL(targetUrl);
    const host = urlObj.hostname;
    const canonicalUri = urlObj.pathname;
    const canonicalQueryString = urlObj.search ? urlObj.search.slice(1) : '';

    const canonicalHeaders = `host:${host}\nx-amz-date:${timeStamp}\n`;
    const signedHeaders = 'host;x-amz-date';

    const payloadHash = method === 'GET' ? sha256('') : 'UNSIGNED-PAYLOAD';
    const canonicalRequest = [
        method,
        canonicalUri,
        canonicalQueryString,
        canonicalHeaders,
        signedHeaders,
        payloadHash
    ].join('\n');

    const algorithm = 'AWS4-HMAC-SHA256';
    const credentialScope = `${dateStamp}/${DO_CONFIG.REGION}/s3/aws4_request`;
    const canonicalRequestHash = sha256(canonicalRequest);
    const stringToSign = [
        algorithm,
        timeStamp,
        credentialScope,
        canonicalRequestHash
    ].join('\n');

    const kDate = hmacSha256('AWS4' + DO_CONFIG.SECRET_KEY, dateStamp);
    const kRegion = hmacSha256(kDate, DO_CONFIG.REGION);
    const kService = hmacSha256(kRegion, 's3');
    const kSigning = hmacSha256(kService, 'aws4_request');
    const signature = hmacSha256(kSigning, stringToSign).toString('hex');

    const authorization = `${algorithm} Credential=${DO_CONFIG.ACCESS_KEY}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;

    return {
        'Authorization': authorization,
        'X-Amz-Date': timeStamp,
        'X-Amz-Content-Sha256': payloadHash
    };
}

// Serveur HTTP proxy pour DigitalOcean
const httpServer = http.createServer(async (req, res) => {
    // CORS headers
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type');

    if (req.method === 'OPTIONS') {
        res.writeHead(200);
        res.end();
        return;
    }

    if (req.method === 'GET' && req.url === '/do-proxy/_list') {
        // Route spéciale pour lister tous les fichiers dans le dossier
        logger.info(`📂 DÉBUT - Listing du dossier ContentMe`);

        try {
            // Utiliser l'endpoint du bucket directement
            const listUrl = `${DO_CONFIG.BUCKET_ENDPOINT}/${DO_CONFIG.BUCKET}?list-type=2&prefix=${DO_CONFIG.CONTENT_PATH}/`;
            logger.info(`🌐 List URL: ${listUrl}`);

            const headers = await generateAWSSignature('GET', listUrl);
            logger.info(`📋 Headers générés pour listing: ${JSON.stringify(headers, null, 2)}`);

            const response = await fetch(listUrl, {
                method: 'GET',
                headers: headers
            });

            if (response.ok) {
                const xmlText = await response.text();
                logger.info(`✅ Listing reçu: ${xmlText.length} caractères`);

                // Parser le XML pour extraire les noms de fichiers
                const fileList = parseS3ListResponse(xmlText);

                res.writeHead(200, {
                    'Content-Type': 'application/json',
                    'Access-Control-Allow-Origin': '*'
                });
                res.end(JSON.stringify({ files: fileList }));
                logger.info(`📁 ${fileList.length} fichiers listés`);
            } else {
                const errorText = await response.text();
                logger.error(`❌ Erreur listing: ${response.status} - ${errorText}`);

                // Si c'est un 403, essayer une approche différente
                if (response.status === 403) {
                    logger.info(`🔄 Tentative avec URL alternative...`);
                    // Pour l'instant on retourne l'erreur
                    res.writeHead(403, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
                    res.end(JSON.stringify({
                        error: 'ListBucket permission denied',
                        message: 'Les clés actuelles ne permettent pas le listing. Utilisation de la liste connue.',
                        knownFiles: ['english-class-demo.json', 'sbs-level-7-8-new.json']
                    }));
                } else {
                    res.writeHead(500, { 'Content-Type': 'text/plain', 'Access-Control-Allow-Origin': '*' });
                    res.end('Error listing files');
                }
            }
        } catch (error) {
            logger.error(`💥 Exception listing: ${error.message}`);
            res.writeHead(500, { 'Content-Type': 'text/plain', 'Access-Control-Allow-Origin': '*' });
            res.end(`Error: ${error.message}`);
        }
        return;
    }

    if ((req.method === 'GET' || req.method === 'HEAD') && req.url.startsWith('/do-proxy/')) {
        const filename = req.url.replace('/do-proxy/', '');
        const targetUrl = `${DO_CONFIG.ENDPOINT}/${DO_CONFIG.CONTENT_PATH}/${filename}`;

        logger.info(`🔗 DÉBUT - Proxy ${req.method} request: ${filename}`);
        logger.info(`🌐 Target URL: ${targetUrl}`);
        logger.info(`🔑 ACCESS_KEY: ${DO_CONFIG.ACCESS_KEY}`);
        logger.info(`🔐 SECRET_KEY: ${DO_CONFIG.SECRET_KEY.substring(0, 8)}...`);

        try {
            logger.info(`🔧 Génération de la signature AWS...`);
            const headers = await generateAWSSignature(req.method, targetUrl);
            logger.info(`📋 Headers générés: ${JSON.stringify(headers, null, 2)}`);

            logger.info(`📤 Envoi de la requête à DigitalOcean...`);
            const fetchStart = Date.now();

            const response = await fetch(targetUrl, {
                method: req.method,
                headers: headers
            });

            const fetchDuration = Date.now() - fetchStart;
            logger.info(`⏱️ Durée requête DO: ${fetchDuration}ms`);
            logger.info(`📡 Status DO: ${response.status} ${response.statusText}`);
            logger.info(`📝 Headers réponse DO: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);

            if (response.ok) {
                res.writeHead(200, {
                    'Content-Type': 'application/json',
                    'Access-Control-Allow-Origin': '*'
                });

                if (req.method === 'GET') {
                    const content = await response.text();
                    res.end(content);
                    logger.info(`✅ SUCCÈS - Proxy GET: ${filename} (${content.length} chars)`);
                } else {
                    // HEAD request - no body
                    res.end();
                    logger.info(`✅ SUCCÈS - Proxy HEAD: ${filename} (headers only)`);
                }
            } else {
                const errorText = await response.text();
                logger.error(`❌ ERREUR DO - Status: ${response.status}, Body: ${errorText}`);
                res.writeHead(response.status, {
                    'Content-Type': 'text/plain',
                    'Access-Control-Allow-Origin': '*'
                });
                res.end(errorText);
            }
        } catch (error) {
            logger.error(`💥 EXCEPTION PROXY - Type: ${error.constructor.name}`);
            logger.error(`💥 Message: ${error.message}`);
            logger.error(`💥 Stack: ${error.stack}`);
            res.writeHead(500, {
                'Content-Type': 'text/plain',
                'Access-Control-Allow-Origin': '*'
            });
            res.end(`Error: ${error.message}`);
        }
        return;
    }

    // Route par défaut
    res.writeHead(404, { 'Content-Type': 'text/plain' });
    res.end('Not Found\nUse: /do-proxy/filename.json');
});

httpServer.listen(httpPort, '0.0.0.0', () => {
    logger.info(`🌐 Serveur HTTP proxy démarré sur le port ${httpPort} (toutes interfaces)`);
    logger.info(`🔗 URL proxy: http://localhost:${httpPort}/do-proxy/filename.json`);
    logger.info(`🌍 Accessible depuis Windows: http://localhost:${httpPort}/do-proxy/filename.json`);
});

// Créer le serveur WebSocket sur toutes les interfaces
const wss = new WebSocket.Server({ port: wsPort, host: '0.0.0.0' });

logger.info(`🚀 Serveur WebSocket démarré sur le port ${wsPort}`);
logger.info(`📡 En attente de connexions...`);
logger.info(`📝 Logs enregistrés dans: ${logFile}`);

// Garder trace des clients connectés
const clients = new Set();

wss.on('connection', function connection(ws) {
    logger.info('✅ Nouveau client connecté');
    clients.add(ws);

    // Envoyer un message de bienvenue
    const welcomeMessage = {
        timestamp: new Date().toISOString(),
        level: 'INFO',
        message: '🎉 Connexion WebSocket établie - Logs en temps réel actifs'
    };
    ws.send(JSON.stringify(welcomeMessage));

    // Gérer les messages du client (si nécessaire)
    ws.on('message', function incoming(data) {
        try {
            const message = JSON.parse(data);

            // Enregistrer le log dans le fichier via Pino
            const level = (message.level || 'INFO').toLowerCase();
            switch (level) {
                case 'error':
                    logger.error(message.message);
                    break;
                case 'warn':
                case 'warning':
                    logger.warn(message.message);
                    break;
                case 'debug':
                    logger.debug(message.message);
                    break;
                case 'trace':
                    logger.trace(message.message);
                    break;
                default:
                    logger.info(message.message);
            }

            // DIFFUSER LE LOG À TOUS LES CLIENTS CONNECTÉS !
            broadcastLog(message);
        } catch (error) {
            logger.warn('📨 Message reçu (brut):', data.toString());
        }
    });

    // Nettoyer quand le client se déconnecte
    ws.on('close', function close() {
        logger.info('❌ Client déconnecté');
        clients.delete(ws);
    });

    // Gérer les erreurs
    ws.on('error', function error(err) {
        logger.error('❌ Erreur WebSocket:', err.message);
        clients.delete(ws);
    });
});

// Fonction pour diffuser un log à tous les clients connectés
function broadcastLog(logData) {
    const message = JSON.stringify(logData);
    let sentCount = 0;

    clients.forEach(ws => {
        if (ws.readyState === WebSocket.OPEN) {
            try {
                ws.send(message);
                sentCount++;
            } catch (error) {
                logger.error('❌ Erreur envoi vers client:', error.message);
                clients.delete(ws);
            }
        } else {
            // Nettoyer les connexions fermées
            clients.delete(ws);
        }
    });

    if (sentCount > 0) {
        logger.debug(`📡 Log diffusé à ${sentCount} client(s): [${logData.level}] ${logData.message.substring(0, 50)}${logData.message.length > 50 ? '...' : ''}`);
    }
}

// Parser la réponse XML S3 pour extraire les noms de fichiers
function parseS3ListResponse(xmlText) {
    const files = [];

    // Simple regex pour extraire les clés (noms de fichiers) du XML
    const keyRegex = /<Key>([^<]+)<\/Key>/g;
    let match;

    while ((match = keyRegex.exec(xmlText)) !== null) {
        const fullPath = match[1];
        // Extraire juste le nom du fichier (après le dernier /)
        const filename = fullPath.split('/').pop();

        // Filtrer les fichiers vides et les dossiers
        if (filename && filename.length > 0 && !filename.endsWith('/')) {
            files.push(filename);
        }
    }

    return files;
}

// Export pour utilisation dans d'autres modules
if (typeof module !== 'undefined' && module.exports) {
    module.exports = { broadcastLog, wss, clients };
}

// Gérer l'arrêt propre
process.on('SIGINT', () => {
    logger.info('\n🛑 Arrêt du serveur WebSocket...');

    // Fermer toutes les connexions
    clients.forEach(ws => {
        if (ws.readyState === WebSocket.OPEN) {
            ws.close();
        }
    });

    // Fermer le serveur
    wss.close(() => {
        logger.info('✅ Serveur WebSocket arrêté');
        logger.flush();
        process.exit(0);
    });
});

// Message de test toutes les 30 secondes pour vérifier que ça marche
setInterval(() => {
    if (clients.size > 0) {
        const testMessage = {
            timestamp: new Date().toISOString(),
            level: 'DEBUG',
            message: `💓 Heartbeat - ${clients.size} client(s) connecté(s)`
        };
        broadcastLog(testMessage);
    }
}, 30000);