import config from 'config';
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 { renderPage } from 'vike/server'; // eslint-disable-line import/extensions

// import root from './root.js';

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

import errorHandler from './error.js';

import { fetchScenesApi } from './scenes.js';
import { fetchActorsApi } from './actors.js';
import { fetchMoviesApi } from './movies.js';
import { fetchEntitiesApi } from './entities.js';
import { fetchTagsApi } from './tags.js';

import {
	setUserApi,
	loginApi,
	logoutApi,
	signupApi,
} from './auth.js';

import {
	fetchUserApi,
} from './users.js';

import {
	createStashApi,
	removeStashApi,
	stashActorApi,
	stashSceneApi,
	stashMovieApi,
	unstashActorApi,
	unstashSceneApi,
	unstashMovieApi,
	updateStashApi,
} from './stashes.js';

import {
	fetchAlertsApi,
	createAlertApi,
	removeAlertApi,
	fetchNotificationsApi,
	updateNotificationApi,
	updateNotificationsApi,
} from './alerts.js';

import { fetchNotifications } from '../alerts.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();

	app.use(compression());
	app.disable('x-powered-by');

	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);
	}

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

	// USERS
	router.get('/api/users/:userId', fetchUserApi);
	router.post('/api/users', signupApi);

	router.get('/api/users/:userId/notifications', fetchNotificationsApi);
	router.patch('/api/users/:userId/notifications', updateNotificationsApi);
	router.patch('/api/users/:userId/notifications/:notificationId', updateNotificationApi);

	// STASHES
	router.post('/api/stashes', createStashApi);
	router.patch('/api/stashes/:stashId', updateStashApi);
	router.delete('/api/stashes/:stashId', removeStashApi);

	router.post('/api/stashes/:stashId/actors', stashActorApi);
	router.post('/api/stashes/:stashId/scenes', stashSceneApi);
	router.post('/api/stashes/:stashId/movies', stashMovieApi);

	router.delete('/api/stashes/:stashId/actors/:actorId', unstashActorApi);
	router.delete('/api/stashes/:stashId/scenes/:sceneId', unstashSceneApi);
	router.delete('/api/stashes/:stashId/movies/:movieId', unstashMovieApi);

	// ALERTS
	router.get('/api/alerts', fetchAlertsApi);
	router.post('/api/alerts', createAlertApi);
	router.delete('/api/alerts/:alertId', removeAlertApi);

	// SCENES
	router.get('/api/scenes', fetchScenesApi);

	// ACTORS
	router.get('/api/actors', fetchActorsApi);

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

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

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

	router.get('*', async (req, res, next) => {
		const notifications = await fetchNotifications(req.user, { limit: 20 });

		const pageContextInit = {
			urlOriginal: req.originalUrl,
			urlQuery: req.query, // vike's own query does not apply boolean parser
			headers: req.headers,
			cookies: req.cookies,
			tagFilter: req.tagFilter,
			user: req.user && {
				id: req.user.id,
				username: req.user.username,
				email: req.user.email,
				avatar: req.user.avatar,
				stashes: req.user.stashes,
				primaryStash: req.user.primaryStash,
			},
			env: {
				allowLogin: config.auth.login,
				allowSignup: config.auth.signup,
				maxMatches: config.database.manticore.maxMatches,
				maxAggregateSize: config.database.manticore.maxAggregateSize,
				media: config.media,
			},
			notifications,
		};

		const pageContext = await renderPage(pageContextInit);
		const { httpResponse } = pageContext;

		if (pageContext.errorWhileRendering) {
			console.error(pageContext.errorWhileRendering);
		}

		if (!httpResponse) {
			next();
			return;
		}

		/*
		if (res.writeEarlyHints) {
			res.writeEarlyHints({ link: httpResponse.earlyHints.map((e) => e.earlyHintLink) });
		}
		*/

		httpResponse.headers.forEach(([name, value]) => res.setHeader(name, value));
		res.status(httpResponse.statusCode);
		// For HTTP streams use httpResponse.pipe() instead, see https://vike.dev/stream
		res.send(httpResponse.body);
	});

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

	const port = process.env.PORT || config.web.port || 3000;
	app.listen(port);

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