const express = require('express');
const router = express.Router();
const db = require('../config/db');
var isWeb = process.env.MODO_ERP === 'web';
const path = require('path');
const fs = require('fs');
var execSync = require('child_process').execSync;
var sql = null;
if (!isWeb) {
    sql = require('mssql');
}

function formatSize(bytes) {
    if (bytes >= 1073741824) return (bytes / 1073741824).toFixed(2) + ' GB';
    if (bytes >= 1048576) return (bytes / 1048576).toFixed(2) + ' MB';
    if (bytes >= 1024) return (bytes / 1024).toFixed(2) + ' KB';
    return bytes + ' B';
}

function getDrivesSync() {
    var drives = [];
    try {
        var output = execSync('wmic logicaldisk get deviceid', { encoding: 'utf8', timeout: 5000 });
        var lines = output.split('\n').filter(function(l) { return /^[A-Z]:/.test(l.trim()); });
        lines.forEach(function(l) {
            var d = l.trim().replace(/[^A-Z:]/gi, '');
            if (d && /^[A-Z]:$/.test(d)) drives.push(d);
        });
    } catch(e) {}
    if (drives.length === 0) {
        for (var c = 65; c <= 90; c++) {
            var drive = String.fromCharCode(c) + ':';
            try { fs.accessSync(drive + '\\'); drives.push(drive); } catch(e) {}
        }
    }
    return drives.sort();
}

router.get('/respaldo/databases', async function(req, res) {
    try {
        if (isWeb) {
            return res.json({ ok: true, data: [process.env.MYSQL_DATABASE || process.env.DB_NAME || 'pfsperu_NexusERP'] });
        }
        var pool = await db.getPool();
        var result = await pool.request().query(
            "SELECT name FROM sys.databases WHERE database_id > 4 ORDER BY name"
        );
        var dbs = result.recordset.map(function(r) { return r.name; });
        res.json({ ok: true, data: dbs });
    } catch (error) {
        console.error('[RESPALDO] Error listar BDs:', error.message);
        try {
            if (isWeb) return res.json({ ok: true, data: [process.env.MYSQL_DATABASE || process.env.DB_NAME || 'NexusERP'] });
            var pool2 = await db.getPool();
            var result2 = await pool2.request().query("EXEC sp_helpdb");
            var dbs2 = result2.recordset.map(function(r) { return r.name; });
            res.json({ ok: true, data: dbs2 });
        } catch (error2) {
            res.status(500).json({ ok: false, mensaje: 'Error al listar bases de datos: ' + error.message });
        }
    }
});

router.post('/respaldo', async function(req, res) {
    try {
        var database = (req.body.database || '').trim();
        var carpeta = (req.body.carpeta || '').trim();
        var nombreArchivo = (req.body.nombre_archivo || '').trim();

        if (!database) return res.status(400).json({ ok: false, mensaje: 'Seleccione una base de datos' });
        if (!carpeta) return res.status(400).json({ ok: false, mensaje: 'Ingrese la ruta de destino' });
        if (!nombreArchivo) return res.status(400).json({ ok: false, mensaje: 'Ingrese el nombre del archivo' });

        nombreArchivo = nombreArchivo.replace(/[<>:"\/\\|?*]/g, '_');

        if (!fs.existsSync(carpeta)) {
            try {
                fs.mkdirSync(carpeta, { recursive: true });
            } catch (e) {
                return res.status(400).json({ ok: false, mensaje: 'No se pudo crear la carpeta: ' + e.message });
            }
        }

        var testFile = path.join(carpeta, '.write_test');
        try {
            fs.writeFileSync(testFile, 'test');
            fs.unlinkSync(testFile);
        } catch (e) {
            return res.status(400).json({ ok: false, mensaje: 'No hay permisos de escritura en la carpeta: ' + carpeta });
        }

        var ext = isWeb ? '.sql' : '.bak';
        if (!nombreArchivo.endsWith(ext)) nombreArchivo += ext;
        var fullPath = path.join(carpeta, nombreArchivo);

        if (fs.existsSync(fullPath)) {
            try { fs.unlinkSync(fullPath); } catch(e) {
                return res.status(400).json({ ok: false, mensaje: 'No se puede sobrescribir el archivo existente. Elija otro nombre.' });
            }
        }

        if (isWeb) {
            var dbUser = process.env.MYSQL_USER || process.env.DB_USER || 'root';
            var dbPass = process.env.MYSQL_PASSWORD || process.env.DB_PASSWORD || '';
            var dbName = database;
            var cmd = 'mysqldump --opt -h ' + (process.env.MYSQL_HOST || process.env.DB_HOST || 'localhost') + ' -u ' + dbUser + ' --password=\'' + dbPass + '\' ' + dbName + ' > "' + fullPath + '"';
            try {
                execSync(cmd, { timeout: 300000, shell: true, stdio: 'pipe' });
            } catch (e) {
                return res.status(500).json({ ok: false, mensaje: 'Error al ejecutar mysqldump: ' + (e.stderr || e.message || 'Compruebe que mysqldump esté disponible') });
            }
        } else {
            var safeDb = database.replace(/]/g, ']]');
            var safePath = fullPath.replace(/'/g, "''");
            var backupQuery = 'BACKUP DATABASE [' + safeDb + '] ' +
                'TO DISK = N\'' + safePath + '\' ' +
                'WITH COMPRESSION, INIT, SKIP, STATS = 10';

            var backupConfig = {
                server: process.env.DB_SERVER,
                port: parseInt(process.env.DB_PORT),
                database: database,
                user: process.env.DB_USER,
                password: process.env.DB_PASSWORD,
                options: {
                    encrypt: process.env.DB_ENCRYPT === 'true',
                    trustServerCertificate: true,
                    enableArithAbort: true
                },
                pool: { max: 1, min: 0 },
                connectionTimeout: 10000,
                requestTimeout: 600000
            };

            var backupPool = new sql.ConnectionPool(backupConfig);
            await backupPool.connect();
            try {
                await backupPool.request().query(backupQuery);
            } finally {
                try { await backupPool.close(); } catch(e) {}
            }

            var maxWait = 30;
            for (var i = 0; i < maxWait; i++) {
                await new Promise(function(resolve) { setTimeout(resolve, 1000); });
                if (fs.existsSync(fullPath)) break;
            }

            if (!fs.existsSync(fullPath)) {
                return res.status(500).json({ ok: false, mensaje: 'El archivo de respaldo no se creó. Verifique permisos del servicio SQL Server.' });
            }
        }

        if (!fs.existsSync(fullPath)) {
            return res.status(500).json({ ok: false, mensaje: 'El archivo de respaldo no se creó.' });
        }

        var stats = fs.statSync(fullPath);

        res.json({
            ok: true,
            mensaje: 'Respaldo generado exitosamente',
            data: {
                archivo: nombreArchivo,
                ruta: fullPath,
                base_datos: database,
                tamano: formatSize(stats.size),
                tamano_bytes: stats.size,
                fecha: stats.mtime.toISOString()
            }
        });
    } catch (error) {
        console.error('[RESPALDO] Error:', error.message);
        var msg = 'Error al generar respaldo: ' + error.message;
        res.status(500).json({ ok: false, mensaje: msg });
    }
});

router.post('/respaldo/restore', async function(req, res) {
    try {
        var database = (req.body.database || '').trim();
        var archivo = (req.body.archivo || '').trim();

        if (!database) return res.status(400).json({ ok: false, mensaje: 'Seleccione una base de datos' });
        if (!archivo) return res.status(400).json({ ok: false, mensaje: 'Seleccione un archivo de respaldo' });
        if (!fs.existsSync(archivo)) return res.status(400).json({ ok: false, mensaje: 'El archivo no existe: ' + archivo });

        if (isWeb) {
            var dbUser = process.env.MYSQL_USER || process.env.DB_USER || 'root';
            var dbPass = process.env.MYSQL_PASSWORD || process.env.DB_PASSWORD || '';
            var dbName = database;
            var cmd = 'mysql -h ' + (process.env.MYSQL_HOST || process.env.DB_HOST || 'localhost') + ' -u ' + dbUser + ' --password=\'' + dbPass + '\' ' + dbName + ' < "' + archivo + '"';
            try {
                execSync(cmd, { timeout: 600000, shell: true, stdio: 'pipe' });
            } catch (e) {
                return res.status(500).json({ ok: false, mensaje: 'Error al restaurar: ' + (e.stderr || e.message || 'Compruebe que mysql esté disponible') });
            }
            res.json({ ok: true, mensaje: 'Base de datos restaurada exitosamente desde: ' + path.basename(archivo) });
        } else {
            var masterPool = null;
            try {
                var safeDb = database.replace(/]/g, ']]');
                var safePath = archivo.replace(/'/g, "''");

                var masterConfig = {
                    server: process.env.DB_SERVER,
                    port: parseInt(process.env.DB_PORT),
                    database: 'master',
                    user: process.env.DB_USER,
                    password: process.env.DB_PASSWORD,
                    options: {
                        encrypt: process.env.DB_ENCRYPT === 'true',
                        trustServerCertificate: true,
                        enableArithAbort: true
                    },
                    pool: { max: 1, min: 0 },
                    connectionTimeout: 10000,
                    requestTimeout: 300000
                };

                masterPool = new sql.ConnectionPool(masterConfig);
                await masterPool.connect();

                await masterPool.request().query(
                    'IF EXISTS (SELECT 1 FROM sys.databases WHERE name = \'' + safeDb + '\') ' +
                    'ALTER DATABASE [' + safeDb + '] SET SINGLE_USER WITH ROLLBACK IMMEDIATE'
                );

                var restoreQuery = 'RESTORE DATABASE [' + safeDb + '] ' +
                    'FROM DISK = N\'' + safePath + '\' ' +
                    'WITH REPLACE, RECOVERY';
                await masterPool.request().query(restoreQuery);

                await masterPool.request().query('ALTER DATABASE [' + safeDb + '] SET MULTI_USER');

                res.json({ ok: true, mensaje: 'Base de datos restaurada exitosamente desde: ' + path.basename(archivo) });
            } catch (error) {
                console.error('[RESPALDO] Error restore:', error.message);
                try {
                    if (masterPool && masterPool.connected) {
                        var dbSafe = (req.body.database || '').replace(/]/g, ']]');
                        await masterPool.request().query('IF EXISTS (SELECT 1 FROM sys.databases WHERE name = \'' + dbSafe + '\') ALTER DATABASE [' + dbSafe + '] SET MULTI_USER');
                    }
                } catch(e) {}
                var msg = 'Error al restaurar: ' + error.message;
                if (error.message && error.message.includes('Exclusive access')) {
                    msg = 'No se pudo obtener acceso exclusivo. La base de datos está en uso por otra aplicación. Cierre todas las conexiones y vuelva a intentar.';
                }
                return res.status(500).json({ ok: false, mensaje: msg });
            } finally {
                if (masterPool) {
                    try { await masterPool.close(); } catch(e) {}
                }
            }
        }
    } catch (error) {
        return res.status(500).json({ ok: false, mensaje: 'Error al restaurar: ' + (error.message || '') });
    }
});

router.delete('/respaldo/eliminar', async function(req, res) {
    try {
        var archivo = (req.query.archivo || '').trim();
        var carpeta = (req.query.carpeta || '').trim();

        if (!archivo || !carpeta) return res.status(400).json({ ok: false, mensaje: 'Ruta y archivo son requeridos' });

        var fullPath = path.join(carpeta, archivo);
        if (!fs.existsSync(fullPath)) return res.status(404).json({ ok: false, mensaje: 'Archivo no encontrado' });

        fs.unlinkSync(fullPath);
        res.json({ ok: true, mensaje: 'Respaldo eliminado: ' + archivo });
    } catch (error) {
        console.error('[RESPALDO] Error eliminar:', error.message);
        res.status(500).json({ ok: false, mensaje: 'Error al eliminar: ' + error.message });
    }
});

router.get('/respaldo/listar-carpetas', async function(req, res) {
    try {
        var ruta = req.query.ruta || (isWeb ? process.env.HOME || '/home' : 'C:\\');
        var esRoot = /^[A-Z]:\\?$/i.test(ruta.replace(/\\$/, ''));

        var unidades = [];
        try {
            var pool = await db.getPool();
            var result = await pool.request().query("EXEC sp_fixed_drives");
            unidades = result.recordset.map(function(r) { return r.drive + ':'; });
        } catch(e) {}

        if (unidades.length === 0) {
            unidades = getDrivesSync();
        }

        if (!fs.existsSync(ruta)) {
            return res.json({ ok: true, data: [], unidades: unidades, ruta: ruta });
        }
        var stat = fs.statSync(ruta);
        if (!stat.isDirectory()) {
            return res.status(400).json({ ok: false, mensaje: 'La ruta no es una carpeta' });
        }
        var items = fs.readdirSync(ruta, { withFileTypes: true });
        var carpetas = items
            .filter(function(item) { return item.isDirectory() && !item.name.startsWith('$'); })
            .map(function(item) { return item.name; })
            .sort(function(a, b) { return a.localeCompare(b, 'es'); });
        res.json({ ok: true, data: carpetas, unidades: unidades, esRoot: esRoot, ruta: ruta });
    } catch (error) {
        console.error('[RESPALDO] Error listar carpetas:', error.message);
        res.status(500).json({ ok: false, mensaje: 'Error al listar carpetas: ' + error.message });
    }
});

router.get('/respaldo/listar', async function(req, res) {
    try {
        var backupPath = req.query.ruta || process.env.BACKUP_PATH || 'C:\\Backups\\NexusERP';

        if (!fs.existsSync(backupPath)) {
            return res.json({ ok: true, data: [] });
        }

        var ext = isWeb ? '.sql' : '.bak';
        var files = fs.readdirSync(backupPath).filter(function(f) { return f.endsWith(ext); });
        var data = files.map(function(f) {
            var stats = fs.statSync(path.join(backupPath, f));
            return {
                archivo: f,
                tamano: formatSize(stats.size),
                tamano_bytes: stats.size,
                fecha: stats.mtime.toISOString()
            };
        }).sort(function(a, b) { return new Date(b.fecha) - new Date(a.fecha); });

        res.json({ ok: true, data: data });
    } catch (error) {
        console.error('[RESPALDO] Error listar:', error.message);
        res.status(500).json({ ok: false, mensaje: 'Error al listar respaldos' });
    }
});

router.get('/respaldo/descargar', function(req, res) {
    try {
        var archivo = (req.query.archivo || '').trim();
        var carpeta = (req.query.carpeta || '').trim();

        if (!archivo || !carpeta) return res.status(400).json({ ok: false, mensaje: 'Ruta y archivo son requeridos' });

        var fullPath = path.join(carpeta, archivo);
        if (!fs.existsSync(fullPath)) return res.status(404).json({ ok: false, mensaje: 'Archivo no encontrado' });

        res.download(fullPath, archivo);
    } catch (error) {
        console.error('[RESPALDO] Error descargar:', error.message);
        res.status(500).json({ ok: false, mensaje: 'Error al descargar: ' + error.message });
    }
});

router.post('/respaldo/retencion', async function(req, res) {
    try {
        var carpeta = (req.body.carpeta || '').trim();
        var mantener = parseInt(req.body.mantener) || 10;

        if (!carpeta) return res.status(400).json({ ok: false, mensaje: 'Ingrese la ruta de destino' });
        if (!fs.existsSync(carpeta)) return res.status(400).json({ ok: false, mensaje: 'La carpeta no existe' });

        var ext = isWeb ? '.sql' : '.bak';
        var files = fs.readdirSync(carpeta)
            .filter(function(f) { return f.endsWith(ext); })
            .map(function(f) {
                var stats = fs.statSync(path.join(carpeta, f));
                return { archivo: f, fecha: stats.mtime, ruta: path.join(carpeta, f) };
            })
            .sort(function(a, b) { return b.fecha - a.fecha; });

        var eliminados = 0;
        for (var i = mantener; i < files.length; i++) {
            try { fs.unlinkSync(files[i].ruta); eliminados++; } catch(e) {}
        }

        res.json({ ok: true, mensaje: 'Retención aplicada. Eliminados: ' + eliminados + ' respaldos. Mantenidos: ' + Math.min(mantener, files.length) });
    } catch (error) {
        console.error('[RESPALDO] Error retención:', error.message);
        res.status(500).json({ ok: false, mensaje: 'Error al aplicar retención: ' + error.message });
    }
});

module.exports = router;
