import path from 'node:path';
import express from 'express';
import session from 'express-session';
import compression from 'compression';
import cookieParser from 'cookie-parser';
import rateLimit from 'express-rate-limit';
import morgan from 'morgan';
import mongoose from 'mongoose';
import passport from 'passport';
import passportLocal from 'passport-local';
import MongoStore from 'connect-mongo';
import { engioscopeApi, specmaticApi } from './api.js';
import { setConfig } from '../config.js';
import setupCrons from '../crons/index.js';
import { dirname } from '../utils.js';
import { oneHourInMs, oneMinuteInMs, oneMonthInMs, oneYearInMs, } from '../../shared/utils.js';
import { UserModel } from '../models/mongoose-models/UsersModel.js';
const LocalStrategy = passportLocal.Strategy;
const uiFolder = path.join(dirname(import.meta.url), '..', '..', 'ui');
const APP_NAME = process.env.VITE_REACT_APP_NAME;
const configureExpress = (config) => {
    const app = express();
    passport.use(new LocalStrategy({ usernameField: 'email', passwordField: 'password' }, (email, password, done) => {
        UserModel.authenticate()(email, password, (err, user) => {
            if (err)
                return done(err);
            if (!user)
                return done(null, false);
            return done(null, user);
        });
    }));
    passport.serializeUser(UserModel.serializeUser());
    passport.deserializeUser(async (email, done) => {
        try {
            const user = await UserModel.findOne({ email }, { email: 1 });
            done(null, { email: user.email });
        }
        catch (error) {
            done(error);
        }
    });
    app.use(express.json());
    app.use(express.text({ type: 'text/html', limit: '1mb' }));
    app.use(session({
        secret: 'your-secret-key',
        resave: true,
        saveUninitialized: false,
        cookie: { maxAge: oneMonthInMs / 1000 },
        store: MongoStore.create({
            mongoUrl: config.mongoUrl,
            stringify: false,
            autoRemove: 'interval',
            autoRemoveInterval: 10,
            ttl: 14 * 24 * 60 * 60, // = 14 days. Default
            touchAfter: oneHourInMs,
        }),
    }));
    app.use(passport.initialize());
    app.use(passport.session());
    app.use(cookieParser());
    app.use((req, res, next) => {
        if (req.cookies.c === undefined) {
            // Set a new cookie
            const randomNumber = Math.random().toString().slice(2);
            res.cookie('c', randomNumber, { maxAge: oneYearInMs, httpOnly: true });
        }
        else {
            // Cookie was already present, just reset its maxage
            res.cookie('c', req.cookies.c, { maxAge: oneYearInMs, httpOnly: true });
        }
        next();
    });
    const sendIndexHtml = (req, res) => {
        res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate');
        res.header('Expires', '-1');
        res.header('Pragma', 'no-cache');
        res.sendFile(path.join(uiFolder, 'index.html'));
    };
    if (process.env.NODE_ENV === 'production') {
        app.use(morgan('combined'));
    }
    app.use(compression());
    app.use(rateLimit({ windowMs: oneMinuteInMs, max: 200 }));
    app.get('/', sendIndexHtml);
    app.use(express.static(uiFolder));
    app.use(APP_NAME === 'specmatic' ? specmaticApi() : engioscopeApi(config));
    app.use((req, res, next) => {
        if (!req.accepts('html'))
            return next();
        sendIndexHtml(req, res);
    });
    return app;
};
export default (config) => {
    // TODO: This belongs at a higher layer, maybe
    setConfig(config);
    mongoose.set('strictQuery', false);
    // Disabling floating promise since mongoose takes care of this internally
    // eslint-disable-next-line @typescript-eslint/no-floating-promises
    mongoose.connect(config.mongoUrl);
    setupCrons();
    const app = configureExpress(config);
    const port = config.port || 1337;
    app.listen(port, () => {
        // eslint-disable-next-line no-console
        console.log(`The server is running on port ${port}`);
    });
};
