const fs = require('fs-extra');

const express = require('express');
const multer = require('multer');

const ReaderWorker = require('./core/Reader/ReaderWorker');//singleton
const log = new (require('./core/AppLogger'))().log;//singleton

const {
    ReaderController,
    WebSocketController,
    BookUpdateCheckerController,
} = require('./controllers');

const utils = require('./core/utils');

function initRoutes(app, wss, config) {
    //эксклюзив для update_checker
    if (config.mode === 'book_update_checker') {
        new BookUpdateCheckerController(wss, config);
        return;
    }

    if (config.mode !== 'reader' && config.mode !== 'omnireader' && config.mode !== 'liberama')
        throw new Error(`Unknown server mode: ${config.mode}`);

    initStatic(app, config);
        
    const reader = new ReaderController(config);
    new WebSocketController(wss, config);

    //multer
    const storage = multer.diskStorage({
        destination: (req, file, cb) => {
            cb(null, config.uploadPublicDir);
        },
        filename: (req, file, cb) => {
            cb(null, utils.randomHexString(30));
        }
    });
    const upload = multer({ storage, limits: {fileSize: config.maxUploadFileSize} });

    //routes
    const routes = [
        ['POST', '/api/reader/upload-file', [upload.single('file'), reader.uploadFile.bind(reader)]],
    ];

    //to app
    for (let route of routes) {
        let callbacks = [];
        let [httpMethod, actionPath, controllers] = route;
        let controller = controllers;
        if (Array.isArray(controllers)) {
            controller = controllers[controllers.length - 1];
            callbacks = controllers.slice(0, -1);
        }

        const callback = async function(req, res) {
            try {
                const result = await controller(req, res);

                if (result !== false)
                    res.send(result);
            } catch (e) {
                res.status(500).send({error: e.message});
            }
        };
        callbacks.push(callback);

        switch (httpMethod) {
            case 'GET' :
                app.get(actionPath, ...callbacks);
                break;
            case 'POST':
                app.post(actionPath, ...callbacks);
                break;
            default: 
                throw new Error(`initRoutes error: unknown httpMethod: ${httpMethod}`);
        }
    }
}

function initStatic(app, config) {
    const readerWorker = new ReaderWorker(config);

    //восстановление файлов в /tmp и /upload из webdav-storage, при необходимости
    app.use('/tmp',
        async(req, res, next) => {
            if (req.method !== 'GET' && req.method !== 'HEAD') {
                return next();
            }

            const filePath = `${config.tempPublicDir}${req.path}`;

            //восстановим
            try {
                if (!await fs.pathExists(filePath))
                    await readerWorker.restoreRemoteFile(req.path, '/tmp');
            } catch(e) {
                log(LM_ERR, `static::restoreRemoteFile ${filePath} > ${e.message}`);
            }

            return next();
        },
        express.static(config.tempPublicDir, {
            setHeaders: (res) => {
                res.set('Content-Type', 'application/xml');
                res.set('Content-Encoding', 'gzip');
            },
        })
    );

    app.use('/upload',
        async(req, res, next) => {
            if (req.method !== 'GET' && req.method !== 'HEAD') {
                return next();
            }

            const filePath = `${config.uploadPublicDir}${req.path}`;

            //восстановим
            try {
                if (!await fs.pathExists(filePath))
                    await readerWorker.restoreRemoteFile(req.path, '/upload');
            } catch(e) {
                log(LM_ERR, `static::restoreRemoteFile ${filePath} > ${e.message}`);
            }

            return next();
        },
        express.static(config.uploadPublicDir)
    );

    app.use(express.static(config.publicDir));
}

module.exports = {
    initRoutes
}
