'use strict'

import { getLogger } from 'frontblock-generic/Types';
import { promises as fs, mkdirSync } from "fs"
import { RPCServer } from 'rpclibrary'
import { AdminConf, TableDefiniton } from './Types';
import { RPCConfigLoader } from './RPCConfigLoader';
import { RPCPluginLoader } from './PluginLoader';
import * as Path from 'path'
import Knex = require('knex');
import http = require('http');
import express = require('express');
import { TableDefinitionExporter } from './Interfaces';
import { FrontworkEventBus } from './Eventbus';

const logger = getLogger("admin", 'debug') 

export class FrontworkAdmin 
implements TableDefinitionExporter {

    private express
    private httpServer
    private pluginLoader:RPCPluginLoader = new RPCPluginLoader(this)
    private eventBus:FrontworkEventBus = new FrontworkEventBus(this)
    config: RPCConfigLoader<AdminConf>
    knex:Knex

    constructor(){}

    async start(){
        this.initConfig()
        await this.makeKnex()
        this.startWebsocket()
        this.startWebserver()
    }

    protected configChangeHandler = (conf:AdminConf, key?:string) => {
        if(key === 'dbConf'){
            this.makeKnex()
        }
    }

    getConfigKey(key:string){
        return this.config.getConfigKey(key)
    }

    setConfigKey(key:string, value:any){
        return this.config.setConfigKey(key, value)
    }

    private initConfig(){
        this.config = new RPCConfigLoader<AdminConf>({
            name: "FrontworkAdminConf", 
            getDefaultConfig: () => {
                return {
                    httpPort: 8080,
                    eventBusConf: {},
                    dbConf: {
                        client: 'sqlite3',
                        connection: {
                            filename: Path.join(__dirname, "data/frontworkAdmin.sqlite")
                        },
                        useNullAsDefault: true
                    }
                }   
            }
        }, './config', this.configChangeHandler) 
    }

    private startWebsocket(){
        new RPCServer(20000, [
            this.config,
            this.pluginLoader,
            this.eventBus
        ])
    }

    private startWebserver(){
        if(this.httpServer != null || this.express != null){
            logger.warn("Webserver is already running")
            return
        }
        
        let port:number = this.config.getConfig().httpPort
        this.express = express()
        this.express.use('/', express.static('dist/static'))

        /**
         * get the compiled FrontendPlugins.js
         */
        this.express.get('/plugins/:id'+".js", async (request, response) => {
            const pth = Path.resolve("plugins/"+request.params.id, "FrontendPlugin.js");
            const file = await fs.readFile(pth)
            const frontend = file.toString()

            response.status(200)
            response.set('Content-Type', 'application/javascript')
            response.send(frontend)
        })

        /**
         * serve the index.html from the static folder
         */
        this.express.get("/", (request, response) => {
            response.status(200)
            response.sendFile('index.html');
        })

        /**
         * redirect all the other traffic to the single page app
         */
        this.express.get("*", (request, response) => {
            response.status(301)
            response.redirect('/')
        })

        this.httpServer = new http.Server(this.express)
        this.httpServer.listen(port, () => {
            logger.info('Admin panel listening for HTTP on *'+port)
        })
    }

    private stopWebserver(){
        if(this.httpServer == null || this.express == null){
            logger.warn("Webserver is not running")
            return
        }
        this.httpServer.close()
        this.httpServer = null
        this.express = null
        logger.info("Webserver stopped")
    }

    async makeKnex():Promise<Knex>{
        const conf:Knex.Config = this.config.getConfigKey("dbConf")

        logger.debug("Making new knex:", conf)        
        if(conf.client === 'sqlite3'){
            mkdirSync(Path.dirname((<any>conf.connection).filename), {recursive: true})
        }

        this.knex = Knex(conf)
    
        await Promise.all(this.getTableDefinitions().map(async (def)=>{
            const hasTable = await this.knex.schema.hasTable(def.name)
            if(!hasTable){
                await this.knex.schema.createTable(def.name, def.tableBuilder)
            }
        }))
        
        return this.knex
    }

    getTableDefinitions(): TableDefiniton[]{
        return [
            this.eventBus,
            ...this.pluginLoader.getPlugins()
        ].flatMap(exporter => exporter.getTableDefinitions())
    }
}

process.on( 'SIGINT', function() {
    logger.info("Shutting down from SIGINT (Ctrl-C)" );
    // some other closing procedures go here
    process.exit(0);
})