const express = require('express');
const multer = require('multer');
const { spawn } = require('child_process');
const path = require('path');
const fs = require('fs');

const app = express();
const PORT = process.env.PORT || 3000;

// Ensure directories exist
const UPLOADS_DIR = path.join(__dirname, 'uploads');
const CONVERTED_DIR = path.join(__dirname, 'converted');
if (!fs.existsSync(UPLOADS_DIR)) fs.mkdirSync(UPLOADS_DIR);
if (!fs.existsSync(CONVERTED_DIR)) fs.mkdirSync(CONVERTED_DIR);

// Configure Multer for local uploads
const storage = multer.diskStorage({
    destination: (req, file, cb) => {
        cb(null, UPLOADS_DIR);
    },
    filename: (req, file, cb) => {
        const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
        cb(null, uniqueSuffix + path.extname(file.originalname));
    }
});

const upload = multer({
    storage: storage,
    limits: { fileSize: 1024 * 1024 * 1024 } // 1GB limit
});

// Serve static frontend files
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.json());

// In-memory task tracking
const tasks = new Map();

// File upload endpoint
app.post('/api/upload', upload.single('ostFile'), (req, res) => {
    if (!req.file) {
        return res.status(400).json({ error: 'No file uploaded' });
    }

    const format = req.body.format === 'mbox' ? 'mbox' : 'pst';
    const combineVcf = req.body.combineVcf === 'true';
    const taskId = 'task-' + Date.now() + '-' + Math.round(Math.random() * 1000);
    const originalName = req.file.originalname;
    const baseName = path.basename(originalName, path.extname(originalName)).replace(/[^a-zA-Z0-9_-]/g, '_');
    const outputFileName = `${baseName}-${Date.now()}.${format === 'mbox' ? 'zip' : 'pst'}`;
    const outputPath = path.join(CONVERTED_DIR, outputFileName);

    tasks.set(taskId, {
        id: taskId,
        originalName: originalName,
        inputPath: req.file.path,
        outputPath: outputPath,
        outputFileName: outputFileName,
        format: format,
        combineVcf: combineVcf,
        status: 'uploaded',
        logs: [],
        error: null
    });

    res.json({ taskId });
});

// SSE Streaming endpoint for real-time logs and status
app.get('/api/stream/:taskId', (req, res) => {
    const { taskId } = req.params;
    const task = tasks.get(taskId);

    if (!task) {
        return res.status(404).json({ error: 'Task not found' });
    }

    // Set headers for Server-Sent Events (SSE)
    res.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive'
    });

    res.write(`data: ${JSON.stringify({ status: 'starting', message: 'Starting Java conversion engine...' })}\n\n`);

    task.status = 'converting';

    // Spawn Java conversion process
    const classpath = 'aspose-email-24.12-jdk16.jar:.';
    const child = spawn('java', [
        '-cp', classpath,
        'Convert.java',
        task.inputPath,
        task.outputPath,
        task.format,
        task.combineVcf ? 'true' : 'false'
    ], {
        cwd: __dirname
    });

    const folderStats = [];
    let totalMessages = 0;
    let duration = 0;

    let buffer = '';
    const sendLog = (data) => {
        buffer += data.toString();
        const lines = buffer.split('\n');
        buffer = lines.pop(); // Keep incomplete line

        for (const line of lines) {
            const trimmed = line.trim();
            if (trimmed) {
                task.logs.push(trimmed);

                // Parse stats in real-time
                const folderMatch = trimmed.match(/Processing Folder:\s+(.+)/);
                if (folderMatch) {
                    const fName = folderMatch[1];
                    if (!folderStats.find(item => item.folder === fName)) {
                        folderStats.push({ folder: fName, messages: 0 });
                    }
                }

                const msgMatch = trimmed.match(/Successfully copied\s+(\d+)\s+messages to folder:\s+(.+)/);
                if (msgMatch) {
                    const count = parseInt(msgMatch[1], 10);
                    const fName = msgMatch[2];
                    const found = folderStats.find(item => item.folder === fName);
                    if (found) {
                        found.messages = count;
                    } else {
                        folderStats.push({ folder: fName, messages: count });
                    }
                    totalMessages += count;
                }

                const durationMatch = trimmed.match(/Conversion completed successfully in\s+([\d\.]+)\s+seconds/);
                if (durationMatch) {
                    duration = parseFloat(durationMatch[1]);
                }

                res.write(`data: ${JSON.stringify({ status: 'running', log: trimmed })}\n\n`);
            }
        }
    };

    child.stdout.on('data', sendLog);
    child.stderr.on('data', sendLog);

    child.on('close', (code) => {
        // Handle remaining buffer
        if (buffer.trim()) {
            task.logs.push(buffer.trim());
            res.write(`data: ${JSON.stringify({ status: 'running', log: buffer.trim() })}\n\n`);
        }

        // Clean up uploaded input OST file immediately to save disk space
        try {
            if (fs.existsSync(task.inputPath)) {
                fs.unlinkSync(task.inputPath);
            }
        } catch (err) {
            console.error('Failed to delete input file:', err);
        }

        if (code === 0) {
            task.status = 'completed';
            
            // Send completed state with parsed statistics
            res.write(`data: ${JSON.stringify({
                status: 'completed',
                downloadUrl: `/api/download/${taskId}`,
                fileName: task.outputFileName,
                summary: {
                    folders: folderStats.filter(f => f.messages > 0),
                    totalMessages: totalMessages,
                    duration: duration || 0
                }
            })}\n\n`);
        } else {
            task.status = 'failed';
            task.error = 'Conversion process exited with code ' + code;
            res.write(`data: ${JSON.stringify({ status: 'failed', error: task.error })}\n\n`);
        }
        res.end();
    });

    req.on('close', () => {
        // If client closes connection prematurely, terminate the conversion process
        if (task.status === 'converting') {
            child.kill();
        }
    });
});

// Download endpoint
app.get('/api/download/:taskId', (req, res) => {
    const { taskId } = req.params;
    const task = tasks.get(taskId);

    if (!task || task.status !== 'completed') {
        return res.status(404).send('PST file not found or task not completed.');
    }

    if (!fs.existsSync(task.outputPath)) {
        return res.status(404).send('PST file has expired or was removed.');
    }

    res.setHeader('Content-Type', task.format === 'mbox' ? 'application/zip' : 'application/octet-stream');
    res.setHeader('Content-Disposition', `attachment; filename="${task.outputFileName}"`);
    res.sendFile(task.outputPath, (err) => {
        if (err) {
            console.error('Download error:', err);
        }

        // Clean up output file after download (or set a timeout)
        setTimeout(() => {
            try {
                if (fs.existsSync(task.outputPath)) {
                    fs.unlinkSync(task.outputPath);
                }
                tasks.delete(taskId);
            } catch (cleanupErr) {
                console.error('Failed to clean up output file:', cleanupErr);
            }
        }, 60000); // Wait 1 minute before removing to ensure download succeeds
    });
});

app.listen(PORT, () => {
    console.log(`OST to PST Converter running at http://localhost:${PORT}`);
});
