const path = require('path');
const fs = require('fs');
const cron = require('node-cron');

let db;
let backupDir;
let dbPath;
let cronJob = null;

const DEFAULT_SETTINGS = {
    backup_auto_enabled: 'true',
    backup_auto_time: '03:00',
    backup_retention_days: '30'
};

function init(database, mainDbPath) {
    db = database;
    dbPath = mainDbPath;
    backupDir = path.join(path.dirname(mainDbPath), 'backups');
    
    if (!fs.existsSync(backupDir)) {
        fs.mkdirSync(backupDir, { recursive: true });
    }
    
    initTable();
    initDefaultSettings();
    startScheduler();
}

function initTable() {
    db.run(`CREATE TABLE IF NOT EXISTS backups (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        filename TEXT NOT NULL,
        size INTEGER,
        created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
        type TEXT DEFAULT 'manual',
        restored_at DATETIME
    )`);
}

function initDefaultSettings() {
    Object.entries(DEFAULT_SETTINGS).forEach(([key, value]) => {
        db.run(`INSERT OR IGNORE INTO settings (key, value) VALUES (?, ?)`, [key, value], (err) => {
            if (err) console.error('Backup settings init error:', err);
        });
    });
}

function createBackupFilename() {
    const now = new Date();
    const y = now.getFullYear();
    const m = String(now.getMonth() + 1).padStart(2, '0');
    const d = String(now.getDate()).padStart(2, '0');
    const h = String(now.getHours()).padStart(2, '0');
    const min = String(now.getMinutes()).padStart(2, '0');
    const s = String(now.getSeconds()).padStart(2, '0');
    return `hotel_${y}-${m}-${d}_${h}-${min}-${s}.db`;
}

function createBackup(type = 'manual') {
    return new Promise((resolve, reject) => {
        const filename = createBackupFilename();
        const backupPath = path.join(backupDir, filename);
        
        try {
            fs.copyFileSync(dbPath, backupPath);
            const stats = fs.statSync(backupPath);
            const size = stats.size;
            
            db.run(
                `INSERT INTO backups (filename, size, type) VALUES (?, ?, ?)`,
                [filename, size, type],
                function(err) {
                    if (err) {
                        console.error('[BACKUP] Database insert error:', err);
                        reject(err);
                        return;
                    }
                    
                    console.log(`[BACKUP] Created backup: ${filename} (${formatBytes(size)}) [${type}]`);
                    
                    cleanupOldBackups((cleanupErr) => {
                        if (cleanupErr) {
                            console.warn('[BACKUP] Cleanup warning:', cleanupErr.message);
                        }
                    });
                    
                    resolve({
                        id: this.lastID,
                        filename,
                        size,
                        type,
                        created_at: new Date().toISOString()
                    });
                }
            );
        } catch (err) {
            console.error('[BACKUP] Create backup error:', err);
            reject(err);
        }
    });
}

function listBackups(callback) {
    db.all(`SELECT * FROM backups ORDER BY created_at DESC`, [], (err, rows) => {
        if (err) {
            console.error('[BACKUP] List backups error:', err);
            return callback(err);
        }
        callback(null, rows);
    });
}

function getBackupById(id, callback) {
    db.get(`SELECT * FROM backups WHERE id = ?`, [id], (err, row) => {
        if (err) return callback(err);
        if (!row) return callback(new Error('Backup not found'));
        callback(null, row);
    });
}

function deleteBackup(id) {
    return new Promise((resolve, reject) => {
        getBackupById(id, (err, backup) => {
            if (err) return reject(err);
            
            const backupPath = path.join(backupDir, backup.filename);
            
            try {
                if (fs.existsSync(backupPath)) {
                    fs.unlinkSync(backupPath);
                }
                
                db.run(`DELETE FROM backups WHERE id = ?`, [id], (err) => {
                    if (err) {
                        console.error('[BACKUP] Delete from DB error:', err);
                        return reject(err);
                    }
                    console.log(`[BACKUP] Deleted backup: ${backup.filename}`);
                    resolve();
                });
            } catch (fsErr) {
                console.error('[BACKUP] Delete file error:', fsErr);
                reject(fsErr);
            }
        });
    });
}

function restoreBackup(id) {
    return new Promise((resolve, reject) => {
        getBackupById(id, (err, backup) => {
            if (err) return reject(err);
            
            const backupPath = path.join(backupDir, backup.filename);
            
            if (!fs.existsSync(backupPath)) {
                return reject(new Error('Backup file not found'));
            }
            
            console.log(`[BACKUP] Starting restore from: ${backup.filename}`);
            
            createBackup('emergency_before_restore')
                .then((emergencyBackup) => {
                    console.log(`[BACKUP] Emergency backup created: ${emergencyBackup.filename}`);
                    
                    try {
                        const tempPath = dbPath + '.tmp';
                        
                        if (fs.existsSync(dbPath)) {
                            fs.copyFileSync(dbPath, tempPath);
                        }
                        
                        fs.copyFileSync(backupPath, dbPath);
                        
                        if (fs.existsSync(tempPath)) {
                            fs.unlinkSync(tempPath);
                        }
                        
                        db.run(
                            `UPDATE backups SET restored_at = CURRENT_TIMESTAMP WHERE id = ?`,
                            [id],
                            (updateErr) => {
                                if (updateErr) {
                                    console.warn('[BACKUP] Update restored_at error:', updateErr);
                                }
                            }
                        );
                        
                        console.log(`[BACKUP] Restore completed successfully`);
                        resolve({
                            message: 'Backup restored successfully',
                            restored_backup: backup.filename,
                            emergency_backup: emergencyBackup.filename
                        });
                    } catch (copyErr) {
                        console.error('[BACKUP] Restore copy error:', copyErr);
                        
                        const tempPath = dbPath + '.tmp';
                        if (fs.existsSync(tempPath)) {
                            try {
                                fs.copyFileSync(tempPath, dbPath);
                                fs.unlinkSync(tempPath);
                            } catch (restoreErr) {
                                console.error('[BACKUP] Rollback failed:', restoreErr);
                            }
                        }
                        
                        reject(copyErr);
                    }
                })
                .catch((emergencyErr) => {
                    console.error('[BACKUP] Emergency backup failed, aborting restore:', emergencyErr);
                    reject(new Error('Failed to create emergency backup, restore aborted'));
                });
        });
    });
}

function cleanupOldBackups(callback) {
    db.get(`SELECT value FROM settings WHERE key = 'backup_retention_days'`, [], (err, row) => {
        if (err || !row) {
            return callback(err || new Error('Retention days not set'));
        }
        
        const retentionDays = parseInt(row.value, 10) || 30;
        const cutoffDate = new Date();
        cutoffDate.setDate(cutoffDate.getDate() - retentionDays);
        const cutoffStr = cutoffDate.toISOString();
        
        db.all(`SELECT * FROM backups WHERE created_at < ?`, [cutoffStr], (err, oldBackups) => {
            if (err) return callback(err);
            
            let deleted = 0;
            if (oldBackups.length === 0) return callback(null, 0);
            
            oldBackups.forEach((backup) => {
                const backupPath = path.join(backupDir, backup.filename);
                try {
                    if (fs.existsSync(backupPath)) {
                        fs.unlinkSync(backupPath);
                        deleted++;
                    }
                } catch (e) {
                    console.warn(`[BACKUP] Could not delete ${backup.filename}:`, e.message);
                }
                
                db.run(`DELETE FROM backups WHERE id = ?`, [backup.id], (delErr) => {
                    if (delErr) console.warn(`[BACKUP] DB delete error for ${backup.id}:`, delErr);
                });
            });
            
            if (deleted > 0) {
                console.log(`[BACKUP] Cleaned up ${deleted} old backup(s)`);
            }
            callback(null, deleted);
        });
    });
}

function startScheduler() {
    db.get(`SELECT value FROM settings WHERE key = 'backup_auto_enabled'`, [], (err, row) => {
        const enabled = !err && row && row.value === 'true';
        
        if (!enabled) {
            console.log('[BACKUP] Auto backup is disabled');
            return;
        }
        
        scheduleBackup();
    });
}

function scheduleBackup() {
    if (cronJob) {
        cronJob.stop();
        cronJob = null;
    }
    
    db.get(`SELECT value FROM settings WHERE key = 'backup_auto_time'`, [], (err, row) => {
        const time = (!err && row) ? row.value : '03:00';
        const [hours, minutes] = time.split(':');
        
        const cronExpression = `${minutes} ${hours} * * *`;
        
        cronJob = cron.schedule(cronExpression, async () => {
            console.log('[BACKUP] Running scheduled backup...');
            try {
                const result = await createBackup('auto');
                console.log(`[BACKUP] Scheduled backup completed: ${result.filename}`);
            } catch (err) {
                console.error('[BACKUP] Scheduled backup failed:', err);
            }
        });
        
        console.log(`[BACKUP] Scheduler started: daily at ${time}`);
    });
}

function updateSchedulerSettings() {
    db.get(`SELECT value FROM settings WHERE key = 'backup_auto_enabled'`, [], (err, row) => {
        const enabled = !err && row && row.value === 'true';
        
        if (!enabled) {
            if (cronJob) {
                cronJob.stop();
                cronJob = null;
                console.log('[BACKUP] Scheduler stopped (disabled)');
            }
            return;
        }
        
        scheduleBackup();
    });
}

function getSettings(callback) {
    db.all(`SELECT key, value FROM settings WHERE key LIKE 'backup_%'`, [], (err, rows) => {
        if (err) return callback(err);
        const settings = {};
        rows.forEach(row => { settings[row.key] = row.value; });
        callback(null, settings);
    });
}

function updateSettings(settings, callback) {
    const entries = Object.entries(settings);
    let completed = 0;
    let hasError = false;
    
    entries.forEach(([key, value]) => {
        if (hasError) return;
        
        db.run(
            `INSERT INTO settings (key, value, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP)
             ON CONFLICT(key) DO UPDATE SET value = ?, updated_at = CURRENT_TIMESTAMP`,
            [key, value, value],
            (err) => {
                if (err) {
                    console.error('[BACKUP] Settings update error:', err);
                    hasError = true;
                    callback(err);
                    return;
                }
                completed++;
                if (completed === entries.length) {
                    updateSchedulerSettings();
                    callback(null);
                }
            }
        );
    });
}

function formatBytes(bytes) {
    if (bytes === 0) return '0 B';
    const k = 1024;
    const sizes = ['B', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}

function setupRoutes(app, authenticateToken, requireAdmin) {
    app.post('/api/admin/backup', authenticateToken, requireAdmin, async (req, res) => {
        try {
            const backup = await createBackup('manual');
            res.json({ success: true, backup });
        } catch (err) {
            console.error('[BACKUP] API create backup error:', err);
            res.status(500).json({ error: 'Failed to create backup' });
        }
    });
    
    app.get('/api/admin/backups', authenticateToken, requireAdmin, (req, res) => {
        listBackups((err, backups) => {
            if (err) {
                console.error('[BACKUP] API list backups error:', err);
                return res.status(500).json({ error: 'Failed to list backups' });
            }
            res.json({ backups });
        });
    });
    
    app.delete('/api/admin/backups/:id', authenticateToken, requireAdmin, async (req, res) => {
        try {
            await deleteBackup(parseInt(req.params.id, 10));
            res.json({ success: true });
        } catch (err) {
            console.error('[BACKUP] API delete backup error:', err);
            res.status(500).json({ error: err.message || 'Failed to delete backup' });
        }
    });
    
    app.post('/api/admin/backups/:id/restore', authenticateToken, requireAdmin, async (req, res) => {
        try {
            const result = await restoreBackup(parseInt(req.params.id, 10));
            res.json({ success: true, ...result });
        } catch (err) {
            console.error('[BACKUP] API restore backup error:', err);
            res.status(500).json({ error: err.message || 'Failed to restore backup' });
        }
    });
    
    app.get('/api/admin/backups/:id/download', authenticateToken, requireAdmin, (req, res) => {
        const id = parseInt(req.params.id, 10);
        getBackupById(id, (err, backup) => {
            if (err) return res.status(404).json({ error: 'Backup not found' });
            
            const backupPath = path.join(backupDir, backup.filename);
            if (!fs.existsSync(backupPath)) {
                return res.status(404).json({ error: 'Backup file not found' });
            }
            
            res.download(backupPath, backup.filename);
        });
    });
    
    app.get('/api/admin/backup/settings', authenticateToken, requireAdmin, (req, res) => {
        getSettings((err, settings) => {
            if (err) return res.status(500).json({ error: 'Failed to get settings' });
            res.json(settings);
        });
    });
    
    app.put('/api/admin/backup/settings', authenticateToken, requireAdmin, (req, res) => {
        const { backup_auto_enabled, backup_auto_time, backup_retention_days } = req.body;
        
        const settings = {};
        if (backup_auto_enabled !== undefined) settings.backup_auto_enabled = String(backup_auto_enabled);
        if (backup_auto_time !== undefined) settings.backup_auto_time = backup_auto_time;
        if (backup_retention_days !== undefined) settings.backup_retention_days = String(backup_retention_days);
        
        if (Object.keys(settings).length === 0) {
            return res.status(400).json({ error: 'No settings provided' });
        }
        
        updateSettings(settings, (err) => {
            if (err) return res.status(500).json({ error: 'Failed to update settings' });
            res.json({ success: true });
        });
    });
}

module.exports = {
    init,
    createBackup,
    listBackups,
    deleteBackup,
    restoreBackup,
    getSettings,
    updateSettings,
    setupRoutes
};