const express = require('express')
const router = express.Router();

const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const passport = require('passport');
const secret = require('../services/keys').secret;
const rateLimitMiddleware = require("../services/rate-limiter");
const crypto = require("crypto");

const { isAdmin } = require('../services/middleware');

const User = require("../models/User");

const upload = require("../services/storage");


router.post('/register', async (req, res) => {
    try {
        const existsAdmin = !!(await User.findOne({ admin: true }));
        const {
            username,
            email,
            name,
            password
        } = req.body;

        // Check if email or username already exists
        const existingUser = await User.findOne({
            $or: [
                { email: email },
                { username: username }
            ]
        });

        if (existingUser) {
            return res.json({
                status: "error",
                msg: "register.errors.email-username-exists"
            });
        }

        const salt = await bcrypt.genSalt(10);
        let user = new User({
            admin: !existsAdmin,
            name: name,
            username: username,
            email: email,
            password: await bcrypt.hash(password, salt)
        });

        await user.save();
        res.json({ status: "ok", code: setupCode });
    } catch (err) {
        res.json({ status: "error", msg: "errors.internal" });
    }
});

// User gets if setup account exists given the query code
router.get('/verify-setup', async (req, res) => {
    try {
        const user = await User.findOne({ setupCode: req.query.code });
        if (user) {
            res.json({ status: "ok", code: req.query.code });
        } else {
            res.json({ status: "error", msg: "not-exists" });
        }
    } catch (err) {
        res.json({ status: "error", msg: "internal" });
    }
});

// User posts the parameters of his new account given by admin
router.post('/setup', rateLimitMiddleware, async (req, res) => {
    const { name, username, email, password } = req.body;
    const setupCode = req.query.code;

    if (!(name && username && email && password && setupCode)) {
        return res.json({ status: "error", msg: "params" });
    }

    try {
        const user = await User.findOne({ setupCode });
        if (!user) {
            return res.json({ status: "error", msg: "not-found" });
        }

        const sameUser = await User.findOne({ email });
        if (sameUser) {
            return res.json({ status: "error", msg: "already-email" });
        }

        const salt = await bcrypt.genSalt(10);
        user.password = await bcrypt.hash(password, salt);
        user.username = username;
        user.email = email;
        user.setupCode = undefined;

        await user.save();
        res.json({ status: "ok" });
    } catch (err) {
        res.json({ status: "error", msg: "internal" });
    }
});

// Login post
router.post('/login', rateLimitMiddleware, async (req, res) => {
    const { usermail, password } = req.body;

    if (!(usermail && password)) {
        return res.json({ status: "error", msg: "login.errors.params" });
    }

    try {
        const user = await User.findOne({ $or: [{ username: usermail }, { email: usermail }] });
        if (!user) {
            return res.json({ status: "error", msg: "login.errors.invalid-credentials" });
        }

        const isMatch = await bcrypt.compare(password, user.password);
        if (!isMatch) {
            return res.json({ status: "error", msg: "login.errors.invalid-credentials" });
        }

        const payload = {
            _id: user._id,
            username: user.username,
            name: user.name,
            email: user.email,
            admin: user.admin,
            settings: user.settings
        };

        const token = await new Promise((resolve, reject) => {
            jwt.sign(payload, secret, { expiresIn: 172800 }, (err, token) => {
                if (err) reject(err);
                else resolve(token);
            });
        });

        res.json({ status: "ok", token, msg: "success" });
    } catch (err) {
        res.json({ status: "error", msg: "errors.internal" });
    }
});

// Upload avatar post
router.post("/upload-avatar", upload.single("image"), passport.authenticate('jwt', {session: false}), async (req, res) => {
    try {
        const imageName = req.file.filename;
        await User.updateOne(req.user, { image: imageName });
        res.json({ status: "ok", msg: "uploaded" });
    } catch (err) {
        res.json({ status: "error", msg: "internal" });
    }
});

router.get("/retrieve-avatar", async (req, res) => {
    try {
        const data = await User.findOne({ username: req.query.username });
        res.json({ status: "ok", image: `${data.image}` });
    } catch (err) {
        res.json({ status: "error" });
    }
});

router.get("/has-admin", async (req, res) => {
    try {
        const data = await User.findOne({ admin: true });
        if (data) res.json({ status: "ok" });
        else res.json({ status: "init" });
    } catch (err) {
        res.json({ status: "error" });
    }
});

router.post("/update-settings", passport.authenticate('jwt', {session: false}), async (req, res) => {
    try {
        await User.updateOne(req.user, { settings: req.body.settings });
        res.json({ status: "ok", settings: req.body.settings });
    } catch (err) {
        res.json({ status: "error", msg: "internal" });
    }
});

router.get('/get-settings', passport.authenticate('jwt', {session: false}), async (req, res) => {
    try {
        const data = await User.findOne(req.user);
        res.json({ status: "ok", settings: data.settings });
    } catch (err) {
        res.json({ status: "error", msg: "internal" });
    }
});

module.exports = router;