import config from 'config';
import path from 'path';
import express from 'express';
import boolParser from 'express-query-boolean';
import Router from 'express-promise-router';
import session from 'express-session';
import RedisStore from 'connect-redis';
import compression from 'compression';
import cookie from 'cookie';

import redis from '../redis.js';

import errorHandler from './error.js';
import consentHandler from './consent.js';
import initRestrictionHandler from './restrictions.js';

import { scenesRouter } from './scenes.js';
import { actorsRouter } from './actors.js';

import { fetchMoviesApi } from './movies.js';
import { fetchEntitiesApi } from './entities.js';
import { fetchTagsApi } from './tags.js';

import { verifyKey } from '../auth.js';
import { graphqlApi } from './graphql.js';

import mainHandler from './main.js';

import {
	setUserApi,
	loginApi,
	logoutApi,
	signupApi,
	fetchUserKeysApi,
	createKeyApi,
	removeUserKeyApi,
	flushUserKeysApi,
} from './auth.js';

import { router as userRouter } from './users.js';
import { router as stashesRouter } from './stashes.js';
import { router as alertsRouter } from './alerts.js';

import { initCachesApi } from './system.js';

import initLogger from '../logger.js';

const logger = initLogger();
const isProduction = process.env.NODE_ENV === 'production';

export default async function initServer() {
	const app = express();
	const router = Router();
	const restrictionHandler = await initRestrictionHandler();

	app.use(compression());
	app.disable('x-powered-by');
	app.set('view engine', 'ejs');

	router.use(boolParser());

	router.use('/', express.static('public'));
	router.use('/', express.static('static'));
	router.use('/media', express.static(config.media.path));

	router.use((req, _res, next) => {
		if (req.headers.cookie) {
			const cookies = cookie.parse(req.headers.cookie);

			/* eslint-disable no-param-reassign */
			req.cookies = cookies;
			req.tagFilter = cookies.tags ? JSON.parse(cookies.tags) : [];
			/* eslint-enable no-param-reassign */
		}

		next();
	});

	router.use(express.json());

	const redisStore = new RedisStore({
		client: redis,
		prefix: 'traxxx:session:',
	});

	router.use(session({
		...config.web.session,
		store: redisStore,
	}));

	router.use(setUserApi);

	// Vite integration
	if (isProduction) {
		app.enable('trust proxy');

		// In production, we need to serve our static assets ourselves.
		// (In dev, Vite's middleware serves our static assets.)
		const sirv = (await import('sirv')).default;
		router.use(sirv('dist/client'));
	} else {
		// We instantiate Vite's development server and integrate its middleware to our server.
		// ⚠️ We instantiate it only in development. (It isn't needed in production and it
		// would unnecessarily bloat our production server.)
		const vite = await import('vite');

		const viteDevMiddleware = (
			await vite.createServer({
				// root,
				server: { middlewareMode: true },
			})
		).middlewares;

		router.use(viteDevMiddleware);
	}

	router.use(restrictionHandler);

	router.get('/consent', (_req, res) => {
		res.sendFile(path.join(import.meta.dirname, '../../assets/consent.html'));
	});

	router.use('/api/*', async (req, _res, next) => {
		if (req.headers['api-user']) {
			await verifyKey(req.headers['api-user'], req.headers['api-key'], req);

			req.user = { // eslint-disable-line no-param-reassign
				id: Number(req.headers['api-user']),
			};
		}

		next();
	});

	// SESSION
	router.post('/api/session', loginApi);
	router.delete('/api/session', logoutApi);

	// USERS
	router.post('/api/users', signupApi);

	// API KEYS
	router.get('/api/me/keys', fetchUserKeysApi);
	router.post('/api/me/keys', createKeyApi);
	router.delete('/api/me/keys/:keyIdentifier', removeUserKeyApi);
	router.delete('/api/me/keys', flushUserKeysApi);

	router.use(userRouter);
	router.use(stashesRouter);
	router.use(alertsRouter);
	router.use(scenesRouter);
	router.use(actorsRouter);

	// MOVIES
	router.get('/api/movies', fetchMoviesApi);

	// ENTITIES
	router.get('/api/entities', fetchEntitiesApi);

	// TAGS
	router.get('/api/tags', fetchTagsApi);

	router.post('/api/caches', initCachesApi);

	if (config.apiAccess.graphqlEnabled) {
		router.post('/graphql', graphqlApi);
	}

	router.use(consentHandler);

	router.use((_req, res, next) => {
		/* eslint-disable no-param-reassign */
		res.set('Accept-CH', 'Sec-CH-Prefers-Color-Scheme');
		res.set('Vary', 'Sec-CH-Prefers-Color-Scheme');
		res.set('Critical-CH', 'Sec-CH-Prefers-Color-Scheme');
		/* eslint-enable no-param-reassign */

		next();
	});

	router.get('*', mainHandler);

	router.use(errorHandler);
	app.use(router);

	const port = process.env.PORT || config.web.port || 3000;
	// const port = Math.round(Math.random() * 10000);

	app.listen(port, config.web.host);

	logger.info(`Server running at http://${config.web.host}:${port}`);
}
