const express = require('express');
const path = require('path');
const sqlite3 = require('sqlite3').verbose();

const app = express();
const port = 3000;

// Serve static files from the 'public' directory
app.use(express.static(path.join(__dirname, 'public')));

// Serve HTML files from the 'views' directory
app.get('/', (req, res) => {
    res.sendFile(path.join(__dirname, 'views', 'index.html'));
});

app.get('/admin', (req, res) => {
    res.sendFile(path.join(__dirname, 'views', 'admin.html'));
});

// Middleware to parse JSON bodies
app.use(express.json());

// Helper function to create database tables
const createDatabaseTables = (callback) => {
    db.serialize(() => {
        // Enable foreign key support
        db.run('PRAGMA foreign_keys = ON', (err) => {
            if (err) {
                callback(new Error(`Failed to enable foreign keys: ${err.message}`));
                return;
            }

            // Create uids table
            db.run(`CREATE TABLE IF NOT EXISTS uids (
                uid TEXT PRIMARY KEY,
                created_at DATETIME DEFAULT CURRENT_TIMESTAMP
            )`, (err) => {
                if (err) {
                    callback(new Error(`Failed to create uids table: ${err.message}`));
                    return;
                }

                // Create availability table
                db.run(`CREATE TABLE IF NOT EXISTS availability (
                    uid TEXT,
                    date TEXT,
                    times TEXT,
                    PRIMARY KEY (uid, date),
                    FOREIGN KEY (uid) REFERENCES uids(uid) ON DELETE CASCADE
                )`, (err) => {
                    if (err) {
                        callback(new Error(`Failed to create availability table: ${err.message}`));
                        return;
                    }
                    callback(null);
                });
            });
        });
    });
};

// Helper function to initialize database
const initializeDatabase = () => {
    // Ensure db directory exists
    const fs = require('fs');
    const dbDir = 'db';
    if (!fs.existsSync(dbDir)) {
        fs.mkdirSync(dbDir);
    }

    // Connect to SQLite database
    db = new sqlite3.Database('db/freesched.db', (err) => {
        if (err) {
            console.error('Could not connect to database:', err.message);
            return;
        }
        console.log('Connected to SQLite database');
        
        // Create tables
        createDatabaseTables((err) => {
            if (err) {
                console.error('Error creating database tables:', err.message);
                return;
            }
            console.log('Database tables created successfully');
        });
    });
};

// Initialize database
let db;
initializeDatabase();

// Admin view - must be defined before the catch-all UID route
app.get('/admin', (req, res) => {
    res.sendFile(path.join(__dirname, 'views', 'admin.html'));
});

// Public view - handle both root and UID-based routes
app.get(['/', '/:uid'], (req, res) => {
    // Only validate UID if it's provided (not root path)
    if (req.params.uid) {
        // Validate UID format
        if (!/^[a-z0-9-]+$/.test(req.params.uid)) {
            res.sendFile(path.join(__dirname, 'views', 'index.html'));
            return;
        }
    }
    res.sendFile(path.join(__dirname, 'views', 'index.html'));
});

// Helper function to convert time string to minutes for sorting
function timeToMinutes(timeStr) {
    const match = timeStr.match(/([0-9]+)(?::([0-9]+))?(am|pm)/i);
    if (!match) return 0;
    
    let [_, hours, minutes, period] = match;
    hours = parseInt(hours);
    minutes = minutes ? parseInt(minutes) : 0;
    period = period.toLowerCase();

    if (period === 'pm' && hours !== 12) hours += 12;
    if (period === 'am' && hours === 12) hours = 0;
    
    return hours * 60 + minutes;
}

// API endpoint to get available dates and times for a specific UID
app.get('/api/availability/:uid', (req, res) => {
    const { uid } = req.params;
    
    // Verify UID exists
    db.get('SELECT uid FROM uids WHERE uid = ?', [uid], (err, row) => {
        if (err) {
            res.status(500).json({ error: err.message });
            return;
        }
        if (!row) {
            res.status(404).json({ error: 'UID not found' });
            return;
        }
        
        // Get availability for this UID
        db.all('SELECT date, times FROM availability WHERE uid = ?', [uid], (err, rows) => {
            if (err) {
                res.status(500).json({ error: err.message });
                return;
            }
            const availability = {};
            rows.forEach(row => {
                // Handle case where times might be null or undefined
                if (row.times) {
                    // Split, trim, and sort times chronologically
                    const times = row.times.split(',').map(t => t.trim());
                    times.sort((a, b) => timeToMinutes(a) - timeToMinutes(b));
                    availability[row.date] = times;
                } else {
                    availability[row.date] = [];
                }
            });
            res.json(availability);
        });
    });
});

// API endpoint to add/update availability for a specific UID
app.post('/api/availability/:uid', (req, res) => {
    const { uid } = req.params;
    const { date, times } = req.body;

    if (!date) {
        res.status(400).json({ error: 'Date is required' });
        return;
    }

    // Verify UID exists
    db.get('SELECT uid FROM uids WHERE uid = ?', [uid], (err, row) => {
        if (err) {
            res.status(500).json({ error: err.message });
            return;
        }
        if (!row) {
            res.status(404).json({ error: 'UID not found' });
            return;
        }

        if (times.length === 0) {
            // Delete the entry if no times remain
            db.run('DELETE FROM availability WHERE uid = ? AND date = ?', [uid, date], (err) => {
                if (err) {
                    res.status(500).json({ error: err.message });
                    return;
                }
                res.json({ message: 'Availability removed successfully' });
            });
        } else {
            // Sort times before storing
            const sortedTimes = [...times].sort((a, b) => timeToMinutes(a) - timeToMinutes(b));
            
            // Insert or replace with the sorted times
            db.run('INSERT OR REPLACE INTO availability (uid, date, times) VALUES (?, ?, ?)', 
                [uid, date, sortedTimes.join(',')], 
                (err) => {
                    if (err) {
                        res.status(500).json({ error: err.message });
                        return;
                    }
                    res.json({ message: 'Availability updated successfully' });
                }
            );
        }
    });
});



// API endpoint to get all UIDs
app.get('/api/uids', (req, res) => {
    createDatabaseTables((err) => {
        if (err) {
            res.status(500).json({ error: err.message });
            return;
        }

        // Get all UIDs
        db.all('SELECT uid, created_at FROM uids ORDER BY created_at DESC', (err, rows) => {
            if (err) {
                res.status(500).json({ error: `Failed to fetch UIDs: ${err.message}` });
                return;
            }
            res.json(rows || []);
        });
    });
});

// API endpoint to create a new UID
app.post('/api/uids', (req, res) => {
    const { uid } = req.body;

    // Validate UID format (lowercase letters, numbers, and hyphens only)
    if (!uid || !/^[a-z0-9-]+$/.test(uid)) {
        res.status(400).json({ error: 'Invalid UID format. Use only lowercase letters, numbers, and hyphens.' });
        return;
    }

    createDatabaseTables((err) => {
        if (err) {
            res.status(500).json({ error: err.message });
            return;
        }

        db.run('INSERT INTO uids (uid) VALUES (?)', [uid], function(err) {
            if (err) {
                if (err.code === 'SQLITE_CONSTRAINT') {
                    res.status(409).json({ error: 'UID already exists' });
                } else {
                    res.status(500).json({ error: `Failed to insert UID: ${err.message}` });
                }
                return;
            }
            res.status(201).json({ uid, created_at: new Date().toISOString() });
        });
    });
});

// API endpoint to delete a UID and its availability
app.delete('/api/uids/:uid', (req, res) => {
    const { uid } = req.params;

    // Begin transaction
    db.serialize(() => {
        db.run('BEGIN TRANSACTION');

        // Delete from availability first due to foreign key constraint
        db.run('DELETE FROM availability WHERE uid = ?', [uid], (err) => {
            if (err) {
                db.run('ROLLBACK');
                res.status(500).json({ error: err.message });
                return;
            }

            // Then delete from uids
            db.run('DELETE FROM uids WHERE uid = ?', [uid], function(err) {
                if (err) {
                    db.run('ROLLBACK');
                    res.status(500).json({ error: err.message });
                    return;
                }
                if (this.changes === 0) {
                    db.run('ROLLBACK');
                    res.status(404).json({ error: 'UID not found' });
                    return;
                }

                // Commit transaction
                db.run('COMMIT', (err) => {
                    if (err) {
                        db.run('ROLLBACK');
                        res.status(500).json({ error: err.message });
                        return;
                    }
                    res.json({ message: 'UID deleted successfully' });
                });
            });
        });
    });
});

// API endpoint to flush (delete) all availability entries for a specific UID
app.delete('/api/availability/:uid/flush', (req, res) => {
    const { uid } = req.params;

    // Verify UID exists
    db.get('SELECT uid FROM uids WHERE uid = ?', [uid], (err, row) => {
        if (err) {
            res.status(500).json({ error: err.message });
            return;
        }
        if (!row) {
            res.status(404).json({ error: 'UID not found' });
            return;
        }

        // Delete all availability for this UID
        db.run('DELETE FROM availability WHERE uid = ?', [uid], (err) => {
            if (err) {
                console.error('Error deleting availability:', err);
                res.status(500).json({ error: err.message });
                return;
            }
            console.log(`All availability entries for UID ${uid} deleted successfully`);
            res.json({ message: `All availability entries for UID ${uid} deleted successfully` });
        });
    });
});

// API endpoint to reset the entire database
app.post('/api/reset', async (req, res) => {
    const fs = require('fs').promises;
    const path = require('path');
    const dbDir = 'db';
    const dbPath = path.join(dbDir, 'freesched.db');
    
    try {
        // Close current database connection
        await new Promise((resolve, reject) => {
            if (!db) {
                resolve();
                return;
            }
            db.close((err) => {
                if (err) reject(err);
                else resolve();
            });
        });

        // Ensure db directory exists
        try {
            await fs.access(dbDir);
        } catch {
            await fs.mkdir(dbDir);
        }

        // Delete existing database if it exists
        try {
            await fs.unlink(dbPath);
        } catch (err) {
            if (err.code !== 'ENOENT') throw err; // Ignore if file doesn't exist
        }
        
        // Initialize fresh database
        initializeDatabase();

        // Wait for database to be ready
        await new Promise((resolve, reject) => {
            const checkDb = () => {
                if (!db) {
                    setTimeout(checkDb, 100); // Check every 100ms
                    return;
                }
                
                // Test database connection
                db.get('SELECT 1', (err) => {
                    if (err) {
                        setTimeout(checkDb, 100);
                    } else {
                        resolve();
                    }
                });
            };
            checkDb();

            // Set a timeout of 5 seconds
            setTimeout(() => {
                reject(new Error('Database initialization timeout'));
            }, 5000);
        });

        res.json({ message: 'Database completely wiped and recreated successfully' });
    } catch (error) {
        console.error('Error in flush-global:', error);

        // Try to reinitialize database
        try {
            initializeDatabase();
        } catch (reinitError) {
            console.error('Failed to reinitialize database:', reinitError);
        }

        // Ensure we haven't already sent a response
        if (!res.headersSent) {
            res.status(500).json({ error: error.message });
        }
    }
});

app.listen(port, () => {
    console.log(`FreeSched app listening at http://localhost:${port}`);
});