require('dotenv').config();

const express = require('express');
const mongoose = require('mongoose');
const session = require('express-session');
const bcrypt = require('bcrypt');
const path = require('path');

const app = express();
const PORT = process.env.PORT || 3000;
const MONGO_URI = process.env.MONGO_URI || 'mongodb://127.0.0.1:27017/speedDB';
const SESSION_SECRET = process.env.SESSION_SECRET || 'change_me_in_production';
const SALT_ROUNDS = 10;

// ─── Database Connection ──────────────────────────────────────────────────────
mongoose.connect(MONGO_URI).then(async () => {
    console.log('MongoDB connected successfully');
    await seedAdmin();
    seedGames();
}).catch((err) => {
    console.error('MongoDB connection error:', err);
});

// ─── Schemas & Models ─────────────────────────────────────────────────────────
const userSchema = new mongoose.Schema({
    username: { type: String, required: true, unique: true, trim: true },
    password: { type: String, required: true },
    role:     { type: String, enum: ['user', 'admin'], default: 'user' }
});
const User = mongoose.model('User', userSchema);

const gameSchema = new mongoose.Schema({
    name:        { type: String, required: true, trim: true },
    description: { type: String, required: true },
    price:       { type: Number, required: true, min: 0 },
    image:       { type: String, required: true }
});
const Game = mongoose.model('Game', gameSchema);

// ─── Seed Data ────────────────────────────────────────────────────────────────
async function seedGames() {
    const count = await Game.countDocuments();
    if (count === 0) {
        const sampleGames = [
            { name: "Elden Ring",              description: "Experience the epic fantasy world.",                                    price: 49.99, image: "/images/eldenring.jpg" },
            { name: "Hogwarts Legacy",         description: "Live your wizarding world adventure.",                                  price: 69.99, image: "/images/hogwartslegacy.jpg" },
            { name: "Red Dead Redemption 2",   description: "Outlaw life in the Wild West.",                                         price: 39.99, image: "/images/reddeadredemption2.jpg" },
            { name: "The Witcher 3",           description: "Embark on an epic journey through a beautiful, war-torn world.",        price: 39.99, image: "/images/thewitcher3.jpg" },
            { name: "Grand Theft Auto V",      description: "A fast-paced action game set in the fictional city of Los Santos.",     price: 29.99, image: "/images/gta5.jpg" },
            { name: "Minecraft",               description: "Build and explore in an infinite world.",                               price: 19.99, image: "/images/minecraft.jpg" },
            { name: "Fortnite",                description: "Survive in the battle royale game with creative mechanics.",            price: 0.00,  image: "/images/fortnite.jpg" },
            { name: "Call of Duty: Warzone",   description: "A free battle royale game with intense combat action.",                 price: 20.00, image: "/images/coodwarzone.jpg" },
            { name: "Assassin's Creed Valhalla", description: "Explore and conquer the Viking world in this epic action RPG.",      price: 59.99, image: "/images/assassinscreedvalhalla.jpg" },
            { name: "Cyberpunk 2077",          description: "Explore the open world of Night City.",                                 price: 59.99, image: "/images/cyberpunk2077.jpg" },
            { name: "FIFA 22",                 description: "The latest edition of the popular football simulation game.",           price: 59.99, image: "/images/fifa22.jpg" },
            { name: "The Last of Us Part II",  description: "A gripping story of survival in a post-apocalyptic world.",            price: 49.99, image: "/images/lastofus2.jpg" }
        ];
        await Game.insertMany(sampleGames);
        console.log('Sample games added to the database.');
    }
}

// ─── Seed Admin Account ───────────────────────────────────────────────────────
async function seedAdmin() {
    const adminUsername = 'admin@gmail.com';
    const existing = await User.findOne({ username: adminUsername });
    if (!existing) {
        const hashed = await bcrypt.hash('123456', SALT_ROUNDS);
        await User.create({ username: adminUsername, password: hashed, role: 'admin' });
        console.log('✅ Default admin account created: admin@gmail.com');
    } else {
        console.log('ℹ️  Admin account already exists, skipping seed.');
    }
}

// ─── Middleware ───────────────────────────────────────────────────────────────
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

app.use(session({
    secret: SESSION_SECRET,
    resave: false,
    saveUninitialized: false,
    cookie: {
        secure: false,       // set to true in production with HTTPS
        maxAge: 24 * 60 * 60 * 1000  // 24 hours default
    }
}));

// ─── Auth Middleware ──────────────────────────────────────────────────────────
function requireLogin(req, res, next) {
    if (req.session && req.session.userId) return next();
    res.redirect('/');
}

function requireAdmin(req, res, next) {
    if (req.session && req.session.role === 'admin') return next();
    res.status(403).json({ error: 'Access denied. Admins only.' });
}

// ─── Input Validation Helpers ─────────────────────────────────────────────────
function validateUserInput(username, password) {
    if (!username || typeof username !== 'string' || username.trim().length < 3)
        return 'Username must be at least 3 characters.';
    if (!password || typeof password !== 'string' || password.length < 6)
        return 'Password must be at least 6 characters.';
    return null;
}

function validateGameInput(name, description, price) {
    if (!name || typeof name !== 'string' || name.trim().length === 0)
        return 'Game name is required.';
    if (!description || typeof description !== 'string' || description.trim().length === 0)
        return 'Description is required.';
    if (price === undefined || isNaN(Number(price)) || Number(price) < 0)
        return 'Price must be a valid non-negative number.';
    return null;
}

// ─── Page Routes ──────────────────────────────────────────────────────────────
// Landing page — public marketing page
app.get('/landing', (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'landing.html'));
});

// Register page
app.get('/register', (req, res) => {
    if (req.session && req.session.userId) return res.redirect('/home');
    res.sendFile(path.join(__dirname, 'public', 'register.html'));
});

// Root → Landing page (public entry point)
app.get('/', (req, res) => {
    if (req.session && req.session.userId) return res.redirect('/home');
    res.redirect('/landing');
});

// Sign-in page alias
app.get('/signin', (req, res) => {
    if (req.session && req.session.userId) return res.redirect('/home');
    res.sendFile(path.join(__dirname, 'public', 'speed_h.html'));
});

// Home (requires login)
app.get('/home', requireLogin, (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'home.html'));
});

// Forgot password page
app.get('/forgot-password', (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'forgot-password.html'));
});

// Game detail page (requires login)
app.get('/game/:id', requireLogin, (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'game.html'));
});

// Admin pages (requires admin role)
app.get('/admin', requireAdmin, (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'admin.html'));
});

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

// Logout
app.get('/logout', (req, res) => {
    req.session.destroy(() => res.redirect('/'));
});

// ─── Auth API ─────────────────────────────────────────────────────────────────
// Register new user
app.post('/register', async (req, res) => {
    const { username, password } = req.body;
    const validationError = validateUserInput(username, password);
    if (validationError) return res.status(400).json({ error: validationError });

    try {
        const existing = await User.findOne({ username: username.trim() });
        if (existing) return res.status(409).json({ error: 'Username is already taken. Please choose another.' });

        const hashed = await bcrypt.hash(password, SALT_ROUNDS);
        const newUser = new User({ username: username.trim(), password: hashed, role: 'user' });
        await newUser.save();
        res.status(201).json({ message: 'Account created successfully!' });
    } catch (err) {
        res.status(500).json({ error: 'Registration failed.', details: err.message });
    }
});

// Login
app.post('/login', async (req, res) => {
    const { username, password, remember } = req.body;

    if (!username || !password)
        return res.status(400).json({ error: 'Username and password are required.' });

    try {
        const user = await User.findOne({ username: username.trim() });
        if (!user) return res.status(400).json({ error: 'Invalid username or password.' });

        const match = await bcrypt.compare(password, user.password);
        if (!match) return res.status(400).json({ error: 'Invalid username or password.' });

        req.session.userId = user._id;
        req.session.role   = user.role;

        // Remember me: extend session to 7 days
        if (remember === 'on' || remember === true) {
            req.session.cookie.maxAge = 7 * 24 * 60 * 60 * 1000;
        }

        res.json({ message: 'Login successful!', role: user.role });
    } catch (err) {
        res.status(500).json({ error: 'Login error.', details: err.message });
    }
});

// ─── Game API ─────────────────────────────────────────────────────────────────
// GET /games — supports optional ?query= search filter (FIXED: was duplicated)
app.get('/games', async (req, res) => {
    const query = req.query.query || '';
    try {
        const filter = query
            ? { $or: [
                { name:        { $regex: query, $options: 'i' } },
                { description: { $regex: query, $options: 'i' } }
              ]}
            : {};
        const games = await Game.find(filter);
        res.json(games);
    } catch (err) {
        res.status(500).json({ error: 'Error retrieving games.', details: err.message });
    }
});

// GET /api/game/:id — game details as JSON for the detail page
app.get('/api/game/:id', async (req, res) => {
    try {
        const game = await Game.findById(req.params.id);
        if (!game) return res.status(404).json({ error: 'Game not found.' });
        res.json(game);
    } catch (err) {
        res.status(500).json({ error: 'Error retrieving game.', details: err.message });
    }
});

// GET /games/:id — single game by ID
app.get('/games/:id', async (req, res) => {
    try {
        const game = await Game.findById(req.params.id);
        if (!game) return res.status(404).json({ error: 'Game not found.' });
        res.json(game);
    } catch (err) {
        res.status(500).json({ error: 'Error retrieving game.', details: err.message });
    }
});

// POST /games — add new game (admin only)
app.post('/games', requireAdmin, async (req, res) => {
    const { name, description, price, image } = req.body;
    const validationError = validateGameInput(name, description, price);
    if (validationError) return res.status(400).json({ error: validationError });

    try {
        const newGame = new Game({ name: name.trim(), description: description.trim(), price: Number(price), image: image || '' });
        await newGame.save();
        res.json({ message: 'Game added successfully!' });
    } catch (err) {
        res.status(500).json({ error: 'Error adding game.', details: err.message });
    }
});

// PUT /games/:id — update game (admin only)
app.put('/games/:id', requireAdmin, async (req, res) => {
    const { name, description, price, image } = req.body;
    const validationError = validateGameInput(name, description, price);
    if (validationError) return res.status(400).json({ error: validationError });

    try {
        const updated = await Game.findByIdAndUpdate(
            req.params.id,
            { name: name.trim(), description: description.trim(), price: Number(price), image },
            { new: true }
        );
        if (!updated) return res.status(404).json({ error: 'Game not found.' });
        res.json({ message: 'Game updated successfully!', game: updated });
    } catch (err) {
        res.status(500).json({ error: 'Error updating game.', details: err.message });
    }
});

// DELETE /games/:id — delete game (admin only)
app.delete('/games/:id', requireAdmin, async (req, res) => {
    try {
        const deleted = await Game.findByIdAndDelete(req.params.id);
        if (!deleted) return res.status(404).json({ error: 'Game not found.' });
        res.json({ message: 'Game deleted successfully!' });
    } catch (err) {
        res.status(500).json({ error: 'Error deleting game.', details: err.message });
    }
});

// ─── User API ─────────────────────────────────────────────────────────────────
// GET /users — all users (admin only)
app.get('/users', requireAdmin, async (req, res) => {
    try {
        const users = await User.find({}, '-password'); // exclude password field
        res.json(users);
    } catch (err) {
        res.status(500).json({ error: 'Error retrieving users.', details: err.message });
    }
});

// GET /search — search users by username (admin only)
app.get('/search', requireAdmin, async (req, res) => {
    const { query } = req.query;
    try {
        const users = await User.find(
            { username: { $regex: query || '', $options: 'i' } },
            '-password'
        );
        res.json(users);
    } catch (err) {
        res.status(500).json({ error: 'Error searching users.', details: err.message });
    }
});

// POST /add — add new user (admin only)
app.post('/add', requireAdmin, async (req, res) => {
    const { username, password } = req.body;
    const validationError = validateUserInput(username, password);
    if (validationError) return res.status(400).json({ error: validationError });

    try {
        const existing = await User.findOne({ username: username.trim() });
        if (existing) return res.status(409).json({ error: 'Username already exists.' });

        const hashed = await bcrypt.hash(password, SALT_ROUNDS);
        const newUser = new User({ username: username.trim(), password: hashed });
        await newUser.save();
        res.json({ message: 'User added successfully!' });
    } catch (err) {
        res.status(500).json({ error: 'Error adding user.', details: err.message });
    }
});

// GET /view/:id — view a single user (admin only)
app.get('/view/:id', requireAdmin, async (req, res) => {
    try {
        const user = await User.findById(req.params.id, '-password');
        if (!user) return res.status(404).json({ error: 'User not found.' });
        res.json(user);
    } catch (err) {
        res.status(500).json({ error: 'Error retrieving user.', details: err.message });
    }
});

// PUT /update/:id — update user (admin only)
app.put('/update/:id', requireAdmin, async (req, res) => {
    const { username, password } = req.body;

    if (!username || username.trim().length < 3)
        return res.status(400).json({ error: 'Username must be at least 3 characters.' });

    try {
        const updateData = { username: username.trim() };
        if (password && password.length >= 6) {
            updateData.password = await bcrypt.hash(password, SALT_ROUNDS);
        }

        const user = await User.findByIdAndUpdate(req.params.id, updateData, { new: true, select: '-password' });
        if (!user) return res.status(404).json({ error: 'User not found.' });
        res.json({ message: 'User updated successfully!', user });
    } catch (err) {
        res.status(500).json({ error: 'Error updating user.', details: err.message });
    }
});

// DELETE /delete/:id — delete user (admin only)
app.delete('/delete/:id', requireAdmin, async (req, res) => {
    try {
        const user = await User.findByIdAndDelete(req.params.id);
        if (!user) return res.status(404).json({ error: 'User not found.' });
        res.json({ message: 'User deleted successfully!' });
    } catch (err) {
        res.status(500).json({ error: 'Error deleting user.', details: err.message });
    }
});

// ─── Image Serving ────────────────────────────────────────────────────────────
app.get('/images/:imageName', (req, res) => {
    res.sendFile(path.join(__dirname, 'public/images', req.params.imageName));
});

// ─── 404 Catch-all ────────────────────────────────────────────────────────────
app.use((req, res) => {
    res.status(404).sendFile(path.join(__dirname, 'public', '404.html'));
});

// ─── Start Server ─────────────────────────────────────────────────────────────
app.listen(PORT, () => {
    console.log(`Server running on http://localhost:${PORT}`);
});
