import fastify, { FastifyInstance } from "fastify";
import fastifyCookie from "fastify-cookie";
import fastifyFormBody from "fastify-formbody";
import fastifyMultipart from "fastify-multipart";
import { InternalService, Microservice } from "@autoplex/microservice";
import { RouteRegisterFactory, RouteFactory } from "./RouteRegisterFactory";
import { MiddlewareMethod } from "./middleware";

export abstract class WebServerService<M extends Microservice = Microservice> extends InternalService<M>
{
	/**
	 * The application key to sign cookies with
	 */
	protected abstract readonly APP_KEY: string;

	/**
	 * The port to host the webserver on
	 */
	protected abstract readonly PORT: number;

	/**
	 * The list of routes to register
	 */
	protected abstract readonly ROUTES: RouteFactory<MiddlewareMethod<any>, M>[];

	/**
	 * The internal webserver instance
	 */
	protected fastify!: FastifyInstance;

	/**
	 * Boot the webserver
	 */
	public async boot() {
		// Create the Fastify instance
		this.fastify = fastify();

		// Install plugins
		await this.registerPlugins(this.fastify);

		// Register the routes
		this.registerRoutes(this.fastify);
	}

	/**
	 * Start the webserver
	 */
	public start() {
		// Start listening
		this.fastify.listen(this.PORT, "0.0.0.0");
		this.log("Webserver listening on port:", this.PORT);
	}

	/**
	 * Shutdown the webserver
	 */
	public async shutdown() {
		this.log("Webserver shutting down");
		await this.fastify.close();
	}

	// ---------------------------------------------------------------------------------------------

	/**
	 * Register required Fastify plugins
	 */
	protected async registerPlugins(fastify: FastifyInstance) {
		return Promise.all([
			fastify.register(fastifyCookie, {
				secret: this.APP_KEY
			}),
			fastify.register(fastifyFormBody),
			fastify.register(fastifyMultipart, {
				limits: {
					fileSize: 16*1024*1024,
					files: 50
				}
			})
		]);
	}

	/**
	 * Register all route groups
	 */
	protected registerRoutes(fastify: FastifyInstance) {
		let factory = new RouteRegisterFactory(this, fastify, this.app);
		for (let group in this.ROUTES) {
			this.ROUTES[group](factory, this.app);
		}
	}
}
