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

 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/>.

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

import fs from 'fs'

import logger from '../../../utils/logger'
import utils from '../../../utils/utils'
import { Request, SubmoduleData, User } from '../../../types/basicTypes'

const dataFileName = '.data.json'

function listDir(publicDir: string, subdir: string, userFilesDir: string) {
    const safeSubdir = subdir.replace(/\.+/g, '').replace(/\/+/g, '')
    const dir = userFilesDir + '/' + safeSubdir
    const usersFile = dir + '/' + dataFileName

    if (!utils.FileExists(dir)) {
        return {
            success: false,
            msg: `Directory ${subdir} does not exists`,
        }
    }
    if (!utils.FileExists(usersFile)) {
        utils.WriteFile('{}', usersFile)
    }
    const users = utils.ReadJSON(usersFile)

    if (!utils.FileExists(dir)) {
        return {
            success: false,
            msg: `Path '${safeSubdir}' does not exists`,
        }
    }

    return {
        success: true,
        files: utils.ReadDir(dir).reduce((acc, file) => {
            const stat = fs.lstatSync(dir + '/' + file)

            if (stat.isDirectory()) {
                return acc
            }

            acc.push({
                name: file,
                path: dir.replace(publicDir, '') + '/' + file,
                size: stat.size,
                date: stat.mtime.getTime(),
                user: users && users[file] ? users[file].uid : -1,
                views:
                    users && users[file] && users[file].views
                        ? users[file].views
                        : 0,
                upvotes:
                    users && users[file] && users[file].upvotes
                        ? users[file].upvotes
                        : [],
                downvotes:
                    users && users[file] && users[file].downvotes
                        ? users[file].downvotes
                        : [],
            })
            return acc
        }, []),
    }
}

function setup(data: SubmoduleData): void {
    const { app, /* userDB, url, */ publicdirs /* moduleSpecificData */ } = data

    app.use((req: Request, _res, next) => {
        // /userFiles/test/2021-04-28_10-59.png
        try {
            if (req.url.includes('/userFiles/')) {
                logger.LogReq(req)
                const safePath = decodeURIComponent(req.url)
                    .split('?')[0]
                    .replace(/\.+/g, '.')
                    .replace(/\/+/g, '/')
                const x = safePath.split('/')
                const dir = x[2]
                const fname = x.pop()
                const dataFilePath =
                    userFilesDir + '/' + dir + '/' + dataFileName

                const data = utils.ReadJSON(dataFilePath)

                if (data[fname]) {
                    if (!data[fname].views) {
                        data[fname].views = 0
                    }
                    data[fname].views = data[fname].views + 1

                    utils.WriteFile(JSON.stringify(data), dataFilePath)
                }
            }
        } catch (e) {
            console.error(e)
            logger.Log(
                `Error trying to update view count on ${req.url}`,
                logger.GetColor('redbg')
            )
        }
        next()
    })

    const publicDir = publicdirs[0]

    const userFilesDir = publicDir + 'userFiles'
    if (!utils.FileExists(userFilesDir)) {
        utils.CreatePath(userFilesDir, true)
    }

    app.get('/listUserDir', (req: Request, res) => {
        logger.LogReq(req)

        if (!utils.FileExists(userFilesDir)) {
            utils.CreatePath(userFilesDir, true)
        }

        const subdir: string = req.query.subdir

        if (subdir) {
            const result = listDir(publicDir, subdir, userFilesDir)
            res.json(result)
        } else {
            res.json({
                success: true,
                dirs: utils.ReadDir(userFilesDir).reduce((acc, file) => {
                    const stat = fs.lstatSync(userFilesDir + '/' + file)

                    if (!stat.isDirectory()) {
                        return acc
                    }

                    acc.push({
                        name: file,
                        date: stat.mtime.getTime(),
                        size: utils.ReadDir(userFilesDir + '/' + file).length,
                    })
                    return acc
                }, []),
            })
        }
    })

    app.post(
        '/deleteUserFile',
        (req: Request<{ dir: string; fname: string }>, res) => {
            logger.LogReq(req)
            const dir: string = req.body.dir
            const fname: string = req.body.fname
            if (!dir || !fname) {
                res.json({
                    success: false,
                    msg: `'dir' or 'fname' is undefined!`,
                })
                return
            }
            const safeDir = dir.replace(/\.+/g, '').replace(/\/+/g, '')
            const safeFname = fname.replace(/\.+/g, '.').replace(/\/+/g, '')
            const filePath = userFilesDir + '/' + safeDir + '/' + safeFname

            if (!utils.FileExists(filePath)) {
                res.json({
                    success: false,
                    msg: `path does not exists!`,
                })
                return
            }
            utils.deleteFile(filePath)
            const usersFile = userFilesDir + '/' + safeDir + '/' + dataFileName
            const users = utils.ReadJSON(usersFile)
            delete users[safeFname]
            utils.WriteFile(JSON.stringify(users), usersFile)

            res.json({
                success: true,
            })
        }
    )

    app.post('/newUserDir', (req: Request<{ name: string }>, res) => {
        logger.LogReq(req)

        const name: string = req.body.name
        if (!name) {
            res.json({
                success: false,
                msg: `name is undefined!`,
            })
            return
        }
        const safeName = name.replace(/\.+/g, '').replace(/\/+/g, '')

        if (utils.FileExists(userFilesDir + '/' + safeName)) {
            res.json({
                success: false,
                msg: `Dir ${name} already exists`,
            })
            return
        }
        utils.CreatePath(userFilesDir + '/' + safeName, true)

        res.json({
            success: true,
        })
    })

    app.post('/uploadUserFile', (req: Request<{ dir: string }>, res) => {
        logger.LogReq(req)

        const user: User = req.session.user
        const dir = req.body.dir
        if (!dir) {
            res.json({
                success: false,
                msg: `dir '${dir}' is undefined!`,
            })
            return
        }
        const safeDir = dir.replace(/\.+/g, '.').replace(/\/+/g, '/')
        if (!utils.FileExists(userFilesDir + '/' + safeDir)) {
            res.json({
                success: false,
                msg: `dir '${dir}' does not exists!`,
            })
            return
        }

        utils
            .uploadFile(req, userFilesDir + '/' + safeDir)
            .then((body) => {
                logger.Log(
                    `Successfull upload ${body.filePath}`,
                    logger.GetColor('blue')
                )

                const usersFile =
                    userFilesDir + '/' + safeDir + '/' + dataFileName
                const users = utils.ReadJSON(usersFile)
                users[body.fileName] = { uid: user.id }
                utils.WriteFile(JSON.stringify(users), usersFile)

                res.json({
                    success: true,
                })
            })
            .catch(() => {
                res.json({ success: false, msg: 'something bad happened :s' })
            })
    })

    app.post('/voteFile', (req: Request<{ path: string; to: string }>, res) => {
        logger.LogReq(req)
        const user: User = req.session.user
        // { path: 'userFiles/test/2021-04-28_10-59.png', to: 'up' } 19
        const { path, to } = req.body
        const safePath = path.replace(/\.+/g, '.').replace(/\/+/g, '/')
        const x = safePath.split('/')
        const dir = x[1]
        const fname = x.pop()
        const dataFilePath = userFilesDir + '/' + dir + '/' + dataFileName

        const data = utils.ReadJSON(dataFilePath)

        if (data[fname]) {
            if (!data[fname].upvotes) {
                data[fname].upvotes = []
            }
            if (!data[fname].downvotes) {
                data[fname].downvotes = []
            }

            const removeVote = (from: number[], uid: number) => {
                if (!from.includes(uid)) {
                    return from
                }
                return from.reduce((acc, id) => {
                    if (id !== uid) {
                        acc = [...acc, id]
                    }
                    return acc
                }, [])
            }

            data[fname].downvotes = removeVote(data[fname].downvotes, user.id)
            data[fname].upvotes = removeVote(data[fname].upvotes, user.id)

            if (to === 'up') {
                data[fname].upvotes = [...data[fname].upvotes, user.id]
            } else if (to === 'down') {
                data[fname].downvotes = [...data[fname].downvotes, user.id]
            } else if (to === 'clear') {
                // ... already cleared
            }

            utils.WriteFile(JSON.stringify(data), dataFilePath)
        }

        const result = listDir(publicDir, dir, userFilesDir)
        res.json(result)
    })

    app.post('/deleteDir', (req: Request<{ name: string }>, res) => {
        logger.LogReq(req)
        const { name } = req.body

        const safeName = name.replace(/\.+/g, '').replace(/\/+/g, '')

        if (!utils.FileExists(userFilesDir + '/' + safeName)) {
            res.json({
                success: false,
                msg: `Dir ${name} does not exist!`,
            })
            return
        }
        utils.CreatePath(userFilesDir + '/' + safeName, true)
        const result = listDir(publicDir, name, userFilesDir)
        if (result.files.length === 0) {
            utils.deleteDir(userFilesDir + '/' + safeName)
        } else {
            res.json({ succes: false, msg: `Dir ${name} is not empty!` })
            return
        }

        res.json({ succes: true })
    })
}

export default {
    setup: setup,
}
