import * as express from "express";
import * as bodyparser from "body-parser";

import { authRoute } from "../security/token";

import { userFromSession } from "../utils/user";

import { IUser, IUserDBModel } from "../models/user";
import { LRequest } from "../types/express";
import { Db } from "mongodb";

const userRouter = express.Router();
userRouter.use(authRoute);
userRouter.use(bodyparser.json());

// Return the user object if the user is still authenticated
userRouter.get("/me", async (req: LRequest, res) => {
    const { db, token } = req;

    const session = await db.collection("sessions").findOne({ token, });
    if (session !== null) {
        const user = await db.collection("users").findOne({ username: session.username });

        // Copy and strip unneeded attributes
        let copy = Object.assign({}, user, {
            sessionToken: token,
        });
        delete copy._id;
        delete copy.hash;
        delete copy.salt;

        res.send({
            error: "0",
            data: copy,
        });
    } else {
        res.send({
            error: "404",
            data: {},
        });
    }
});

// Removes the user's session
userRouter.get("/logout", async (req: LRequest, res) => {
    // Try to remove the session
    const { db, token } = req;
    await db.collection("sessions").findOneAndDelete({ token, });

    res.send({
        error: "0",
        data: {},
    });
});

// TODO: This should be shared with the frontend, to remove code duplication
export enum VocabType {
    NOMEN = 0,
    VERB = 1,
    ADJEKTIV = 2,
    ADVERB = 3,
};

// Gets the user's review queue
userRouter.get("/queue", async (req: LRequest, res) => {
    // TODO: if (user)
    const { token, db } = req;
    const user = await userFromSession(token, db);

    // Fetch all vocab ids from the vocabulary collection
    const vocabRaw = await db.collection("vocabulary").find({
        id: { $in: user.queue },
    }, {
            // TODO: Make this configurable?
            limit: 20,
        }).toArray();

    // Remove unneeded data
    const vocab = vocabRaw.map(el => {
        let tmp = Object.assign({}, el);
        delete tmp._id;

        return tmp;
    });

    res.send({
        error: "0",
        data: {
            queue: vocab,
        },
    });
});

// Get ot set the last review results
userRouter.get("/lastReview", async (req: LRequest, res) => {
    // TODO: if(user)
    const { token, db } = req;
    const user = await userFromSession(token, db);

    res.send({
        error: "0",
        data: user.lastReview,
    });
});
userRouter.post("/lastReview", async (req: LRequest, res) => {
    // TODO: Check if we get the correct data
    // TODO: if(user)
    const { token, db } = req;
    const user = await userFromSession(token, db);

    // TODO: Error handling
    await db.collection("users").updateOne({
        username: user.username,
    }, {
            $set: {
                lastReview: req.body.meta,
            },
        });

    res.send({
        error: "0",
        data: {},
    });
});

// Returns the next level (level + 1) or the current level, if no higher level
// can be found
async function getNextLevel(token: string, db: Db): Promise<any> {
    // TODO: if(user)
    const user = await userFromSession(token, db);
    const { lastLevel } = user;

    // Try to find a level, which level is lastLevel + 1
    const level = await db.collection("levels").findOne({
        level: lastLevel + 1,
    });

    if (level) {
        return level;
    } else {
        // TODO: Send different data, so that the Client can say "Hey, no more levels"
        return await db.collection("levels").findOne({
            level: lastLevel
        });
    }
}

// Get the next level
userRouter.get("/nextLevel", async (req: LRequest, res) => {
    const level = await getNextLevel(req.token, req.db);
    res.send({
        error: "0",
        data: level,
    });
});

// Mark a level as done
userRouter.post("/level/:id", async (req, res) => {
    console.log("STUB(post): /user/level/:id");

    // TODO: Stub
    res.send({
        error: "0",
        data: {},
    });
});

// Get the data needed for the dashboard 
userRouter.get("/dashboard", async (req: LRequest, res) => {
    const { db, token } = req;

    // Get the user
    // TODO: if (user)
    const user = await userFromSession(token, db);
    const { classId } = user;

    // Fetch the top ten of the class
    const rawTopTen = await db.collection("users").find({
        classId,
    }, {
            sort: {
                score: -1,
            },
            limit: 10,
        }).toArray();

    let nr = 1;
    const topTen = rawTopTen.map((user: IUser) => {
        return {
            username: user.username,
            score: user.score,
            // TODO: Calculate on the client?
            level: 1,
            nr: nr++,
        };
    });

    const nextLevel = await getNextLevel(token, db);
    res.send({
        error: "200",
        data: {
            nextLevel,
            topTen,
            lastReview: user.lastReview,
        },
    });
});

export default userRouter;