const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const basicAuth = require('express-basic-auth');
const cookieParser = require('cookie-parser');
const { db, init } = require('./database');

const app = express();
const server = http.createServer(app);
const io = new Server(server, {
    cookie: true
});

// Initialize DB
init();

app.use(cookieParser());

// Rate limit helper using IP and Cookie
// Map: ip -> timestamp
const ipLimits = new Map();

function canClick(req, socket) {
    const ip = req ? req.ip : socket.handshake.address;

    // Check IP limit
    const lastClickTime = ipLimits.get(ip);
    const now = Date.now();

    if (lastClickTime && (now - lastClickTime) < 30000) {
        return false;
    }

    return true;
}

function updateLimit(req, socket) {
    const ip = req ? req.ip : socket.handshake.address;
    ipLimits.set(ip, Date.now());
}

function getGlobalCount() {
    return db.prepare('SELECT count FROM global_count WHERE id = 1').get().count;
}

// Security: Basic Auth for Admin
app.use('/admin.html', basicAuth({
    users: { 'admin': 'admin123' }, // In a real app, use env vars!
    challenge: true
}));

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

// Input Sanitization
function sanitize(str) {
    if (!str) return '';
    return str.replace(/</g, "&lt;").replace(/>/g, "&gt;");
}

io.on('connection', (socket) => {
    // Send current count immediately
    socket.emit('update', { count: getGlobalCount() });

    // Handle increment
    socket.on('increment', (data) => {
        let { name, quote } = data;

        name = sanitize(name);
        quote = sanitize(quote);

        if (!name || name.trim() === "") {
            socket.emit('error', 'Name is required');
            return;
        }

        if (!canClick(null, socket)) {
            socket.emit('error', 'You must wait 30 seconds between clicks.');
            return;
        }

        // Transaction to ensure consistency
        const incrementTx = db.transaction(() => {
            db.prepare('UPDATE global_count SET count = count + 1 WHERE id = 1').run();
            db.prepare('INSERT INTO logs (name, quote) VALUES (?, ?)').run(name, quote || '');
            return getGlobalCount();
        });

        const newCount = incrementTx();

        // Update rate limit
        updateLimit(null, socket);

        // Broadcast new count to EVERYONE
        io.emit('update', { count: newCount });

        io.to('admin').emit('new_log', {
            name,
            quote,
            timestamp: new Date().toISOString()
        });
    });

    // Admin room join
    socket.on('join_admin', () => {
        socket.join('admin');
        // Send recent logs and simplified stats for chart
        const logs = db.prepare('SELECT * FROM logs ORDER BY timestamp DESC LIMIT 50').all();

        // Stats: Last 24 hours logs for chart
        const statsCus = db.prepare(`
            SELECT strftime('%H:00', timestamp) as hour, COUNT(*) as count 
            FROM logs 
            WHERE timestamp >= datetime('now', '-24 hours') 
            GROUP BY hour 
            ORDER BY hour
        `).all();

        socket.emit('admin_data', { logs, stats: statsCus });
    });
});

const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
    console.log(`Server running on http://localhost:${PORT}`);
});
