import debug = require("debug");
import express = require("express");
import expressSession = require("express-session");
import { createServer } from "http";
import { RemoteMessageType, REQUIRE_FUNC_NAME } from "neweb-core";
import { ModulePacker } from "neweb-pack";
import { ServerPageRenderer } from "neweb-react/server";
import { Application, SeancesManager, Server } from "neweb-server";
import { join, resolve } from "path";
import SessionFileStore = require("session-file-store");
import SocketIO = require("socket.io");
import ExpressHttpHandler from "./ExpressHttpHandler";
import ModuleServer from "./ModulesServer";
import NewebServerPageRenderer from "./neweb-components/server/PageRenderer";
import SessionsManager from "./SessionsManager";
import SocketIOClient from "./SocketIOClient";
export interface IServerBootstrapConfig {
    rootPath: string;
    port: number;
    env: "production" | "development";
    expressApp?: express.Express;
    engine?: "react" | "neweb";
}
class ServerBootstrap {
    protected engine: "react" | "neweb" = "neweb";
    constructor(protected config: IServerBootstrapConfig) {
        this.engine = this.config.engine || "neweb";
    }
    public async start() {
        const rootPath = this.config.rootPath;
        const port = this.config.port;
        const env = this.config.env;
        // settings
        const appPath = resolve(join(rootPath, "app"));
        const modulesPath = join(appPath, "..", "modules");
        const sessionsPath = join(appPath, "..", "sessions");
        // logs
        if (env === "development") {
            debug.enable("*,-engine:*,-engine,-socket.io-parser,-socket.io:*");
        }
        const expressApp = this.config.expressApp ? this.config.expressApp : express();
        // neweb-pack
        const modulePacker = new ModulePacker({
            appRoot: appPath,
            excludedModules: ["react", "react-dom", "neweb", "neweb-components"],
            modulesPath,
            REQUIRE_FUNC_NAME,
            disableCacheForLocalModules: env === "development",
            webpackConfig: {
                module: {
                    rules: [{
                        test: /\.html$/,
                        use: [{
                            loader: require.resolve("html-loader"),
                            options: {
                                minimize: true,
                            },
                        }],
                    }],
                },
            },
        });
        // create app
        const app = new Application({
            appDir: appPath,
            env,
            modulePacker,
        });
        await app.init();

        const appConfig = await app.getConfig();

        const sessionsFileStore = SessionFileStore(expressSession);
        const sessionsStorage = new sessionsFileStore({
            path: sessionsPath,
            retries: 1,
            ttl: appConfig.session.ttl || 86400 * 100000,
        });
        const session = expressSession({
            secret: appConfig.session.secret,
            saveUninitialized: true,
            resave: true,
            store: sessionsStorage,
        });

        // static
        expressApp.use(express.static(resolve(join(appPath, "public"))));
        expressApp.get("/bundle." + env + ".js", (_, res) =>
            res.sendFile(resolve(join(__dirname, "..", "dist", "bundle." + env + "." + this.engine + ".js"))));

        const sessionsManager = new SessionsManager({
            sessionsPath,
            sessionsStorage,
        });
        const seancesManager = new SeancesManager({
            app,
            sessionsManager,
        });
        // neweb
        const pageRenderer = this.engine === "react" ? new ServerPageRenderer({
            app,
        }) : new NewebServerPageRenderer({
            app,
        });
        const server = new Server({
            app,
            seancesManager,
            pageRenderer,
        });
        const modulesServer = new ModuleServer({
            modulesPath,
        });
        expressApp.get("/modules/:type/:name/:version.js", modulesServer.handle);

        const expressHandler = new ExpressHttpHandler({
            app: expressApp,
            server,
        });
        expressApp.use(session, expressHandler.handle);

        const httpServer = createServer(expressApp);
        // Setup SocketIO
        const io = SocketIO();
        io.use((socket, next) => {
            session(socket.request, socket.request.res, next);
        });
        io.on("connection", (socket) => {
            socket.on(RemoteMessageType.Initialize, async ({ seanceId }: { seanceId: string }) => {
                const socketClient = new SocketIOClient({
                    socket,
                    seanceId,
                });
                const request = await socketClient.getRequest();
                server.connectClient({
                    seanceId,
                    client: socketClient,
                    request,
                });
            });
        });
        io.attach(httpServer);
        // Start listening
        httpServer.listen(port, (err: any) => {
            if (err) {
                debug("http")("Error server starting", err);
                process.exit(1);
                return;
            }
            debug("http")("Http server at http://127.0.0.1:" + port + "/");
        });
    }
}
export default ServerBootstrap;
