import { Express }                    from 'express-serve-static-core';
import cors                           from 'cors';
import morganMiddleware               from '../logging/MorganMiddleware.js';
import { AddressInfo }                from 'net';
import http                           from 'http';
import helmet                         from 'helmet';
import express                        from 'express';
import WorkerTask                     from '../process/WorkerTask.js';
import multer                         from 'multer';
import SessionMiddleware              from '../middlewares/SessionMiddleware.js';
import Config                         from '../config/Config.js';
import logger                         from '../shared/logging/WinstonLogger.js';
import ParamsCallbackManager          from '../middlewares/ParamsCallbackManager.js';
import ApiRoutesManager               from '../routes/ApiRoutesManager.js';
import compression                    from 'compression';
import ClientVersionCheckerMiddleware from '../middlewares/ClientVersionCheckerMiddleware.js';
import swaggerUi                      from 'swagger-ui-express';
import path                           from 'path';
import DojoCliVersionHelper           from '../helpers/DojoCliVersionHelper.js';


class API implements WorkerTask {
    private readonly backend: Express;
    private server!: http.Server;

    constructor() {
        this.backend = express();

        this.initOpenAPI();
        this.initBaseMiddlewares();

        this.backend.use(ClientVersionCheckerMiddleware.register());

        ParamsCallbackManager.registerOnBackend(this.backend);
        SessionMiddleware.registerOnBackend(this.backend);
        ApiRoutesManager.registerOnBackend(this.backend);
    }

    private initBaseMiddlewares() {
        this.backend.use(multer({
                                    limits: { fieldSize: 100 * 1024 * 1024 }
                                }).none()); //Used for extract params from body with format "form-data", The none is for say that we do not wait a file in params
        this.backend.use(morganMiddleware); //Log API accesses
        this.backend.use(helmet()); //Help to secure express, https://helmetjs.github.io/
        this.backend.use(cors()); //Allow CORS requests
        this.backend.use(compression()); //Compress responses

        this.backend.use((_req, res, next) => {
            DojoCliVersionHelper.getLatestVersion().then(latestVersion => {
                res.header('dojocli-latest-version', latestVersion);
                next();
            });
        });
    }

    private initOpenAPI() {
        const options = {
            customSiteTitle: 'Dojo API',
            explorer       : false,
            swaggerOptions : {
                url: '../OpenAPI.yaml'
            }
        };
        this.backend.get('/docs/OpenAPI.yaml', (_req, res) => res.sendFile(path.resolve(__dirname + '/../../assets/OpenAPI/OpenAPI.yaml')));
        this.backend.use('/docs/swagger', swaggerUi.serveFiles(undefined, options), swaggerUi.setup(undefined, options));
        this.backend.get('/docs/redoc.html', (_req, res) => res.sendFile(path.resolve(__dirname + '/../../assets/OpenAPI/redoc.html')));

        this.backend.get('/docs/', (req, res) => {
            const prefix = req.url.slice(-1) === '/' ? '' : 'docs/';
            res.send(`
                <!DOCTYPE html>
                <html lang="en">
                <body>
                    <ul>
                        <li><a href="${ prefix }OpenAPI.yaml">OpenAPI</a></li>
                        <li>GUI
                            <ul>
                                <li><a href="${ prefix }swagger/">Swagger</a></li>
                                <li><a href="${ prefix }redoc.html">Redoc</a></li>
                            </ul>
                        </li>
                    </ul>
                </body>
                </html>
            `);
        });
    }

    run() {
        this.server = this.backend.listen(Config.api.port, '0.0.0.0', () => {
            const {
                      port,
                      address
                  } = this.server.address() as AddressInfo;
            logger.info(`Server started on http://${ address }:${ port }`);
        });
    }
}


export default API;