/* ----------------------------------------------------------------------------

 Question Server
 GitLab: <https://gitlab.com/MrFry/mrfrys-node-server>

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program. If not, see <https://www.gnu.org/licenses/>.

 ------------------------------------------------------------------------- */

// package requires
import express, { RequestHandler } from 'express'
import fileUpload from 'express-fileupload'
import type { Database } from 'better-sqlite3'
import http from 'http'
import https from 'https'

// other requires
import logger from '../../utils/logger'
import utils from '../../utils/utils'
import auth from '../../middlewares/auth.middleware'
import { SetupData } from '../../server'
import {
    DataFile,
    ModuleSpecificData,
    ModuleType,
    QuestionDb,
    Request,
    Submodule,
} from '../../types/basicTypes'
import { loadJSON } from '../../utils/actions'
import { initWorkerPool } from '../../utils/workerPool'
import { paths } from '../../utils/files'

// other paths
const moduleName = 'API'

// stuff gotten from server.js
let userDB: Database
let url: string
let publicdirs: string[] = []
let httpServer: http.Server
let httpsServer: https.Server

function GetApp(): ModuleType {
    const app = express()

    const publicDir = publicdirs[0]
    if (!publicDir) {
        throw new Error(`No public dir! ( API )`)
    }

    let domain: any = url.split('.') // [ "https://api", "frylabs", "net" ]
    domain.shift() // [ "frylabs", "net" ]
    domain = domain.join('.') // "frylabs.net"
    logger.DebugLog(`Cookie domain: ${domain}`, 'cookie', 1)

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

    app.use(
        express.urlencoded({
            limit: '10mb',
            extended: true,
        }) as RequestHandler
    )
    app.use(
        express.json({
            limit: '10mb',
        }) as RequestHandler
    )
    app.set('view engine', 'ejs')
    app.set('views', ['./src/modules/api/views', './src/sharedViews'])
    app.use(
        auth({
            userDB: userDB,
            exceptions: [
                '/favicon.ico',
                '/api/register',
                '/api/login',
                '/api/postfeedback',
                '/api/fosuploader',
                '/api/badtestsender',
                '/api/validationtoken',
            ],
        })
    )
    app.use(
        fileUpload({
            limits: { fileSize: 50 * 1024 * 1024 },
        })
    )
    // -------------------------------------------------------------------------------------------

    let rootRedirectURL = ''

    function reloadRootRedirectURL() {
        if (utils.FileExists(paths.rootRedirectToFile)) {
            rootRedirectURL = utils.ReadFile(paths.rootRedirectToFile).trim()
        }
    }

    const filesToWatch = [
        {
            fname: paths.rootRedirectToFile,
            logMsg: 'Root redirect URL changed',
            action: reloadRootRedirectURL,
        },
    ]

    function Load() {
        filesToWatch.forEach((ftw) => {
            if (utils.FileExists(ftw.fname)) {
                utils.WatchFile(ftw.fname, () => {
                    logger.Log(ftw.logMsg)
                    ftw.action()
                })
                ftw.action()
            } else {
                logger.Log(
                    `File ${ftw.fname} does not exists to watch!`,
                    logger.GetColor('redbg')
                )
            }
        })
    }

    Load()

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

    app.get('/', function (req: Request, res: any) {
        logger.LogReq(req)
        if (rootRedirectURL) {
            res.redirect(rootRedirectURL)
        } else {
            res.json({ msg: 'hi c:' })
        }
    })

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

    const dbsFile = publicDir + 'questionDbs.json'

    // FIXME: is dataFiles only a temp variable? does this cause any problems?
    if (!utils.FileExists(dbsFile)) {
        utils.WriteFile('[]', dbsFile)
    }
    const dataFiles: Array<DataFile> = utils.ReadJSON(dbsFile)
    let questionDbs: Array<QuestionDb> = loadJSON(dataFiles, publicDir)
    initWorkerPool(() => questionDbs)

    const submoduleDatas = setupSubModules(app, {
        getQuestionDbs: () => {
            return questionDbs
        },
        setQuestionDbs: (newQdbs: QuestionDb[]) => {
            questionDbs = newQdbs
        },
        dbsFile: dbsFile,
    })

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

    publicdirs.forEach((pdir) => {
        app.use(express.static(pdir))
    })

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

    function DailyAction() {
        submoduleDatas.forEach((data) => {
            if (data.dailyAction) {
                data.dailyAction()
            }
        })
    }

    submoduleDatas.forEach((data) => {
        if (data.load) {
            data.load()
        }
    })

    return {
        dailyAction: DailyAction,
        app: app,
    }
}

function setupSubModules(
    parentApp: express.Application,
    moduleSpecificData: ModuleSpecificData
): Submodule[] {
    const submoduleDir = './submodules/'
    const absolutePath = __dirname + '/' + submoduleDir
    if (!utils.FileExists(absolutePath)) {
        return null
    }
    const files = utils.ReadDir(absolutePath)
    const moduleDatas: Submodule[] = []
    files.forEach((file) => {
        if (!file.endsWith('.js')) {
            return
        }
        const submodulePath = submoduleDir + file

        try {
            const mod = require(submodulePath).default // eslint-disable-line
            const loadedModData = mod.setup({
                app: parentApp,
                userDB: userDB,
                url: url,
                publicdirs: publicdirs,
                moduleSpecificData: moduleSpecificData,
                httpServer: httpServer,
                httpsServer: httpsServer,
            })
            moduleDatas.push(loadedModData || {})
        } catch (e) {
            logger.Log(`Error loading submodule from ${submodulePath}`)
            console.error(e)
        }
    })

    return moduleDatas
}

export default {
    name: moduleName,
    getApp: GetApp,
    setup: (data: SetupData): void => {
        userDB = data.userDB
        url = data.url
        publicdirs = data.publicdirs
        httpServer = data.httpServer
        httpsServer = data.httpsServer
    },
}
