import { env, exit } from "process";
// import * as fs from "fs";
import { randomBytes, pbkdf2Sync } from "crypto";
import * as assert from "assert";

import * as express from "express";
import * as cors from "cors";

import * as bodyparser from "body-parser";

//@ts-ignore
//import * as Filter from "bad-words";

import { isAuthenticated, performLogin } from "./security/auth";

import { LRequest } from "./types/express";

import UserRouter from "./api/user";
import ClassRouter from "./api/class";
import LevelRouter from "./api/level";

import { ITrackerDBModel } from "./models/tracker";

const baseRouter = express.Router();
const authRouter = express.Router();

import { MongoClient } from "mongodb";

const user = encodeURIComponent("backend");
const password = encodeURIComponent(env["LATEINICUS_USER_PW"]);

(async function() {
    // Load the profanity list
    // const list = JSON.parse(fs.readFileSync("/etc/profanity", { encoding: "utf-8" }));
    // const profanityFilter = new Filter({
    //     list,
    // });

    // Database Name
    const dbName = 'lateinicus';
    // Connection URL
    const url = `mongodb://${user}:${password}@128.1.0.2:27017/?authMechanism=SCRAM-SHA-1&authSource=${dbName}`;
    let client: MongoClient;

    try {
        // Use connect method to connect to the Server
        client = await MongoClient.connect(url);
        console.log("Connected to MongoDB");
    } catch (err) {
        console.log(err.stack);
        assert(false);
    }

    const db = client.db(dbName);
    console.log("Connected to the database");


    const app = express();
    app.use(bodyparser.json());
    app.options("*", cors());

    app.use((req: LRequest, res, next) => {
        // Every route should have access to the database so that
        // we can easily make calls to it
        req.db = db;
        next();
    });
    app.use("/api/level", LevelRouter);
    app.use("/api/class", ClassRouter);
    app.use("/api/user", UserRouter);
    app.post("/api/tracker", async (req, res) => {
        // Did we get any data
        if (!req.body) {
            res.send({
                error: "403",
                data: {
                    msg: "No request body provided",
                },
            });
            return;
        }

        // Did we get all arguments?
        if (!("session" in req.body) || !("event" in req.body)) {
            res.send({
                error: "403",
                data: {
                    msg: "Invalid request",
                },
            });
            return;
        }

        // Insert it into the database
        const tracker: ITrackerDBModel = Object.assign({}, req.body, {
            timestamp: Date.now(),
        });
        await db.collection("tracker").insertOne(tracker);

        res.send({
            error: "200",
            data: {},
        });
    });
    app.get("/api/levels", async (req, res) => {
        // TODO: if (levels)
        const levels = (await db.collection("levels").find({}, {
            // The order in which we send the levels is important, so better
            // sort them
            sort: {
                level: 1,
            },
        })
            .toArray())
            .map((el) => {
                let tmp = Object.assign({}, el);
                delete tmp.vocab;
                delete tmp._id;

                return tmp;
            });

        res.send({
            error: "0",
            data: {
                levels,
            },
        });
    });
    app.post("/api/register", async (req, res) => {
        // Check if any data was sent
        if (!req.body) {
            res.send({
                error: "403",
                data: {
                    msg: `No data sent`,
                },
            });
            return;
        }

        // Check if we have all we need
        const params = ["username", "password", "classId"];
        for (let param of params) {
            if (!(param in req.body)) {
                res.send({
                    error: "403",
                    data: {
                        msg: `${param} not specified!`,
                    },
                });
                return;
            }
        }

        const { username, password, classId } = req.body;

        // Check if the registration is open for the class Id
        // NOTE: This to prevent people from spamming the database
        const classes = env["LATEINICUS_CLASSES"].split(",");
        if (classes.indexOf(classId) === -1) {
            res.send({
                error: "403",
                data: {
                    msg: "Class does not exist",
                },
            });
            return;
        }

        // TODO: Check if the username is profane
        // if (profanityFilter.isProfane(username)) {
        //     res.send({
        //         error: "451",
        //         data: {
        //             msg: "Profane username",
        //         },
        //     });
        //     return;
        // }

        // Check if the user already exists
        const checkUser = await db.collection("users").findOne({
            username,
        });
        if (checkUser) {
            res.send({
                error: "403",
                data: {
                    msg: "User already exists",
                },
            });
            return;
        }

        const salt = randomBytes(30).toString("hex");
        const hash = pbkdf2Sync(password, salt, 50000, 512, "sha512").toString("hex");
        const user = {
            username,
            salt,
            hash,
            classId,
            score: 0,
            showWelcome: true,

            lastReview: {
                correct: 0,
                wrong: 0,
            },

            lastLevel: 0,
            levels: [] as number[],
            vocabMetadata: {},
        };
        await db.collection("users").insertOne(user);

        res.send({
            error: "200",
            data: {},
        });
    });
    app.get("/api/health", (req, res) => {
        res.send({
            error: "0",
            data: {
                msg: "lol",
            },
        });
    });
    app.post("/api/login", async (req, res) => {
        // Check if all arguments were sent
        const { body } = req;
        if (!body || !("username" in body) || !("password" in body)) {
            res.send({
                error: "400",
                data: {
                    msg: "Username or password not specified",
                },
            });

            return;
        }

        // Try to log the user in
        try {
            const userData = await performLogin(body.username, body.password, db);
            res.send({
                error: "0",
                data: userData,
            });

        } catch (err) {
            console.log("Could not resolve login promise!", err);

            // If anything was wrong, just tell the client
            res.send({
                error: "1",
                data: {
                    msg: "Username or password is wrong",
                },
            });
        }
    });
    const server = app.listen(8080, () => {
        console.log("Starting on port 8080");
    });
})();
