const path = require('path')
const http = require('http')
const debug = require('debug')('48hr-email:server')
const express = require('express')
const session = require('express-session')
const cookieParser = require('cookie-parser')
const logger = require('morgan')
const Twig = require('twig')
const compression = require('compression')
const helmet = require('helmet')
const socketio = require('socket.io')

const config = require('../../application/config')
const inboxRouter = require('./routes/inbox')
const loginRouter = require('./routes/login')
const errorRouter = require('./routes/error')
const lockRouter = require('./routes/lock')
const authRouter = require('./routes/auth')
const accountRouter = require('./routes/account')
const statsRouter = require('./routes/stats')
const { sanitizeHtmlTwigFilter, readablePurgeTime } = require('./views/twig-filters')

const Helper = require('../../application/helper')
const helper = new(Helper)
const purgeTime = helper.purgeTimeElemetBuilder()

// Utility function for consistent error handling in routes
const handleRouteError = (error, req, res, next, context = 'route') => {
    debug(`Error in ${context}:`, error.message)
    console.error(`Error in ${context}`, error)
    next(error)
}

// Init express middleware
const app = express()
app.use(helmet())
app.use(compression())
app.set('config', config)
const server = http.createServer(app)
const io = socketio(server)

app.set('socketio', io)
app.use(logger('dev'))
app.use(express.json())
app.use(express.urlencoded({ extended: false }))

// Cookie parser for signed cookies (email verification)
app.use(cookieParser(config.http.sessionSecret))

// Session support (always enabled for forward verification and inbox locking)
app.use(session({
    secret: config.http.sessionSecret,
    resave: false,
    saveUninitialized: false,
    cookie: { maxAge: 24 * 60 * 60 * 1000 } // 24 hours
}))

// Clear lock session data when user goes Home (but preserve authentication)
app.get('/', (req, res, next) => {
    if (req.session && req.session.lockedInbox) {
        // Only clear lock-related data, preserve user authentication
        delete req.session.lockedInbox
        req.session.save(() => next())
    } else {
        next()
    }
})

// Remove trailing slash middleware (except for root)
app.use((req, res, next) => {
    if (req.path.length > 1 && req.path.endsWith('/')) {
        const query = req.url.slice(req.path.length) // preserve query string
        return res.redirect(301, req.path.slice(0, -1) + query)
    }
    next()
})

// View engine setup
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'twig')
app.set('twig options', {
    autoescape: true
})

// Application code:
app.use(
    express.static(path.join(__dirname, 'public'), {
        immutable: true,
        maxAge: '1h'
    })
)
Twig.extendFilter('sanitizeHtml', sanitizeHtmlTwigFilter)
Twig.extendFilter('readablePurgeTime', readablePurgeTime)

// Middleware to expose user session to all templates
app.use((req, res, next) => {
    res.locals.authEnabled = config.user.authEnabled
    res.locals.config = config
    res.locals.currentUser = null
    res.locals.alertMessage = req.session ? req.session.alertMessage : null

    // Clear alert after reading
    if (req.session && req.session.alertMessage) {
        delete req.session.alertMessage
    }

    if (req.session && req.session.userId && req.session.username && req.session.isAuthenticated) {
        res.locals.currentUser = {
            id: req.session.userId,
            username: req.session.username
        }
    }
    next()
})

// Middleware to expose mail count to all templates
app.use(async(req, res, next) => {
    const mailProcessingService = req.app.get('mailProcessingService')
    const imapService = req.app.get('imapService')
    const Helper = require('../../application/helper')
    const helper = new Helper()

    if (mailProcessingService) {
        const count = mailProcessingService.getCount()
        let largestUid = null

        if (imapService) {
            try {
                largestUid = await imapService.getLargestUid()
            } catch (e) {
                debug('Error getting largest UID:', e.message)
            }
        }

        res.locals.mailCount = helper.mailCountBuilder(count, largestUid)
    } else {
        res.locals.mailCount = ''
    }
    next()
})

// Middleware to show loading page until IMAP is ready
app.use((req, res, next) => {
    const isImapReady = req.app.get('isImapReady')
    if (!isImapReady && !req.path.startsWith('/images') && !req.path.startsWith('/javascripts') && !req.path.startsWith('/stylesheets') && !req.path.startsWith('/dependencies')) {
        return res.render('loading')
    }
    next()
})

app.use('/', loginRouter)
if (config.user.authEnabled) {
    app.use('/', authRouter)
    app.use('/', accountRouter)
}
app.use('/inbox', inboxRouter)
app.use('/error', errorRouter)
app.use('/lock', lockRouter)
app.use('/stats', statsRouter)

// Catch 404 and forward to error handler
app.use((req, res, next) => {
    next({ message: 'Page not found', status: 404 })
})

// Error handler
app.use(async(err, req, res, _next) => {
    try {
        debug('Error handler triggered:', err.message)

        // Set locals, only providing error in development
        res.locals.message = err.message
        res.locals.error = req.app.get('env') === 'development' ? err : {}

        // Render the error page
        res.status(err.status || 500)
        res.render('error', {
            purgeTime: purgeTime,
            address: req.params && req.params.address,
            branding: config.http.features.branding || ['48hr.email', 'Service', 'https://example.com']
        })
    } catch (renderError) {
        debug('Error in error handler:', renderError.message)
        console.error('Critical error in error handler', renderError)
            // Fallback: send plain text error if rendering fails
        res.status(500).send('Internal Server Error')
    }
})

/**
 * Get port from environment and store in Express.
 */

app.set('port', config.http.port)

/**
 * Listen on provided port, on all network interfaces.
 */
server.listen(config.http.port)
server.on('listening', () => {
    const addr = server.address()
    const bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port
    debug('Listening on ' + bind)
})

module.exports = { app, io, server }
