// Authentication Routes
const express = require('express');
const bcrypt = require('bcryptjs');
const crypto = require('crypto');
const rateLimit = require('express-rate-limit');
const pool = require('../db');
const { requireAuth, requireGroup } = require('../middleware/auth');

const loginLimiter = rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 20, // 20 attempts per window
    standardHeaders: true,
    legacyHeaders: false,
    message: { error: 'Too many login attempts. Please try again in 15 minutes.' }
});

function createAuthRouter(logAudit) {
    const router = express.Router();

    /**
     * POST /api/auth/login
     *
     * Authenticates a user with username and password, creates a session,
     * and sets an httpOnly session cookie. Rate-limited to 20 attempts per 15 minutes.
     *
     * @body {string} username - The user's login username
     * @body {string} password - The user's password
     * @returns {object} 200 - { message: 'Login successful', user: { id, username, email, group } }
     * @returns {object} 400 - { error: 'Username and password are required' }
     * @returns {object} 401 - { error: 'Invalid username or password' } | { error: 'Account is disabled' }
     * @returns {object} 429 - { error: 'Too many login attempts. Please try again in 15 minutes.' }
     * @returns {object} 500 - { error: 'Login failed' }
     */
    router.post('/login', loginLimiter, async (req, res) => {
        const { username, password } = req.body;

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

        try {
            // Find user
            const { rows } = await pool.query(
                'SELECT * FROM users WHERE username = $1',
                [username]
            );
            const user = rows[0];

            if (!user) {
                logAudit({
                    userId: null,
                    username: username,
                    action: 'login_failed',
                    entityType: 'auth',
                    entityId: null,
                    details: { reason: 'user_not_found' },
                    ipAddress: req.ip
                });
                return res.status(401).json({ error: 'Invalid username or password' });
            }

            if (!user.is_active) {
                logAudit({
                    userId: user.id,
                    username: username,
                    action: 'login_failed',
                    entityType: 'auth',
                    entityId: null,
                    details: { reason: 'account_disabled' },
                    ipAddress: req.ip
                });
                return res.status(401).json({ error: 'Account is disabled' });
            }

            // Verify password
            const validPassword = await bcrypt.compare(password, user.password_hash);
            if (!validPassword) {
                logAudit({
                    userId: user.id,
                    username: username,
                    action: 'login_failed',
                    entityType: 'auth',
                    entityId: null,
                    details: { reason: 'invalid_password' },
                    ipAddress: req.ip
                });
                return res.status(401).json({ error: 'Invalid username or password' });
            }

            // Generate session ID
            const sessionId = crypto.randomBytes(32).toString('hex');
            const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 hours

            // Create session
            await pool.query(
                'INSERT INTO sessions (session_id, user_id, expires_at) VALUES ($1, $2, $3)',
                [sessionId, user.id, expiresAt.toISOString()]
            );

            // Update last login
            await pool.query(
                'UPDATE users SET last_login = NOW() WHERE id = $1',
                [user.id]
            );

            // Set cookie
            res.cookie('session_id', sessionId, {
                httpOnly: true,
                secure: process.env.NODE_ENV === 'production',
                sameSite: 'lax',
                maxAge: 24 * 60 * 60 * 1000 // 24 hours
            });

            logAudit({
                userId: user.id,
                username: user.username,
                action: 'login',
                entityType: 'auth',
                entityId: null,
                details: { group: user.user_group },
                ipAddress: req.ip
            });

            res.json({
                message: 'Login successful',
                user: {
                    id: user.id,
                    username: user.username,
                    email: user.email,
                    group: user.user_group,
                    teams: user.bu_teams ? user.bu_teams.split(',').filter(Boolean) : []
                }
            });
        } catch (err) {
            console.error('Login error:', err);
            res.status(500).json({ error: 'Login failed' });
        }
    });

    /**
     * POST /api/auth/logout
     *
     * Ends the current user session by deleting it from the database
     * and clearing the session cookie.
     *
     * @returns {object} 200 - { message: 'Logged out successfully' }
     */
    router.post('/logout', async (req, res) => {
        const sessionId = req.cookies?.session_id;

        if (sessionId) {
            // Look up user before deleting session
            let session = null;
            try {
                const { rows } = await pool.query(
                    `SELECT u.id as user_id, u.username FROM sessions s
                     JOIN users u ON s.user_id = u.id
                     WHERE s.session_id = $1`,
                    [sessionId]
                );
                session = rows[0] || null;
            } catch (err) {
                // Non-critical — proceed with logout
            }

            // Delete session from database
            try {
                await pool.query(
                    'DELETE FROM sessions WHERE session_id = $1',
                    [sessionId]
                );
            } catch (err) {
                // Non-critical — proceed with logout
            }

            if (session) {
                logAudit({
                    userId: session.user_id,
                    username: session.username,
                    action: 'logout',
                    entityType: 'auth',
                    entityId: null,
                    details: null,
                    ipAddress: req.ip
                });
            }
        }

        // Clear cookie
        res.clearCookie('session_id');
        res.json({ message: 'Logged out successfully' });
    });

    /**
     * GET /api/auth/me
     *
     * Returns the currently authenticated user based on the session cookie.
     * Clears the cookie and returns 401 if the session is expired or the account is disabled.
     *
     * @returns {object} 200 - { user: { id, username, email, group } }
     * @returns {object} 401 - { error: 'Not authenticated' } | { error: 'Session expired' } | { error: 'Account is disabled' }
     * @returns {object} 500 - { error: 'Failed to get user' }
     */
    router.get('/me', async (req, res) => {
        const sessionId = req.cookies?.session_id;

        if (!sessionId) {
            return res.status(401).json({ error: 'Not authenticated' });
        }

        try {
            const { rows } = await pool.query(
                `SELECT s.*, u.id as user_id, u.username, u.email, u.user_group, u.bu_teams, u.is_active
                 FROM sessions s
                 JOIN users u ON s.user_id = u.id
                 WHERE s.session_id = $1 AND s.expires_at > NOW()`,
                [sessionId]
            );

            const session = rows[0];

            if (!session) {
                res.clearCookie('session_id');
                return res.status(401).json({ error: 'Session expired' });
            }

            if (!session.is_active) {
                res.clearCookie('session_id');
                return res.status(401).json({ error: 'Account is disabled' });
            }

            res.json({
                user: {
                    id: session.user_id,
                    username: session.username,
                    email: session.email,
                    group: session.user_group,
                    teams: session.bu_teams ? session.bu_teams.split(',').filter(Boolean) : []
                }
            });
        } catch (err) {
            console.error('Get user error:', err);
            res.status(500).json({ error: 'Failed to get user' });
        }
    });

    /**
     * GET /api/auth/profile
     *
     * Returns the full profile for the currently authenticated user.
     * Queries the database for up-to-date account details including
     * creation date and last login timestamp.
     *
     * @returns {object} 200 - { id, username, email, group, created_at, last_login }
     * @returns {object} 401 - { error: 'Account is disabled' } (clears session cookie)
     * @returns {object} 500 - { error: 'Failed to fetch profile' }
     */
    router.get('/profile', requireAuth(), async (req, res) => {
        try {
            const { rows } = await pool.query(
                'SELECT id, username, email, user_group, created_at, last_login, is_active FROM users WHERE id = $1',
                [req.user.id]
            );

            const user = rows[0];

            if (!user || !user.is_active) {
                res.clearCookie('session_id');
                return res.status(401).json({ error: 'Account is disabled' });
            }

            res.json({
                id: user.id,
                username: user.username,
                email: user.email,
                group: user.user_group,
                created_at: user.created_at,
                last_login: user.last_login
            });
        } catch (err) {
            console.error('Profile fetch error:', err);
            res.status(500).json({ error: 'Failed to fetch profile' });
        }
    });

    // Rate limiter for password change — 5 attempts per 15-minute window, keyed by session cookie
    const passwordChangeLimiter = rateLimit({
        windowMs: 15 * 60 * 1000, // 15 minutes
        max: 5,
        standardHeaders: true,
        legacyHeaders: false,
        keyGenerator: (req) => req.cookies?.session_id || req.ip,
        message: { error: 'Too many password change attempts. Please try again later.' }
    });

    /**
     * POST /api/auth/change-password
     *
     * Allows the authenticated user to change their own password.
     * Rate-limited to 5 attempts per 15-minute window per session.
     *
     * @body {string} currentPassword - The user's current password
     * @body {string} newPassword - The desired new password (min 8 characters)
     * @returns {object} 200 - { message: 'Password changed successfully' }
     * @returns {object} 400 - { error: 'Current password and new password are required' } | { error: 'New password must be at least 8 characters' }
     * @returns {object} 401 - { error: 'Account is disabled' } | { error: 'Current password is incorrect' }
     * @returns {object} 429 - { error: 'Too many password change attempts. Please try again later.' }
     * @returns {object} 500 - { error: 'Failed to change password' }
     */
    router.post('/change-password', requireAuth(), passwordChangeLimiter, async (req, res) => {
        const { currentPassword, newPassword } = req.body;

        if (!currentPassword || !newPassword) {
            return res.status(400).json({ error: 'Current password and new password are required' });
        }

        if (newPassword.length < 8) {
            return res.status(400).json({ error: 'New password must be at least 8 characters' });
        }

        try {
            // Fetch user's password hash and active status
            const { rows } = await pool.query(
                'SELECT password_hash, is_active FROM users WHERE id = $1',
                [req.user.id]
            );

            const user = rows[0];

            if (!user || !user.is_active) {
                return res.status(401).json({ error: 'Account is disabled' });
            }

            // Verify current password
            const validPassword = await bcrypt.compare(currentPassword, user.password_hash);
            if (!validPassword) {
                return res.status(401).json({ error: 'Current password is incorrect' });
            }

            // Hash new password and update
            const newHash = await bcrypt.hash(newPassword, 10);
            await pool.query(
                'UPDATE users SET password_hash = $1 WHERE id = $2',
                [newHash, req.user.id]
            );

            logAudit({
                userId: req.user.id,
                username: req.user.username,
                action: 'password_change',
                entityType: 'auth',
                entityId: null,
                details: null,
                ipAddress: req.ip
            });

            res.json({ message: 'Password changed successfully' });
        } catch (err) {
            console.error('Password change error:', err);
            res.status(500).json({ error: 'Failed to change password' });
        }
    });

    /**
     * POST /api/auth/cleanup-sessions
     *
     * Deletes all expired sessions from the database. Requires Admin group.
     *
     * @returns {object} 200 - { message: 'Expired sessions cleaned up' }
     * @returns {object} 401 - { error: 'Authentication required' }
     * @returns {object} 403 - { error: 'Insufficient permissions', required: ['Admin'], current: '...' }
     * @returns {object} 500 - { error: 'Cleanup failed' }
     */
    router.post('/cleanup-sessions', requireAuth(), requireGroup('Admin'), async (req, res) => {
        try {
            await pool.query("DELETE FROM sessions WHERE expires_at < NOW()");
            res.json({ message: 'Expired sessions cleaned up' });
        } catch (err) {
            console.error('Session cleanup error:', err);
            res.status(500).json({ error: 'Cleanup failed' });
        }
    });

    return router;
}

module.exports = createAuthRouter;
